forked from mystiq/hydrogen-web
encode idb array keys as sortable strings
that's why numeric parts of the keys have to be encoded as a fixed length, "big-endian" ordered strings, so string sorting will also sort the numeric keys correctly. this also assumes room ids don't contain the "|" character, we should probably escape the separator at some point.
This commit is contained in:
parent
106146660c
commit
0fd52be710
4 changed files with 54 additions and 29 deletions
|
@ -12,18 +12,14 @@ function createStores(db) {
|
|||
db.createObjectStore("roomSummary", {keyPath: "roomId"});
|
||||
|
||||
// need index to find live fragment? prooobably ok without for now
|
||||
db.createObjectStore("timelineFragments", {keyPath: ["roomId", "id"]});
|
||||
const timelineEvents = db.createObjectStore("timelineEvents", {keyPath: ["roomId", "fragmentId", "eventIndex"]});
|
||||
timelineEvents.createIndex("byEventId", [
|
||||
"roomId",
|
||||
"event.event_id"
|
||||
], {unique: true});
|
||||
|
||||
db.createObjectStore("roomState", {keyPath: [
|
||||
"roomId",
|
||||
"event.type",
|
||||
"event.state_key"
|
||||
]});
|
||||
//key = room_id | fragment_id
|
||||
db.createObjectStore("timelineFragments", {keyPath: "key"});
|
||||
//key = room_id | fragment_id | event_index
|
||||
const timelineEvents = db.createObjectStore("timelineEvents", {keyPath: "key"});
|
||||
//eventIdKey = room_id | event_id
|
||||
timelineEvents.createIndex("byEventId", "eventIdKey", {unique: true});
|
||||
//key = room_id | event.type | event.state_key,
|
||||
db.createObjectStore("roomState", {keyPath: "key"});
|
||||
|
||||
// const roomMembers = db.createObjectStore("roomMembers", {keyPath: [
|
||||
// "event.room_id",
|
||||
|
|
|
@ -12,6 +12,8 @@ export default class RoomStateStore {
|
|||
}
|
||||
|
||||
async setStateEvent(roomId, event) {
|
||||
return this._roomStateStore.put({roomId, event});
|
||||
const key = `${roomId}|${event.type}|${event.state_key}`;
|
||||
const entry = {roomId, event, key};
|
||||
return this._roomStateStore.put(entry);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,25 @@
|
|||
import EventKey from "../../../room/timeline/EventKey.js";
|
||||
import Platform from "../../../../Platform.js";
|
||||
|
||||
// storage keys are defined to be unsigned 32bit numbers in WebPlatform.js, which is assumed by idb
|
||||
function encodeUint32(n) {
|
||||
const hex = n.toString(16);
|
||||
return "0".repeat(8 - hex.length) + hex;
|
||||
}
|
||||
|
||||
function encodeKey(roomId, fragmentId, eventIndex) {
|
||||
return `${roomId}|${encodeUint32(fragmentId)}|${encodeUint32(eventIndex)}`;
|
||||
}
|
||||
|
||||
function encodeEventIdKey(roomId, eventId) {
|
||||
return `${roomId}|${eventId}`;
|
||||
}
|
||||
|
||||
function decodeEventIdKey(eventIdKey) {
|
||||
const [roomId, eventId] = eventIdKey.split("|");
|
||||
return {roomId, eventId};
|
||||
}
|
||||
|
||||
class Range {
|
||||
constructor(only, lower, upper, lowerOpen, upperOpen) {
|
||||
this._only = only;
|
||||
|
@ -13,14 +32,14 @@ class Range {
|
|||
asIDBKeyRange(roomId) {
|
||||
// only
|
||||
if (this._only) {
|
||||
return IDBKeyRange.only([roomId, this._only.fragmentId, this._only.eventIndex]);
|
||||
return IDBKeyRange.only(encodeKey(roomId, this._only.fragmentId, this._only.eventIndex));
|
||||
}
|
||||
// lowerBound
|
||||
// also bound as we don't want to move into another roomId
|
||||
if (this._lower && !this._upper) {
|
||||
return IDBKeyRange.bound(
|
||||
[roomId, this._lower.fragmentId, this._lower.eventIndex],
|
||||
[roomId, this._lower.fragmentId, Platform.maxStorageKey],
|
||||
encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),
|
||||
encodeKey(roomId, this._lower.fragmentId, Platform.maxStorageKey),
|
||||
this._lowerOpen,
|
||||
false
|
||||
);
|
||||
|
@ -29,8 +48,8 @@ class Range {
|
|||
// also bound as we don't want to move into another roomId
|
||||
if (!this._lower && this._upper) {
|
||||
return IDBKeyRange.bound(
|
||||
[roomId, this._upper.fragmentId, Platform.minStorageKey],
|
||||
[roomId, this._upper.fragmentId, this._upper.eventIndex],
|
||||
encodeKey(roomId, this._upper.fragmentId, Platform.minStorageKey),
|
||||
encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),
|
||||
false,
|
||||
this._upperOpen
|
||||
);
|
||||
|
@ -38,8 +57,8 @@ class Range {
|
|||
// bound
|
||||
if (this._lower && this._upper) {
|
||||
return IDBKeyRange.bound(
|
||||
[roomId, this._lower.fragmentId, this._lower.eventIndex],
|
||||
[roomId, this._upper.fragmentId, this._upper.eventIndex],
|
||||
encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),
|
||||
encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),
|
||||
this._lowerOpen,
|
||||
this._upperOpen
|
||||
);
|
||||
|
@ -170,7 +189,7 @@ export default class TimelineEventStore {
|
|||
// also passing them in chronological order makes sense as that's how we'll receive them almost always.
|
||||
async findFirstOccurringEventId(roomId, eventIds) {
|
||||
const byEventId = this._timelineStore.index("byEventId");
|
||||
const keys = eventIds.map(eventId => [roomId, eventId]);
|
||||
const keys = eventIds.map(eventId => encodeEventIdKey(roomId, eventId));
|
||||
const results = new Array(keys.length);
|
||||
let firstFoundKey;
|
||||
|
||||
|
@ -191,8 +210,7 @@ export default class TimelineEventStore {
|
|||
firstFoundKey = firstFoundAndPrecedingResolved();
|
||||
return !!firstFoundKey;
|
||||
});
|
||||
// key of index is [roomId, eventId], so pick out eventId
|
||||
return firstFoundKey && firstFoundKey[1];
|
||||
return firstFoundKey && decodeEventIdKey(firstFoundKey).eventId;
|
||||
}
|
||||
|
||||
/** Inserts a new entry into the store. The combination of roomId and eventKey should not exist yet, or an error is thrown.
|
||||
|
@ -201,6 +219,8 @@ export default class TimelineEventStore {
|
|||
* @throws {StorageError} ...
|
||||
*/
|
||||
insert(entry) {
|
||||
entry.key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex);
|
||||
entry.eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id);
|
||||
// TODO: map error? or in idb/store?
|
||||
return this._timelineStore.add(entry);
|
||||
}
|
||||
|
@ -215,7 +235,7 @@ export default class TimelineEventStore {
|
|||
}
|
||||
|
||||
get(roomId, eventKey) {
|
||||
return this._timelineStore.get([roomId, eventKey.fragmentId, eventKey.eventIndex]);
|
||||
return this._timelineStore.get(encodeKey(roomId, eventKey.fragmentId, eventKey.eventIndex));
|
||||
}
|
||||
// returns the entries as well!! (or not always needed? I guess not always needed, so extra method)
|
||||
removeRange(roomId, range) {
|
||||
|
@ -224,6 +244,6 @@ export default class TimelineEventStore {
|
|||
}
|
||||
|
||||
getByEventId(roomId, eventId) {
|
||||
return this._timelineStore.index("byEventId").get([roomId, eventId]);
|
||||
return this._timelineStore.index("byEventId").get(encodeEventIdKey(roomId, eventId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import Platform from "../../../../Platform.js";
|
||||
|
||||
function encodeKey(roomId, fragmentId) {
|
||||
let fragmentIdHex = fragmentId.toString(16);
|
||||
fragmentIdHex = "0".repeat(8 - fragmentIdHex.length) + fragmentIdHex;
|
||||
return `${roomId}|${fragmentIdHex}`;
|
||||
}
|
||||
|
||||
export default class RoomFragmentStore {
|
||||
constructor(store) {
|
||||
this._store = store;
|
||||
|
@ -7,8 +13,8 @@ export default class RoomFragmentStore {
|
|||
|
||||
_allRange(roomId) {
|
||||
return IDBKeyRange.bound(
|
||||
[roomId, Platform.minStorageKey],
|
||||
[roomId, Platform.maxStorageKey]
|
||||
encodeKey(roomId, Platform.minStorageKey),
|
||||
encodeKey(roomId, Platform.maxStorageKey)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -35,6 +41,7 @@ export default class RoomFragmentStore {
|
|||
// depends if we want to do anything smart with fragment ids,
|
||||
// like give them meaning depending on range. not for now probably ...
|
||||
add(fragment) {
|
||||
fragment.key = encodeKey(fragment.roomId, fragment.id);
|
||||
return this._store.add(fragment);
|
||||
}
|
||||
|
||||
|
@ -43,6 +50,6 @@ export default class RoomFragmentStore {
|
|||
}
|
||||
|
||||
get(roomId, fragmentId) {
|
||||
return this._store.get([roomId, fragmentId]);
|
||||
return this._store.get(encodeKey(roomId, fragmentId));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue