Skip to content

Commit

Permalink
Add more IDB demo code
Browse files Browse the repository at this point in the history
  • Loading branch information
captainbrosset committed Nov 4, 2024
1 parent a9342fe commit 0afd76e
Showing 1 changed file with 102 additions and 52 deletions.
154 changes: 102 additions & 52 deletions idb-getallrecords/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@

<h1>IndexedDB: getAllRecords()</h1>

<button id="read" disabled>Read in batches</button>
<button id="read-reverse" disabled>Read in reverse order</button>

<script>
const readButton = document.querySelector("#read");
const readReverseButton = document.querySelector("#read-reverse");

// Open the DB.
const openRequest = indexedDB.open("db", 3);
const openRequest = indexedDB.open("db", 4);

// The first time, the onupgradeneeded event is triggered, and we use it
// to create an object store (similar to a table in SQL databases).
Expand All @@ -17,14 +23,8 @@ <h1>IndexedDB: getAllRecords()</h1>
const db = openRequest.result

// Creating an object store.
// Providing keyPath means that objects stored here must have an "id" property.
console.log("creating user store");
const store = db.createObjectStore("features", { keyPath: "id" });

// Indexes are used to query object stores by various properties.
// Creating one to search by the "name" property.
console.log("creating name index");
const index = store.createIndex("name", "name");
const store = db.createObjectStore("features");
};

function clearStore(db) {
Expand All @@ -37,15 +37,15 @@ <h1>IndexedDB: getAllRecords()</h1>
console.error("Clear transaction error");
reject(new Error("Clear transaction error", event));
};

// The transaction gives us access to the store, but we need to name it.
// Indeed, we could have opened the transaction for multiple stores at once.
const clearTransactionStore = clearTransaction.objectStore("features");

// We can now clear the store.
console.log("Clearing the store");
const clearRequest = clearTransactionStore.clear();

clearRequest.onsuccess = event => {
console.log("Store cleared");
resolve();
Expand All @@ -68,15 +68,15 @@ <h1>IndexedDB: getAllRecords()</h1>
console.error("Add transaction error");
reject(new Error("Add transaction error", event));
};

// The transaction gives us access to the store, but we need to name it.
// Indeed, we could have opened the transaction for multiple stores at once.
const addTransactionStore = addTransaction.objectStore("features");

// We can now add data to the store.
console.log("Adding the data");
const addRequest = addTransactionStore.add({ id, name, description });
const addRequest = addTransactionStore.add({ name, description }, id);

addRequest.onsuccess = event => {
console.log("Data added");
resolve();
Expand All @@ -95,61 +95,111 @@ <h1>IndexedDB: getAllRecords()</h1>
const data = await response.json();

const featureAdditionPromises = Object.keys(data.features).map(id => {
return addOneFeatureToStore(db, id, data.features[id].name, data.features[id].description);
const { name, description } = data.features[id];
return addOneFeatureToStore(db, id, name, description);
});

return Promise.all(featureAdditionPromises);
}

// Transactions can't run before the onupgradeneeded event has finished.
// We can wait by using the onsuccess event of the request.
openRequest.onsuccess = async event => {
console.log("onsuccess");
// Reading a DB in order can be done in mulitple ways.
// With a cursor, you do it one at a time, which means that if you're reading a
// lot of records, you will suffer through a lot of back and forth between the
// main thread and the IDB engine thread.
// You could also just read the entire DB at once, but if that DB is big, you
// will have memory problems.
// Below, we use getAll+getAllKeys to read the DB in batches, limiting the number
// of back-and-forth with the IDB engine thread.
// However this doesn't support reading in reverse order, and forces to use both
// getAll and getAllKeys together. Plus, finding the right batch size might be
// difficult.
async function readInBatches(db) {
const batchSize = 10;

const db = openRequest.result
console.log("starting read transaction");
const readTransaction = db.transaction("features", "readonly");

// Start by clearing the entire store
await clearStore(db);
readTransaction.oncomplete = event => console.log("read transaction complete");
readTransaction.onerror = event => console.log("read transaction error");

// Add some data to the store.
await addAllFeatures(db);
const readTransactionStore = readTransaction.objectStore("features");

function getAllKeys(range) {
return new Promise(resolve => {
readTransactionStore.getAllKeys(range, batchSize).onsuccess = event => {
resolve(event.target.result);
};
});
}

function getAllValues(range) {
return new Promise(resolve => {
readTransactionStore.getAll(range, batchSize).onsuccess = event => {
resolve(event.target.result);
};
});
}

let range = null;

while (true) {
const [keys, values] = await Promise.all([getAllKeys(range), getAllValues(range)]);
if (keys && values && values.length === batchSize) {
// There could be more records, set a starting range for next iteration.
range = IDBKeyRange.lowerBound(keys.at(-1), true);

console.log(`Read ${batchSize} records`, keys, values);
} else {
break;
}
}
}

// Reading in reserve order requires the use of a cursor.
// This means that it has to be done one item at a time.
async function readReverse(db) {
const batchSize = 10;

// To read data from the database, we need another transaction.
console.log("starting read transaction");
const readTransaction = db.transaction("features", "readonly");

// We can also log the transaction's events.
readTransaction.oncomplete = event => console.log("read transaction complete");
readTransaction.onerror = event => console.log("read transaction error");

// Get the store from the transaction.
const readTransactionStore = readTransaction.objectStore("features");

// We can now read data from the store.
console.log("reading data - all records");
const getAllRequest = readTransactionStore.getAll();
getAllRequest.onsuccess = event => console.log("all records", getAllRequest.result);
readTransactionStore.openCursor(null, "prev").onsuccess = event => {
const cursor = event.target.result;
if (cursor) {
console.log(`Read one item backward`, cursor.key, cursor.value);
cursor.continue();
} else {
console.log("Reverse read done");
}
};
}

console.log("reading data - all keys");
const getAllKeysRequest = readTransactionStore.getAllKeys();
getAllKeysRequest.onsuccess = event => console.log("all keys", getAllKeysRequest.result);
};
// Transactions can't run before the onupgradeneeded event has finished.
// We can wait by using the onsuccess event of the request.
openRequest.onsuccess = async event => {
console.log("onsuccess");

const db = openRequest.result;

/*
EXISTING
api.IDBIndex.getAll(query, count) // both optional
api.IDBIndex.getAllKeys(query, count) // both optional
api.IDBObjectStore.getAll(query, count) // both optional
api.IDBObjectStore.getAllKeys(query, count) // both optional
PROPOSED
api.IDBIndex.getAllRecords({ count, direction })
api.IDBObjectStore.getAllRecords({ count, direction })
*/
// Start by clearing the entire store
await clearStore(db);

// Add some data to the store.
await addAllFeatures(db);

readButton.removeAttribute("disabled");
readButton.addEventListener("click", () => {
readInBatches(db);
});

readReverseButton.removeAttribute("disabled");
readReverseButton.addEventListener("click", () => {
readReverse(db);
});
};
</script>

0 comments on commit 0afd76e

Please sign in to comment.