hydrogen-web/prototypes/idb-continue-key.html

171 lines
7.1 KiB
HTML

<html>
<head><meta charset="utf-8"></head>
<body>
<script type="text/javascript">
console.log = (...params) => {
document.write(params.join(" ")+"<br>");
};
function reqAsPromise(req) {
return new Promise((resolve, reject) => {
req.onsuccess = () => resolve(req);
req.onerror = (err) => reject(err);
});
}
function txnAsPromise(txn) {
return new Promise((resolve, reject) => {
txn.addEventListener("complete", resolve);
txn.addEventListener("abort", reject);
});
}
function iterateCursor(cursor, processValue) {
// TODO: does cursor already have a value here??
return new Promise((resolve, reject) => {
cursor.onerror = (event) => {
reject(new Error("Query failed: " + event.target.errorCode));
};
// collect results
cursor.onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) {
resolve(false);
return; // end of results
}
const {done, jumpTo} = processValue(cursor.value, cursor.key);
if (done) {
resolve(true);
} else {
cursor.continue(jumpTo);
}
};
});
}
/**
* Checks if a given set of keys exist.
* Calls `callback(key, found)` for each key in `keys`, in an unspecified order.
* If the callback returns true, the search is halted and callback won't be called again.
*/
async function findKeys(target, keys, backwards, callback) {
const direction = backwards ? "prev" : "next";
const compareKeys = (a, b) => backwards ? -indexedDB.cmp(a, b) : indexedDB.cmp(a, b);
const sortedKeys = keys.slice().sort(compareKeys);
console.log(sortedKeys);
const firstKey = backwards ? sortedKeys[sortedKeys.length - 1] : sortedKeys[0];
const lastKey = backwards ? sortedKeys[0] : sortedKeys[sortedKeys.length - 1];
const cursor = target.openKeyCursor(IDBKeyRange.bound(firstKey, lastKey), direction);
let i = 0;
let consumerDone = false;
await iterateCursor(cursor, (value, key) => {
// while key is larger than next key, advance and report false
while(i < sortedKeys.length && compareKeys(sortedKeys[i], key) < 0 && !consumerDone) {
console.log("before match", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], false);
++i;
}
if (i < sortedKeys.length && compareKeys(sortedKeys[i], key) === 0 && !consumerDone) {
console.log("match", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], true);
++i;
}
const done = consumerDone || i >= sortedKeys.length;
const jumpTo = !done && sortedKeys[i];
return {done, jumpTo};
});
// report null for keys we didn't to at the end
while (!consumerDone && i < sortedKeys.length) {
console.log("afterwards", sortedKeys[i]);
consumerDone = callback(sortedKeys[i], false);
++i;
}
}
async function findFirstOrLastOccurringEventId(target, roomId, eventIds, findLast = false) {
const keys = eventIds.map(eventId => [roomId, eventId]);
const results = new Array(keys.length);
let firstFoundEventId;
// find first result that is found and has no undefined results before it
function firstFoundAndPrecedingResolved() {
let inc = findLast ? -1 : 1;
let start = findLast ? results.length - 1 : 0;
for(let i = start; i >= 0 && i < results.length; i += inc) {
if (results[i] === undefined) {
return;
} else if(results[i] === true) {
return keys[i];
}
}
}
await findKeys(target, keys, findLast, (key, found) => {
const index = keys.indexOf(key);
results[index] = found;
firstFoundEventId = firstFoundAndPrecedingResolved();
return !!firstFoundEventId;
});
return firstFoundEventId;
}
(async () => {
let db;
let isNew = false;
// open db
{
const req = window.indexedDB.open("prototype-idb-continue-key");
req.onupgradeneeded = (ev) => {
const db = ev.target.result;
db.createObjectStore("timeline", {keyPath: ["roomId", "eventId"]});
isNew = true;
};
db = (await reqAsPromise(req)).result;
}
const roomId = "!abcdef:localhost";
if (isNew) {
const txn = db.transaction(["timeline"], "readwrite");
const store = txn.objectStore("timeline");
for (var i = 1; i <= 100; ++i) {
store.add({roomId, eventId: `$${i * 10}`});
}
await txnAsPromise(txn);
}
console.log("show all in order we get them");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const cursor = store.openKeyCursor();
await iterateCursor(cursor, (value, key) => {
console.log(key);
return {done: false};
});
}
console.log("run findKeys");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const eventIds = ["$992", "$1000", "$1010", "$991", "$500", "$990"];
// const eventIds = ["$992", "$1010"];
const keys = eventIds.map(eventId => [roomId, eventId]);
await findKeys(store, keys, false, (key, found) => {
console.log(key, found);
});
}
console.log("run findFirstOrLastOccurringEventId");
{
const txn = db.transaction(["timeline"], "readonly");
const store = txn.objectStore("timeline");
const eventIds = ["$992", "$1000", "$1010", "$991", "$500", "$990", "$123"];
const firstMatch = await findFirstOrLastOccurringEventId(store, roomId, eventIds, false);
console.log("firstMatch", firstMatch);
const lastMatch = await findFirstOrLastOccurringEventId(store, roomId, eventIds, true);
console.log("lastMatch", lastMatch);
}
})();
</script>
</body>
</html>