idb sort key prototype
This commit is contained in:
parent
0cf9e84bdd
commit
25a84d41a5
2 changed files with 189 additions and 1 deletions
13
matrix.mjs
13
matrix.mjs
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
idb stores:
|
||||
all in one database per stored session:
|
||||
- session
|
||||
- session (store all sessions in localStorage object?)
|
||||
- device id
|
||||
- last sync token
|
||||
- access token
|
||||
|
@ -44,6 +44,17 @@ all in one database per stored session:
|
|||
- members_{room_id}
|
||||
historical?
|
||||
- timeline_{room_id}
|
||||
how to store timeline events in order they should be shown?
|
||||
what's the key they should be sorted by?
|
||||
|
||||
start with origin_server_ts of first event as 0 and add / subtract from there
|
||||
in case of gaps, take the max(last_ts + 1000, origin_server_ts) again to get an idea of how many
|
||||
numbers are in between, and go down/up one again for events filling the gap
|
||||
|
||||
when closing the gap, we notice there are not enough numbers between the PK
|
||||
of both sides of the gap (because more than 1 event / millisecond was sent, or server clocks were off),
|
||||
what do we do? floating point?
|
||||
|
||||
- search?
|
||||
|
||||
where to store avatars?
|
||||
|
|
177
typedarray.html
Normal file
177
typedarray.html
Normal file
|
@ -0,0 +1,177 @@
|
|||
<html>
|
||||
<body>
|
||||
<ul id="messages"></ul>
|
||||
<script type="text/javascript">
|
||||
class Key {
|
||||
constructor() {
|
||||
this._keys = new Int32Array(2);
|
||||
}
|
||||
|
||||
get gapKey() {
|
||||
return this._keys[0];
|
||||
}
|
||||
|
||||
set gapKey(value) {
|
||||
this._keys[0] = value;
|
||||
}
|
||||
|
||||
get eventKey() {
|
||||
return this._keys[1];
|
||||
}
|
||||
|
||||
set eventKey(value) {
|
||||
this._keys[1] = value;
|
||||
}
|
||||
|
||||
buffer() {
|
||||
return this._keys.buffer;
|
||||
}
|
||||
|
||||
nextKeyWithGap() {
|
||||
const k = new Key();
|
||||
k.gapKey = this.gapKey + 1;
|
||||
k.eventKey = 0;
|
||||
return k;
|
||||
}
|
||||
|
||||
nextKey() {
|
||||
const k = new Key();
|
||||
k.gapKey = this.gapKey;
|
||||
k.eventKey = this.eventKey + 1;
|
||||
return k;
|
||||
}
|
||||
|
||||
previousKey() {
|
||||
const k = new Key();
|
||||
k.gapKey = this.gapKey;
|
||||
k.eventKey = this.eventKey - 1;
|
||||
return k;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const k = new Key();
|
||||
k.gapKey = this.gapKey;
|
||||
k.eventKey = this.eventKey;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
function reqAsPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = () => resolve(req);
|
||||
req.onerror = (err) => reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchResults(cursor, isDone, resultMapper) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
cursor.onerror = (event) => {
|
||||
reject(new Error("Query failed: " + event.target.errorCode));
|
||||
};
|
||||
// collect results
|
||||
cursor.onsuccess = (event) => {
|
||||
console.log("got a result");
|
||||
const cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
resolve(results);
|
||||
return; // end of results
|
||||
}
|
||||
results.push(resultMapper(cursor));
|
||||
if (!isDone(results)) {
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(results);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class Storage {
|
||||
constructor(databaseName) {
|
||||
this._databaseName = databaseName;
|
||||
this._database = null;
|
||||
}
|
||||
|
||||
async open() {
|
||||
const req = window.indexedDB.open(this._databaseName);
|
||||
req.onupgradeneeded = async (ev) => {
|
||||
const db = ev.target.result;
|
||||
const oldVersion = ev.oldVersion;
|
||||
this._createStores(db, oldVersion);
|
||||
};
|
||||
await reqAsPromise(req);
|
||||
this._database = req.result;
|
||||
}
|
||||
|
||||
_createStores(db) {
|
||||
db.createObjectStore("timeline", {keyPath: ["roomId", "sortKey"]});
|
||||
}
|
||||
|
||||
async insert(value) {
|
||||
const tx = this._database.transaction(["timeline"], "readwrite");
|
||||
const store = tx.objectStore("timeline");
|
||||
await reqAsPromise(store.add(value));
|
||||
}
|
||||
|
||||
async selectAt(roomId, sortKey, amount) {
|
||||
const tx = this._database.transaction(["timeline"], "readonly");
|
||||
const store = tx.objectStore("timeline");
|
||||
const range = IDBKeyRange.lowerBound([roomId, sortKey.buffer()]);
|
||||
const cursor = store.openCursor(range);
|
||||
return await fetchResults(cursor,
|
||||
(results) => results.length === amount,
|
||||
(cursor) => cursor.value);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const initialSortKey = new Key();
|
||||
initialSortKey.gapKey = 1000;
|
||||
const roomId = "!abc:hs.tld";
|
||||
const storage = new Storage("mysession");
|
||||
await storage.open();
|
||||
|
||||
let records = await storage.selectAt(roomId, initialSortKey, 15);
|
||||
if (!records.length) {
|
||||
// insert first batch backwards,
|
||||
// to see we're not assuming insertion order to sort
|
||||
let sortKey = initialSortKey.clone();
|
||||
sortKey.eventKey = 10;
|
||||
for (var i = 10; i > 0; i--) {
|
||||
await storage.insert({
|
||||
roomId,
|
||||
sortKey: sortKey.buffer(),
|
||||
message: `message ${i} before gap`
|
||||
});
|
||||
sortKey = sortKey.previousKey();
|
||||
}
|
||||
sortKey = sortKey.nextKeyWithGap();
|
||||
await storage.insert({
|
||||
roomId,
|
||||
sortKey: sortKey.buffer(),
|
||||
message: `event to represent gap!`
|
||||
});
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
sortKey = sortKey.nextKey();
|
||||
await storage.insert({
|
||||
roomId,
|
||||
sortKey: sortKey.buffer(),
|
||||
message: `message ${i} after gap`
|
||||
});
|
||||
}
|
||||
records = await storage.selectAt(roomId, initialSortKey, 15);
|
||||
}
|
||||
console.log(records, "records");
|
||||
const nodes = records.map(r => {
|
||||
const li = document.createElement("li");
|
||||
li.appendChild(document.createTextNode(r.message));
|
||||
return li;
|
||||
});
|
||||
const parentNode = document.getElementById("messages");
|
||||
nodes.forEach(n => parentNode.appendChild(n));
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in a new issue