@bodil/bdb 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/backend.d.ts +40 -0
- package/dist/backend.js +31 -0
- package/dist/backend.js.map +1 -1
- package/dist/index.d.ts +80 -9
- package/dist/index.js +22 -17
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +62 -14
- package/dist/index.test.js.map +1 -1
- package/dist/indices.d.ts +6 -0
- package/dist/indices.js +6 -0
- package/dist/indices.js.map +1 -1
- package/dist/query.d.ts +30 -24
- package/dist/query.js +35 -23
- package/dist/query.js.map +1 -1
- package/dist/table.d.ts +152 -31
- package/dist/table.js +147 -15
- package/dist/table.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +3 -3
- package/src/backend.ts +40 -0
- package/src/index.test.ts +84 -16
- package/src/index.ts +112 -45
- package/src/indices.ts +11 -6
- package/src/query.ts +88 -73
- package/src/table.ts +226 -66
- package/src/types.ts +1 -1
package/dist/table.js
CHANGED
|
@@ -8,18 +8,105 @@ import BTree from "sorted-btree";
|
|
|
8
8
|
import { CustomIndex } from "./indices";
|
|
9
9
|
import { IndexQuery, IteratorDirection } from "./query";
|
|
10
10
|
import { TableStorage } from "./storage";
|
|
11
|
+
/**
|
|
12
|
+
* A database table.
|
|
13
|
+
*
|
|
14
|
+
* To learn how to create a table, see the static method {@link Table.create}.
|
|
15
|
+
*
|
|
16
|
+
* @template Document The type of the documents this table stores.
|
|
17
|
+
*/
|
|
11
18
|
export class Table extends Emitter {
|
|
19
|
+
/**
|
|
20
|
+
* A promise which resolves when this table is ready to use.
|
|
21
|
+
*
|
|
22
|
+
* If the table isn't connected to a {@link StorageBackend}, this is a
|
|
23
|
+
* promise which resolves immediately, and you don't really need to await
|
|
24
|
+
* it. The table will be ready for use immediately.
|
|
25
|
+
*
|
|
26
|
+
* With an attached {@link StorageBackend}, this promise will resolve when
|
|
27
|
+
* the table has finished restoring its contents from the storage. You
|
|
28
|
+
* should not under any circumstances use the table before this is complete.
|
|
29
|
+
*/
|
|
30
|
+
get ready() {
|
|
31
|
+
return this.#ready;
|
|
32
|
+
}
|
|
33
|
+
#ready;
|
|
34
|
+
#changed;
|
|
35
|
+
/**
|
|
36
|
+
* A signal which updates whenever the table has been modified.
|
|
37
|
+
*/
|
|
38
|
+
get changed() {
|
|
39
|
+
return this.#changed.readOnly;
|
|
40
|
+
}
|
|
12
41
|
#storage;
|
|
13
42
|
#signals;
|
|
14
43
|
#signalCleanupRegistry;
|
|
15
44
|
#context;
|
|
45
|
+
/**
|
|
46
|
+
* Create a database {@link Table}.
|
|
47
|
+
*
|
|
48
|
+
* `Table.create` is a function which takes one type argument, the type of the
|
|
49
|
+
* object (which we'll call a *document*) you want the table to store, and no
|
|
50
|
+
* value arguments.
|
|
51
|
+
*
|
|
52
|
+
* This returns an object with one method: `withPrimaryIndex`. This method is
|
|
53
|
+
* what actually creates the table. So, the full incantation is, for instance:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* type Document = { id: string; value: number };
|
|
57
|
+
* const table = Table.create<Document>()
|
|
58
|
+
* .withPrimaryIndex(index<Document>().key("id"));
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* In order to look up something in a database table, you need an index. You can
|
|
62
|
+
* create an index using the {@link index} function, which, like `Table.create`,
|
|
63
|
+
* takes the document you're creating an index for as its type argument, and
|
|
64
|
+
* returns a selection of index constructors, of which the most straightforward
|
|
65
|
+
* one is {@link IndexConstructor.key}. This creates an index for a single named
|
|
66
|
+
* property of the document containing a primitive comparable value (a string, a
|
|
67
|
+
* number, or a bigint), and allows you to search for documents where the given
|
|
68
|
+
* property matches any given value. You can also create an index over multiple
|
|
69
|
+
* keys using {@link IndexConstructor.keys} or over a key containing an array
|
|
70
|
+
* using {@link IndexConstructor.array}. You can even create a completely
|
|
71
|
+
* customised index using {@link IndexConstructor.custom}.
|
|
72
|
+
*
|
|
73
|
+
* The primary index, unlike a regular index, is a unique identifier, and only
|
|
74
|
+
* one document can exist at any given time under the key or keys represented by
|
|
75
|
+
* the primary index. A table is required to have a primary index, which is why
|
|
76
|
+
* `Table.create` doesn't actually create the table until you call
|
|
77
|
+
* `withPrimaryIndex` on it. You can then add as many extra indices as you like
|
|
78
|
+
* using `withIndex`. To extend our example above with an additional index over
|
|
79
|
+
* the `value` property:
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* type Document = { id: string; value: number };
|
|
83
|
+
* const table = Table.create<Document>()
|
|
84
|
+
* .withPrimaryIndex(index<Document>().key("id"))
|
|
85
|
+
* .withIndex(index<Document>().key("value"));
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @see {@link index}
|
|
89
|
+
*
|
|
90
|
+
* @template Document The type of the document this table stores.
|
|
91
|
+
*/
|
|
92
|
+
static create() {
|
|
93
|
+
return {
|
|
94
|
+
withPrimaryIndex(primaryIndex) {
|
|
95
|
+
return new Table(primaryIndex);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** @internal */
|
|
16
100
|
constructor(primaryIndex) {
|
|
17
101
|
super();
|
|
18
|
-
this
|
|
102
|
+
this.#ready = Promise.resolve();
|
|
103
|
+
/** @ignore */
|
|
19
104
|
this.primary = new BTree();
|
|
105
|
+
/** @ignore */
|
|
20
106
|
this.indices = {};
|
|
107
|
+
/** @ignore */
|
|
21
108
|
this.indexTables = {};
|
|
22
|
-
this
|
|
109
|
+
this.#changed = Signal.from(null, { equals: () => false });
|
|
23
110
|
this.#signals = new BTree();
|
|
24
111
|
this.#signalCleanupRegistry = new FinalizationRegistry(this.#signalCleanup.bind(this));
|
|
25
112
|
this.#context = new DisposableContext();
|
|
@@ -29,6 +116,15 @@ export class Table extends Emitter {
|
|
|
29
116
|
super[Symbol.dispose]();
|
|
30
117
|
this.#context.dispose();
|
|
31
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Add an index to a table.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* type Document = { id: string; value: number };
|
|
124
|
+
* const table = Table.create<Document>()
|
|
125
|
+
* .withPrimaryIndex(index<Document>().key("id"))
|
|
126
|
+
* .withIndex(index<Document>().key("value"));
|
|
127
|
+
*/
|
|
32
128
|
withIndex(index) {
|
|
33
129
|
assert(this.indices[index.name] === undefined, `duplicate index definition: "${index.name}"`);
|
|
34
130
|
this.indices[index.name] = index;
|
|
@@ -36,9 +132,14 @@ export class Table extends Emitter {
|
|
|
36
132
|
index instanceof CustomIndex ? index.makeIndex() : new BTree();
|
|
37
133
|
return this;
|
|
38
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Attach a table to a {@link StorageBackend}.
|
|
137
|
+
*
|
|
138
|
+
* See {@link IndexedDBBackend.open} for an example of how to use this.
|
|
139
|
+
*/
|
|
39
140
|
withStorage(backend, name) {
|
|
40
141
|
this.#storage = new TableStorage(backend, name, this.primaryIndex);
|
|
41
|
-
this
|
|
142
|
+
this.#ready = this.#storage.ready;
|
|
42
143
|
this.#context.use(this.#storage.on((event) => {
|
|
43
144
|
switch (event.type) {
|
|
44
145
|
case "update":
|
|
@@ -55,14 +156,14 @@ export class Table extends Emitter {
|
|
|
55
156
|
signal(primaryKey) {
|
|
56
157
|
const active = this.#signals.get(primaryKey)?.deref();
|
|
57
158
|
if (active !== undefined) {
|
|
58
|
-
return active.readOnly
|
|
159
|
+
return active.readOnly;
|
|
59
160
|
}
|
|
60
161
|
const sig = Signal.from(this.get(primaryKey), {
|
|
61
162
|
equals: () => false,
|
|
62
163
|
});
|
|
63
164
|
this.#signalCleanupRegistry.register(sig, primaryKey);
|
|
64
165
|
this.#signals.set(primaryKey, new WeakRef(sig));
|
|
65
|
-
return sig.readOnly
|
|
166
|
+
return sig.readOnly;
|
|
66
167
|
}
|
|
67
168
|
get(primaryKey) {
|
|
68
169
|
return this.primary.get(primaryKey);
|
|
@@ -78,9 +179,18 @@ export class Table extends Emitter {
|
|
|
78
179
|
return this.#setItem(item, oldItem, primaryKey);
|
|
79
180
|
}
|
|
80
181
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
182
|
+
* Create or update a document.
|
|
183
|
+
*
|
|
184
|
+
* Given a primary key, apply the `update` function to the document stored
|
|
185
|
+
* under that key.
|
|
186
|
+
*
|
|
187
|
+
* If there's no such document, call the `create` function to create a new
|
|
188
|
+
* document and insert that under the given primary key. In this case, the
|
|
189
|
+
* update function is not called.
|
|
190
|
+
*
|
|
191
|
+
* The `update` function is passed to {@link produce | Immer.produce} to
|
|
192
|
+
* perform the update. It should modify the provided document in place, and
|
|
193
|
+
* it's not necessary to return it.
|
|
84
194
|
*/
|
|
85
195
|
createOrUpdate(primaryKey, create, update) {
|
|
86
196
|
const oldItem = this.primary.get(primaryKey);
|
|
@@ -88,9 +198,17 @@ export class Table extends Emitter {
|
|
|
88
198
|
return this.#setItem(item, oldItem, primaryKey);
|
|
89
199
|
}
|
|
90
200
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
201
|
+
* Update a document.
|
|
202
|
+
*
|
|
203
|
+
* Apply the `update` function to the document stored under the given
|
|
204
|
+
* primary key.
|
|
205
|
+
*
|
|
206
|
+
* If no such document exists, the update function is not called and
|
|
207
|
+
* `undefined` is returned. Otherwise, the updated document is returned.
|
|
208
|
+
*
|
|
209
|
+
* The `update` function is passed to {@link produce | Immer.produce} to
|
|
210
|
+
* perform the update. It should modify the provided document in place, and
|
|
211
|
+
* it's not necessary to return it.
|
|
94
212
|
*/
|
|
95
213
|
update(primaryKey, update) {
|
|
96
214
|
const oldItem = this.primary.get(primaryKey);
|
|
@@ -107,20 +225,34 @@ export class Table extends Emitter {
|
|
|
107
225
|
return this.#deleteFrom(items.length === 1 && isIterable(items[0]) ? items[0] : items);
|
|
108
226
|
}
|
|
109
227
|
/**
|
|
110
|
-
* Delete all
|
|
228
|
+
* Delete all documents from the table.
|
|
111
229
|
*/
|
|
112
230
|
clear() {
|
|
113
231
|
this.delete(...this.primary.keys());
|
|
114
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Iterate over the documents in the table, ordered by primary key.
|
|
235
|
+
*/
|
|
115
236
|
[Symbol.iterator]() {
|
|
116
|
-
return this.primary.values();
|
|
237
|
+
return Iterator.from(this.primary.values());
|
|
117
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Perform an {@link IndexQuery} using the specified index.
|
|
241
|
+
*/
|
|
118
242
|
where(index) {
|
|
119
243
|
return new IndexQuery(this, index, IteratorDirection.Ascending);
|
|
120
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Perform an {@link IndexQuery} using the specified index.
|
|
247
|
+
*
|
|
248
|
+
* This is an alias for {@link Table.where}.
|
|
249
|
+
*/
|
|
121
250
|
orderBy(index) {
|
|
122
251
|
return this.where(index);
|
|
123
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Get the number of documents currently stored in the table.
|
|
255
|
+
*/
|
|
124
256
|
size() {
|
|
125
257
|
return this.primary.size;
|
|
126
258
|
}
|
|
@@ -176,7 +308,7 @@ export class Table extends Emitter {
|
|
|
176
308
|
// emit an update event and update signals
|
|
177
309
|
this.#withSignal(this.primaryIndex.extractKey(item), (sig) => sig.set(item));
|
|
178
310
|
this.emit({ type: "update", item });
|
|
179
|
-
this
|
|
311
|
+
this.#changed.set(null);
|
|
180
312
|
return item;
|
|
181
313
|
}
|
|
182
314
|
#deleteItem(item, primaryKey) {
|
|
@@ -187,7 +319,7 @@ export class Table extends Emitter {
|
|
|
187
319
|
// emit a delete event and update signals
|
|
188
320
|
this.#withSignal(primaryKey, (sig) => sig.set(undefined));
|
|
189
321
|
this.emit({ type: "delete", key: primaryKey });
|
|
190
|
-
this
|
|
322
|
+
this.#changed.set(null);
|
|
191
323
|
}
|
|
192
324
|
#withSignal(primaryKey, fn) {
|
|
193
325
|
const sigRef = this.#signals.get(primaryKey);
|
package/dist/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAc,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,MAAM,cAAc,CAAC;AAGjC,OAAO,EAAE,WAAW,EAA8B,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAQzC,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type IndexablePrimitive = string | number |
|
|
1
|
+
export type IndexablePrimitive = string | number | bigint;
|
|
2
2
|
export type IndexableType = IndexablePrimitive | Array<IndexablePrimitive> | ReadonlyArray<IndexablePrimitive>;
|
|
3
3
|
export type CustomIndexablesOf<A, T> = string & keyof A & {
|
|
4
4
|
[Key in keyof A]: A[Key] extends T ? Key : never;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bodil/bdb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A signal enabled database",
|
|
5
5
|
"homepage": "https://codeberg.org/bodil/bdb",
|
|
6
6
|
"repository": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bodil/core": "^0.5.2",
|
|
30
|
-
"@bodil/opt": "^0.
|
|
31
|
-
"@bodil/signal": "^0.
|
|
30
|
+
"@bodil/opt": "^1.0.0",
|
|
31
|
+
"@bodil/signal": "^0.5.1",
|
|
32
32
|
"immer": "^11.1.3",
|
|
33
33
|
"sorted-btree": "2.1.0"
|
|
34
34
|
},
|
package/src/backend.ts
CHANGED
|
@@ -4,9 +4,18 @@ import type { IndexablePrimitive } from "./types";
|
|
|
4
4
|
|
|
5
5
|
export type DatabaseBroadcast = { event: "update" | "delete"; table: string; key: unknown };
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for IndexedDB backends.
|
|
9
|
+
*/
|
|
7
10
|
export type IndexedDBBackendConfig = {
|
|
11
|
+
/** Name of the IndexedDB database to connect to. */
|
|
8
12
|
name: string;
|
|
13
|
+
/** Version number of the database. */
|
|
9
14
|
version: number;
|
|
15
|
+
/**
|
|
16
|
+
* Name of the IndexedDB database table to store our database in. Defaults
|
|
17
|
+
* to `"store"`.
|
|
18
|
+
*/
|
|
10
19
|
table?: string;
|
|
11
20
|
};
|
|
12
21
|
|
|
@@ -27,6 +36,9 @@ function unpackRecord<A = unknown>(record: StorageRecord): A {
|
|
|
27
36
|
return deserialise(record.value) as A;
|
|
28
37
|
}
|
|
29
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Base class for storage backends.
|
|
41
|
+
*/
|
|
30
42
|
export abstract class StorageBackend {
|
|
31
43
|
readonly name: string;
|
|
32
44
|
|
|
@@ -49,12 +61,36 @@ export abstract class StorageBackend {
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Storage backend using an IndexedDB database.
|
|
66
|
+
*
|
|
67
|
+
* Use {@link IndexedDBBackend.open} to obtain a storage backend connected to
|
|
68
|
+
* the specified IndexedDB database.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Connect to the IndexedDB database.
|
|
72
|
+
* const storage = await IndexedDBBackend.open("my-docs", 1);
|
|
73
|
+
* // Declare a document type.
|
|
74
|
+
* type Document = { id: string; value: number };
|
|
75
|
+
* // Create the table.
|
|
76
|
+
* const docs = Table.create<Document>()
|
|
77
|
+
* .withPrimaryKey(index<Document>().key("id"))
|
|
78
|
+
* // Connect the table to our storage.
|
|
79
|
+
* .withStorage(storage);
|
|
80
|
+
* // Wait until the table has finished syncing with the storage.
|
|
81
|
+
* await docs.ready;
|
|
82
|
+
* // Ready for use!
|
|
83
|
+
* docs.add({ id: "Robert", value: 9001 });
|
|
84
|
+
*/
|
|
52
85
|
export class IndexedDBBackend extends StorageBackend {
|
|
53
86
|
private db?: IDBDatabase;
|
|
54
87
|
|
|
55
88
|
readonly version: number;
|
|
56
89
|
readonly table: string;
|
|
57
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Connect to an IndexedDB database.
|
|
93
|
+
*/
|
|
58
94
|
static async open(config: IndexedDBBackendConfig): Promise<IndexedDBBackend> {
|
|
59
95
|
const c = { ...backendConfigDefaults, ...config };
|
|
60
96
|
const storage = new IndexedDBBackend(c.name, c.version, c.table);
|
|
@@ -107,6 +143,7 @@ export class IndexedDBBackend extends StorageBackend {
|
|
|
107
143
|
});
|
|
108
144
|
}
|
|
109
145
|
|
|
146
|
+
/** @internal */
|
|
110
147
|
async get<A = unknown>(table: string, key: unknown): Promise<A> {
|
|
111
148
|
const value: StorageRecord = await this.transaction("readonly", (store) => {
|
|
112
149
|
return request(store.get([table, key] as IDBValidKey));
|
|
@@ -114,6 +151,7 @@ export class IndexedDBBackend extends StorageBackend {
|
|
|
114
151
|
return unpackRecord(value);
|
|
115
152
|
}
|
|
116
153
|
|
|
154
|
+
/** @internal */
|
|
117
155
|
async getAll<A = unknown>(table: string): Promise<Array<A>> {
|
|
118
156
|
const values = await this.transaction("readonly", (store) => {
|
|
119
157
|
return request(store.index("table").getAll(table));
|
|
@@ -121,6 +159,7 @@ export class IndexedDBBackend extends StorageBackend {
|
|
|
121
159
|
return (values as Array<StorageRecord>).map(unpackRecord<A>);
|
|
122
160
|
}
|
|
123
161
|
|
|
162
|
+
/** @internal */
|
|
124
163
|
async update(table: string, key: unknown, value: unknown): Promise<void> {
|
|
125
164
|
await this.transaction("readwrite", (store) => {
|
|
126
165
|
return request(store.put({ table, key, value: serialise(value) }));
|
|
@@ -128,6 +167,7 @@ export class IndexedDBBackend extends StorageBackend {
|
|
|
128
167
|
this.broadcast?.send({ event: "update", table, key });
|
|
129
168
|
}
|
|
130
169
|
|
|
170
|
+
/** @internal */
|
|
131
171
|
async delete(table: string, key: unknown): Promise<void> {
|
|
132
172
|
await this.transaction("readwrite", (store) => {
|
|
133
173
|
return request(store.delete([table, key as IDBValidKey]));
|
package/src/index.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { sleep } from "@bodil/core/async";
|
|
|
6
6
|
import { Signal } from "@bodil/signal";
|
|
7
7
|
import { expect, expectTypeOf, test } from "vitest";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { index, Table } from ".";
|
|
10
10
|
import { IndexedDBBackend } from "./backend";
|
|
11
11
|
import type { IndexablesOf } from "./types";
|
|
12
12
|
|
|
@@ -34,11 +34,12 @@ test("memdb basics", () => {
|
|
|
34
34
|
tags: Array<string>;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
const table =
|
|
38
|
-
.
|
|
39
|
-
.withIndex(index<TestItem>()("
|
|
40
|
-
.withIndex(
|
|
41
|
-
.withIndex(
|
|
37
|
+
const table = Table.create<TestItem>()
|
|
38
|
+
.withPrimaryIndex(index<TestItem>().key("id"))
|
|
39
|
+
.withIndex(index<TestItem>().time("created"))
|
|
40
|
+
.withIndex(index<TestItem>().key("uri"))
|
|
41
|
+
.withIndex(index<TestItem>().array("tags"))
|
|
42
|
+
.withIndex(index<TestItem>().keys("id", "uri"));
|
|
42
43
|
|
|
43
44
|
const now = time.now();
|
|
44
45
|
const [welp, wolp, wulp, wilp] = [
|
|
@@ -115,9 +116,9 @@ test("memdb basics", () => {
|
|
|
115
116
|
|
|
116
117
|
test("memdb index dupes", () => {
|
|
117
118
|
type Account = { id: string; order: number };
|
|
118
|
-
const accounts =
|
|
119
|
-
index<Account>()("
|
|
120
|
-
|
|
119
|
+
const accounts = Table.create<Account>()
|
|
120
|
+
.withPrimaryIndex(index<Account>().key("id"))
|
|
121
|
+
.withIndex(index<Account>().key("order"));
|
|
121
122
|
|
|
122
123
|
const account = { id: "test@test.com", order: 1 };
|
|
123
124
|
accounts.add({ ...account });
|
|
@@ -128,10 +129,10 @@ test("memdb index dupes", () => {
|
|
|
128
129
|
|
|
129
130
|
test("memdb signals", () => {
|
|
130
131
|
type Thing = { name: string; counter: number };
|
|
131
|
-
const things =
|
|
132
|
+
const things = Table.create<Thing>().withPrimaryIndex(index<Thing>().key("name"));
|
|
132
133
|
|
|
133
134
|
type ThingMap = { id: number; name: string };
|
|
134
|
-
const thingMaps =
|
|
135
|
+
const thingMaps = Table.create<ThingMap>().withPrimaryIndex(index<ThingMap>().key("id"));
|
|
135
136
|
thingMaps.add({ id: 1, name: "Mike" }, { id: 2, name: "Robert" });
|
|
136
137
|
|
|
137
138
|
const joe = things.signal("Joe");
|
|
@@ -163,7 +164,7 @@ test("memdb signals", () => {
|
|
|
163
164
|
|
|
164
165
|
test("failed update shouldn't change anything", () => {
|
|
165
166
|
type Thing = { name: string; counter: number };
|
|
166
|
-
const things =
|
|
167
|
+
const things = Table.create<Thing>().withPrimaryIndex(index<Thing>().key("name"));
|
|
167
168
|
|
|
168
169
|
things.add({ name: "Joe", counter: 321 });
|
|
169
170
|
expect(() =>
|
|
@@ -181,8 +182,9 @@ test("IndexedDB", async () => {
|
|
|
181
182
|
const before = now.subtract(time.seconds(25));
|
|
182
183
|
{
|
|
183
184
|
const store = await IndexedDBBackend.open({ name: "test", version: 1 });
|
|
184
|
-
const things =
|
|
185
|
-
.
|
|
185
|
+
const things = Table.create<Thing>()
|
|
186
|
+
.withPrimaryIndex(index<Thing>().key("key"))
|
|
187
|
+
.withIndex(index<Thing>().time("time"))
|
|
186
188
|
.withStorage(store, "things");
|
|
187
189
|
await things.ready;
|
|
188
190
|
things.add(
|
|
@@ -199,8 +201,9 @@ test("IndexedDB", async () => {
|
|
|
199
201
|
}
|
|
200
202
|
{
|
|
201
203
|
const store = await IndexedDBBackend.open({ name: "test", version: 1 });
|
|
202
|
-
const things =
|
|
203
|
-
.
|
|
204
|
+
const things = Table.create<Thing>()
|
|
205
|
+
.withPrimaryIndex(index<Thing>().key("key"))
|
|
206
|
+
.withIndex(index<Thing>().time("time"))
|
|
204
207
|
.withStorage(store, "things");
|
|
205
208
|
await things.ready;
|
|
206
209
|
expect(things.get("Joe")).deep.equal({ key: "Joe", value: "Armstrong", time: now });
|
|
@@ -210,3 +213,68 @@ test("IndexedDB", async () => {
|
|
|
210
213
|
]);
|
|
211
214
|
}
|
|
212
215
|
});
|
|
216
|
+
|
|
217
|
+
test("query.below/query.above", () => {
|
|
218
|
+
type Item = { id: string; value: number };
|
|
219
|
+
const table = Table.create<Item>()
|
|
220
|
+
.withPrimaryIndex(index<Item>().key("id"))
|
|
221
|
+
.withIndex(index<Item>().key("value"));
|
|
222
|
+
table.add(
|
|
223
|
+
{ id: "1", value: 1 },
|
|
224
|
+
{ id: "2", value: 2 },
|
|
225
|
+
{ id: "3", value: 3 },
|
|
226
|
+
{ id: "4", value: 4 },
|
|
227
|
+
{ id: "5", value: 5 },
|
|
228
|
+
);
|
|
229
|
+
expect(
|
|
230
|
+
table
|
|
231
|
+
.where("value")
|
|
232
|
+
.below(3)
|
|
233
|
+
.toArray()
|
|
234
|
+
.map((i) => i.value),
|
|
235
|
+
).toEqual([2, 1]);
|
|
236
|
+
expect(
|
|
237
|
+
table
|
|
238
|
+
.where("value")
|
|
239
|
+
.below(3)
|
|
240
|
+
.inclusive()
|
|
241
|
+
.toArray()
|
|
242
|
+
.map((i) => i.value),
|
|
243
|
+
).toEqual([3, 2, 1]);
|
|
244
|
+
expect(
|
|
245
|
+
table
|
|
246
|
+
.where("value")
|
|
247
|
+
.above(3)
|
|
248
|
+
.toArray()
|
|
249
|
+
.map((i) => i.value),
|
|
250
|
+
).toEqual([4, 5]);
|
|
251
|
+
expect(
|
|
252
|
+
table
|
|
253
|
+
.where("value")
|
|
254
|
+
.above(3)
|
|
255
|
+
.inclusive()
|
|
256
|
+
.toArray()
|
|
257
|
+
.map((i) => i.value),
|
|
258
|
+
).toEqual([3, 4, 5]);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("query.delete()", () => {
|
|
262
|
+
type Doc = { id: number };
|
|
263
|
+
const table = Table.create<Doc>().withPrimaryIndex(index<Doc>().key("id"));
|
|
264
|
+
for (let id = 0; id < 10; id++) {
|
|
265
|
+
table.add({ id });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const ids = table
|
|
269
|
+
.where("id")
|
|
270
|
+
.signal()
|
|
271
|
+
.map((docs) => docs.map((doc) => doc.id));
|
|
272
|
+
expect(ids.get()).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
273
|
+
|
|
274
|
+
expect(table.where("id").above(5).delete()).toEqual(4);
|
|
275
|
+
expect(
|
|
276
|
+
Iterator.from(table)
|
|
277
|
+
.map((doc) => doc.id)
|
|
278
|
+
.toArray(),
|
|
279
|
+
).toEqual([0, 1, 2, 3, 4, 5]);
|
|
280
|
+
});
|