mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
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
@ -12,18 +12,14 @@ function createStores(db) {
|
|||||||
db.createObjectStore("roomSummary", {keyPath: "roomId"});
|
db.createObjectStore("roomSummary", {keyPath: "roomId"});
|
||||||
|
|
||||||
// need index to find live fragment? prooobably ok without for now
|
// need index to find live fragment? prooobably ok without for now
|
||||||
db.createObjectStore("timelineFragments", {keyPath: ["roomId", "id"]});
|
//key = room_id | fragment_id
|
||||||
const timelineEvents = db.createObjectStore("timelineEvents", {keyPath: ["roomId", "fragmentId", "eventIndex"]});
|
db.createObjectStore("timelineFragments", {keyPath: "key"});
|
||||||
timelineEvents.createIndex("byEventId", [
|
//key = room_id | fragment_id | event_index
|
||||||
"roomId",
|
const timelineEvents = db.createObjectStore("timelineEvents", {keyPath: "key"});
|
||||||
"event.event_id"
|
//eventIdKey = room_id | event_id
|
||||||
], {unique: true});
|
timelineEvents.createIndex("byEventId", "eventIdKey", {unique: true});
|
||||||
|
//key = room_id | event.type | event.state_key,
|
||||||
db.createObjectStore("roomState", {keyPath: [
|
db.createObjectStore("roomState", {keyPath: "key"});
|
||||||
"roomId",
|
|
||||||
"event.type",
|
|
||||||
"event.state_key"
|
|
||||||
]});
|
|
||||||
|
|
||||||
// const roomMembers = db.createObjectStore("roomMembers", {keyPath: [
|
// const roomMembers = db.createObjectStore("roomMembers", {keyPath: [
|
||||||
// "event.room_id",
|
// "event.room_id",
|
||||||
|
@ -12,6 +12,8 @@ export default class RoomStateStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setStateEvent(roomId, event) {
|
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 EventKey from "../../../room/timeline/EventKey.js";
|
||||||
import Platform from "../../../../Platform.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 {
|
class Range {
|
||||||
constructor(only, lower, upper, lowerOpen, upperOpen) {
|
constructor(only, lower, upper, lowerOpen, upperOpen) {
|
||||||
this._only = only;
|
this._only = only;
|
||||||
@ -13,14 +32,14 @@ class Range {
|
|||||||
asIDBKeyRange(roomId) {
|
asIDBKeyRange(roomId) {
|
||||||
// only
|
// only
|
||||||
if (this._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
|
// lowerBound
|
||||||
// also bound as we don't want to move into another roomId
|
// also bound as we don't want to move into another roomId
|
||||||
if (this._lower && !this._upper) {
|
if (this._lower && !this._upper) {
|
||||||
return IDBKeyRange.bound(
|
return IDBKeyRange.bound(
|
||||||
[roomId, this._lower.fragmentId, this._lower.eventIndex],
|
encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),
|
||||||
[roomId, this._lower.fragmentId, Platform.maxStorageKey],
|
encodeKey(roomId, this._lower.fragmentId, Platform.maxStorageKey),
|
||||||
this._lowerOpen,
|
this._lowerOpen,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -29,8 +48,8 @@ class Range {
|
|||||||
// also bound as we don't want to move into another roomId
|
// also bound as we don't want to move into another roomId
|
||||||
if (!this._lower && this._upper) {
|
if (!this._lower && this._upper) {
|
||||||
return IDBKeyRange.bound(
|
return IDBKeyRange.bound(
|
||||||
[roomId, this._upper.fragmentId, Platform.minStorageKey],
|
encodeKey(roomId, this._upper.fragmentId, Platform.minStorageKey),
|
||||||
[roomId, this._upper.fragmentId, this._upper.eventIndex],
|
encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),
|
||||||
false,
|
false,
|
||||||
this._upperOpen
|
this._upperOpen
|
||||||
);
|
);
|
||||||
@ -38,8 +57,8 @@ class Range {
|
|||||||
// bound
|
// bound
|
||||||
if (this._lower && this._upper) {
|
if (this._lower && this._upper) {
|
||||||
return IDBKeyRange.bound(
|
return IDBKeyRange.bound(
|
||||||
[roomId, this._lower.fragmentId, this._lower.eventIndex],
|
encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),
|
||||||
[roomId, this._upper.fragmentId, this._upper.eventIndex],
|
encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),
|
||||||
this._lowerOpen,
|
this._lowerOpen,
|
||||||
this._upperOpen
|
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.
|
// also passing them in chronological order makes sense as that's how we'll receive them almost always.
|
||||||
async findFirstOccurringEventId(roomId, eventIds) {
|
async findFirstOccurringEventId(roomId, eventIds) {
|
||||||
const byEventId = this._timelineStore.index("byEventId");
|
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);
|
const results = new Array(keys.length);
|
||||||
let firstFoundKey;
|
let firstFoundKey;
|
||||||
|
|
||||||
@ -191,8 +210,7 @@ export default class TimelineEventStore {
|
|||||||
firstFoundKey = firstFoundAndPrecedingResolved();
|
firstFoundKey = firstFoundAndPrecedingResolved();
|
||||||
return !!firstFoundKey;
|
return !!firstFoundKey;
|
||||||
});
|
});
|
||||||
// key of index is [roomId, eventId], so pick out eventId
|
return firstFoundKey && decodeEventIdKey(firstFoundKey).eventId;
|
||||||
return firstFoundKey && firstFoundKey[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Inserts a new entry into the store. The combination of roomId and eventKey should not exist yet, or an error is thrown.
|
/** 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} ...
|
* @throws {StorageError} ...
|
||||||
*/
|
*/
|
||||||
insert(entry) {
|
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?
|
// TODO: map error? or in idb/store?
|
||||||
return this._timelineStore.add(entry);
|
return this._timelineStore.add(entry);
|
||||||
}
|
}
|
||||||
@ -215,7 +235,7 @@ export default class TimelineEventStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(roomId, eventKey) {
|
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)
|
// returns the entries as well!! (or not always needed? I guess not always needed, so extra method)
|
||||||
removeRange(roomId, range) {
|
removeRange(roomId, range) {
|
||||||
@ -224,6 +244,6 @@ export default class TimelineEventStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getByEventId(roomId, eventId) {
|
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";
|
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 {
|
export default class RoomFragmentStore {
|
||||||
constructor(store) {
|
constructor(store) {
|
||||||
this._store = store;
|
this._store = store;
|
||||||
@ -7,8 +13,8 @@ export default class RoomFragmentStore {
|
|||||||
|
|
||||||
_allRange(roomId) {
|
_allRange(roomId) {
|
||||||
return IDBKeyRange.bound(
|
return IDBKeyRange.bound(
|
||||||
[roomId, Platform.minStorageKey],
|
encodeKey(roomId, Platform.minStorageKey),
|
||||||
[roomId, Platform.maxStorageKey]
|
encodeKey(roomId, Platform.maxStorageKey)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +41,7 @@ export default class RoomFragmentStore {
|
|||||||
// depends if we want to do anything smart with fragment ids,
|
// depends if we want to do anything smart with fragment ids,
|
||||||
// like give them meaning depending on range. not for now probably ...
|
// like give them meaning depending on range. not for now probably ...
|
||||||
add(fragment) {
|
add(fragment) {
|
||||||
|
fragment.key = encodeKey(fragment.roomId, fragment.id);
|
||||||
return this._store.add(fragment);
|
return this._store.add(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +50,6 @@ export default class RoomFragmentStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(roomId, fragmentId) {
|
get(roomId, fragmentId) {
|
||||||
return this._store.get([roomId, fragmentId]);
|
return this._store.get(encodeKey(roomId, fragmentId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user