@bodil/bdb 0.1.3 → 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/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 { arrayIndex, compoundIndex, createTable, index, timeIndex } from ".";
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 = createTable<TestItem>()(index<TestItem>()("id"))
38
- .withIndex(timeIndex<TestItem>()("created"))
39
- .withIndex(index<TestItem>()("uri"))
40
- .withIndex(arrayIndex<TestItem>()("tags"))
41
- .withIndex(compoundIndex<TestItem>()("id", "uri"));
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 = createTable<Account>()(index<Account>()("id")).withIndex(
119
- index<Account>()("order"),
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 = createTable<Thing>()(index<Thing>()("name"));
132
+ const things = Table.create<Thing>().withPrimaryIndex(index<Thing>().key("name"));
132
133
 
133
134
  type ThingMap = { id: number; name: string };
134
- const thingMaps = createTable<ThingMap>()(index<ThingMap>()("id"));
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 = createTable<Thing>()(index<Thing>()("name"));
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 = createTable<Thing>()(index<Thing>()("key"))
185
- .withIndex(timeIndex<Thing>()("time"))
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 = createTable<Thing>()(index<Thing>()("key"))
203
- .withIndex(timeIndex<Thing>()("time"))
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 });
@@ -213,7 +216,9 @@ test("IndexedDB", async () => {
213
216
 
214
217
  test("query.below/query.above", () => {
215
218
  type Item = { id: string; value: number };
216
- const table = createTable<Item>()(index<Item>()("id")).withIndex(index<Item>()("value"));
219
+ const table = Table.create<Item>()
220
+ .withPrimaryIndex(index<Item>().key("id"))
221
+ .withIndex(index<Item>().key("value"));
217
222
  table.add(
218
223
  { id: "1", value: 1 },
219
224
  { id: "2", value: 2 },
@@ -252,3 +257,24 @@ test("query.below/query.above", () => {
252
257
  .map((i) => i.value),
253
258
  ).toEqual([3, 4, 5]);
254
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
+ });
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { OrderFn } from "@bodil/core/order";
2
2
  import BTree from "sorted-btree";
3
3
 
4
- import { ArrayIndex, CompoundIndex, CustomIndex, PrimitiveIndex, type UnitIndex } from "./indices";
5
- import { Table } from "./table";
4
+ import { ArrayIndex, CompoundIndex, CustomIndex, PrimitiveIndex } from "./indices";
6
5
  import type { ArrayIndexablesOf, CustomIndexablesOf, PrimitiveIndexablesOf } from "./types";
7
6
 
8
7
  export type {
@@ -19,58 +18,126 @@ export type {
19
18
  CustomIndexablesOf,
20
19
  PrimitiveIndexablesOf,
21
20
  } from "./types";
22
- export type { Table, TableEvent } from "./table";
21
+ export { Table, type TableEvent } from "./table";
23
22
  export type { IndexQuery, ArrayQuery, ChainQuery } from "./query";
24
23
  export type { Broadcaster } from "./broadcast";
25
24
  export type { IndexedDBBackendConfig, DatabaseBroadcast } from "./backend";
26
25
 
27
26
  export { StorageBackend, IndexedDBBackend } from "./backend";
28
27
 
29
- export function createTable<A extends object>(): <PI extends UnitIndex<A>>(
30
- primaryIndex: PI,
31
- ) => Table<A, PI, PI["record"]> {
32
- return (primaryIndex) => new Table(primaryIndex);
33
- }
28
+ /**
29
+ * Constructor functions for creating an {@link index}.
30
+ * @interface
31
+ */
32
+ export type IndexConstructor<Document extends object> = {
33
+ /**
34
+ * Create an index for a single property on a document.
35
+ *
36
+ * The type of the property needs to match {@link IndexablePrimitive}, ie.
37
+ * it needs to be a string, a number or a bigint. If you need an index for a
38
+ * different value type, use {@link IndexConstructor.custom}.
39
+ *
40
+ * @example
41
+ * type Document = { id: string; value: number };
42
+ * const index = index<Document>().key("id");
43
+ */
44
+ key: <I extends PrimitiveIndexablesOf<Document>>(key: I) => PrimitiveIndex<Document, I>;
34
45
 
35
- export function index<A extends object>(): <I extends PrimitiveIndexablesOf<A>>(
36
- key: I,
37
- ) => PrimitiveIndex<A, I> {
38
- return (key) => new PrimitiveIndex(key);
39
- }
46
+ /**
47
+ * Create a compound index for a pair of properties on a document.
48
+ *
49
+ * The types of the properties need to match {@link IndexablePrimitive}, ie.
50
+ * they need to be strings, numbers or bigints.
51
+ *
52
+ * This index matches documents where both properties match the provided
53
+ * pair of values. Partial matches do not count.
54
+ *
55
+ * Whemn a compound index is used as a primary index, the unique key is the
56
+ * value pair, not either of the individual values, so you can have multiple
57
+ * documents with either one of the properties having identical values, but
58
+ * only one document matching any one given value pair.
59
+ *
60
+ * @example
61
+ * type Document = { id: string; value: number };
62
+ * const compoundIndex = index<Document>().keys("id", "value");
63
+ */
64
+ keys: <
65
+ I extends PrimitiveIndexablesOf<Document>,
66
+ J extends Exclude<PrimitiveIndexablesOf<Document>, I>,
67
+ >(
68
+ leftKey: I,
69
+ rightKey: J,
70
+ ) => CompoundIndex<Document, I, J>;
40
71
 
41
- export function timeIndex<A extends object>(): <I extends CustomIndexablesOf<A, Temporal.Instant>>(
42
- key: I,
43
- ) => CustomIndex<A, Temporal.Instant, I> {
44
- return (key) =>
45
- new CustomIndex(
46
- key,
47
- () => new BTree(undefined, Temporal.Instant.compare as OrderFn<A[typeof key]>),
48
- );
49
- }
72
+ /**
73
+ * Create an array index for a property on a document.
74
+ *
75
+ * This index works on a property with an array of
76
+ * {@link IndexablePrimitive}s. It will match documents where the lookup
77
+ * value is a member of the array.
78
+ *
79
+ * @example
80
+ * type Document = { id: string; flags: Array<string> };
81
+ * const arrayIndex = index<Document>().array("flags");
82
+ */
83
+ array: <I extends ArrayIndexablesOf<Document>, L extends Document[I] & Array<unknown>>(
84
+ key: I,
85
+ ) => ArrayIndex<Document, I, L>;
50
86
 
51
- export function arrayIndex<A extends object>(): <
52
- I extends ArrayIndexablesOf<A>,
53
- L extends A[I] & Array<unknown>,
54
- >(
55
- key: I,
56
- ) => ArrayIndex<A, I, L> {
57
- return (key) => new ArrayIndex(key);
58
- }
87
+ /**
88
+ * Create an index for a {@link Temporal.Instant} property on a document.
89
+ *
90
+ * This works exactly like {@link IndexConstructor.key}, except that it
91
+ * takes a {@link Temporal.Instant} instead of a primitive value.
92
+ */
93
+ time: <I extends CustomIndexablesOf<Document, Temporal.Instant>>(
94
+ key: I,
95
+ ) => CustomIndex<Document, Temporal.Instant, I>;
59
96
 
60
- export function compoundIndex<A extends object>(): <
61
- I extends PrimitiveIndexablesOf<A>,
62
- J extends Exclude<PrimitiveIndexablesOf<A>, I>,
63
- >(
64
- leftKey: I,
65
- rightKey: J,
66
- ) => CompoundIndex<A, I, J> {
67
- return (leftKey, rightKey) => new CompoundIndex(leftKey, rightKey);
68
- }
97
+ /**
98
+ * Create an index with a custom ordering function for a property on a
99
+ * document.
100
+ *
101
+ * This works like {@link IndexConstructor.key}, except that it takes any
102
+ * value rather than just a primitive, as long as you provide an
103
+ * {@link OrderFn | ordering function} for it.
104
+ */
105
+ custom: <T, I extends CustomIndexablesOf<Document, T>>(
106
+ key: I,
107
+ orderFn: OrderFn<T>,
108
+ ) => CustomIndex<Document, T, I>;
109
+ };
110
+
111
+ const indexConstructor = Symbol("indexConstructor");
69
112
 
70
- export function customIndex<A extends object, T>(): <I extends CustomIndexablesOf<A, T>>(
71
- key: I,
72
- orderFn: OrderFn<T>,
73
- ) => CustomIndex<A, T, I> {
74
- return (key, orderFn: OrderFn<T>) =>
75
- new CustomIndex(key, () => new BTree(undefined, orderFn as OrderFn<A[typeof key]>));
113
+ /**
114
+ * Create an index.
115
+ *
116
+ * This function takes a document type as its type argument, and returns an
117
+ * object with a selection of {@link IndexConstructor}s which allow you to
118
+ * create an index for the given document type.
119
+ *
120
+ * @example
121
+ * type Document = { id: string; timestamp: Temporal.Instant };
122
+ * const idIndex = index<Document>().key("id");
123
+ * const timeIndex = index<Document>().time("timestamp");
124
+ */
125
+ export function index<Document extends object>(): IndexConstructor<Document> {
126
+ (index as any)[indexConstructor] ??= {
127
+ key: (key) => new PrimitiveIndex(key),
128
+ keys: (leftKey, rightKey) => new CompoundIndex(leftKey, rightKey),
129
+ array: (key) => new ArrayIndex(key),
130
+ time: (key) =>
131
+ new CustomIndex(
132
+ key,
133
+ () =>
134
+ new BTree(undefined, Temporal.Instant.compare as OrderFn<Document[typeof key]>),
135
+ ),
136
+ custom: (key, orderFn) =>
137
+ new CustomIndex(
138
+ key,
139
+ () => new BTree(undefined, orderFn as OrderFn<Document[typeof key]>),
140
+ ),
141
+ } as IndexConstructor<Document>;
142
+ return (index as any)[indexConstructor] as IndexConstructor<Document>;
76
143
  }
package/src/indices.ts CHANGED
@@ -2,6 +2,7 @@ import type BTree from "sorted-btree";
2
2
 
3
3
  import type { ArrayIndexablesOf, CustomIndexablesOf, PrimitiveIndexablesOf } from "./types";
4
4
 
5
+ /** @internal */
5
6
  export abstract class Index<A extends object> {
6
7
  readonly name!: string;
7
8
  readonly keyType!: unknown;
@@ -9,10 +10,12 @@ export abstract class Index<A extends object> {
9
10
  abstract extractKeys(value: A): Array<typeof this.keyType>;
10
11
  }
11
12
 
13
+ /** @internal */
12
14
  export abstract class UnitIndex<A extends object> extends Index<A> {
13
15
  abstract extractKey(value: A): typeof this.keyType;
14
16
  }
15
17
 
18
+ /** @internal */
16
19
  export class CustomIndex<A extends object, T, I extends CustomIndexablesOf<A, T>>
17
20
  implements Index<A>, UnitIndex<A>
18
21
  {
@@ -37,6 +40,7 @@ export class CustomIndex<A extends object, T, I extends CustomIndexablesOf<A, T>
37
40
  }
38
41
  }
39
42
 
43
+ /** @internal */
40
44
  export class PrimitiveIndex<A extends object, I extends PrimitiveIndexablesOf<A>>
41
45
  implements Index<A>, UnitIndex<A>
42
46
  {
@@ -58,12 +62,12 @@ export class PrimitiveIndex<A extends object, I extends PrimitiveIndexablesOf<A>
58
62
  }
59
63
  }
60
64
 
65
+ /** @internal */
61
66
  export class ArrayIndex<
62
67
  A extends object,
63
68
  I extends ArrayIndexablesOf<A>,
64
69
  L extends A[I] & Array<unknown>,
65
- > implements Index<A>
66
- {
70
+ > implements Index<A> {
67
71
  readonly index: I;
68
72
  readonly name: `*${I}`;
69
73
  readonly keyType!: L[number];
@@ -79,11 +83,12 @@ export class ArrayIndex<
79
83
  }
80
84
  }
81
85
 
86
+ /** @internal */
82
87
  export class CompoundIndex<
83
- A extends object,
84
- I extends PrimitiveIndexablesOf<A>,
85
- J extends Exclude<PrimitiveIndexablesOf<A>, I>,
86
- >
88
+ A extends object,
89
+ I extends PrimitiveIndexablesOf<A>,
90
+ J extends Exclude<PrimitiveIndexablesOf<A>, I>,
91
+ >
87
92
  implements Index<A>, UnitIndex<A>
88
93
  {
89
94
  readonly leftIndex: I;
package/src/query.ts CHANGED
@@ -11,12 +11,12 @@ export enum IteratorDirection {
11
11
  Descending = 1,
12
12
  }
13
13
 
14
- function* indexIterator<A extends object, I extends Index<A>>(
15
- table: BTree<I["keyType"], Array<A>>,
14
+ function* indexIterator<Document extends object, I extends Index<Document>>(
15
+ table: BTree<I["keyType"], Array<Document>>,
16
16
  direction: IteratorDirection,
17
17
  start?: I["keyType"],
18
18
  skipStart = false,
19
- ): Generator<Readonly<A>> {
19
+ ): Generator<Readonly<Document>> {
20
20
  if (table.isEmpty) {
21
21
  return;
22
22
  }
@@ -35,12 +35,12 @@ function* indexIterator<A extends object, I extends Index<A>>(
35
35
  }
36
36
  }
37
37
 
38
- function* primaryIndexIterator<A extends object, I extends UnitIndex<A>>(
39
- table: BTree<I["keyType"], Readonly<A>>,
38
+ function* primaryIndexIterator<Document extends object, I extends UnitIndex<Document>>(
39
+ table: BTree<I["keyType"], Readonly<Document>>,
40
40
  direction: IteratorDirection,
41
41
  start?: I["keyType"],
42
42
  skipStart = false,
43
- ): Generator<Readonly<A>> {
43
+ ): Generator<Readonly<Document>> {
44
44
  if (table.isEmpty) {
45
45
  return;
46
46
  }
@@ -57,72 +57,78 @@ function* primaryIndexIterator<A extends object, I extends UnitIndex<A>>(
57
57
  }
58
58
  }
59
59
 
60
- export abstract class Query<A extends object> implements Iterable<Readonly<A>> {
61
- abstract [Symbol.iterator](): IterableIterator<Readonly<A>>;
62
- abstract signal(): Signal.Computed<Array<Readonly<A>>>;
60
+ export abstract class Query<Document extends object> implements Iterable<Readonly<Document>> {
61
+ abstract [Symbol.iterator](): IterableIterator<Readonly<Document>>;
62
+ abstract signal(): Signal.Computed<Array<Readonly<Document>>>;
63
63
 
64
- map<B>(mapFn: (item: Readonly<A>) => B): IterableIterator<B> {
64
+ map<B>(mapFn: (item: Readonly<Document>) => B): IterableIterator<B> {
65
65
  return Iterator.from(this).map(mapFn);
66
66
  }
67
67
 
68
- forEach(action: (item: A) => void): void {
68
+ forEach(action: (item: Document) => void): void {
69
69
  for (const item of this) {
70
70
  action(item);
71
71
  }
72
72
  }
73
73
 
74
- toArray(): Array<Readonly<A>> {
74
+ toArray(): Array<Readonly<Document>> {
75
75
  return Array.from(this);
76
76
  }
77
77
  }
78
78
 
79
79
  export abstract class TableQuery<
80
- A extends object,
81
- PI extends UnitIndex<A>,
80
+ Document extends object,
81
+ PI extends UnitIndex<Document>,
82
82
  Ix extends object,
83
- > extends Query<A> {
84
- table: Table<A, PI, Ix>;
83
+ > extends Query<Document> {
84
+ /** @ignore */
85
+ protected table: Table<Document, PI, Ix>;
85
86
 
86
- constructor(table: Table<A, PI, Ix>) {
87
+ constructor(table: Table<Document, PI, Ix>) {
87
88
  super();
88
89
  this.table = table;
89
90
  }
90
91
 
91
- signal(): Signal.Computed<Array<Readonly<A>>> {
92
+ signal(): Signal.Computed<Array<Readonly<Document>>> {
92
93
  return Signal.computed(() => {
93
94
  this.table.changed.get();
94
95
  return Array.from(this);
95
96
  });
96
97
  }
97
98
 
99
+ /**
100
+ * Delete all documents from the table matching this query.
101
+ */
98
102
  delete(): number {
99
- return this.table.delete(...Array.from(this));
103
+ return this.table.delete(
104
+ ...Array.from(this).map((doc) => this.table.primaryIndex.extractKey(doc)),
105
+ );
100
106
  }
101
107
 
102
- filter(predicate: (item: Readonly<A>) => boolean): ChainQuery<A, PI, Ix> {
108
+ filter(predicate: (item: Readonly<Document>) => boolean): ChainQuery<Document, PI, Ix> {
103
109
  return new ChainQuery(this.table, this, (iter) => Iterator.from(iter).filter(predicate));
104
110
  }
105
111
 
106
- limit(count: number): ChainQuery<A, PI, Ix> {
112
+ limit(count: number): ChainQuery<Document, PI, Ix> {
107
113
  return new ChainQuery(this.table, this, (iter) => Iterator.from(iter).take(count));
108
114
  }
109
115
  }
110
116
 
111
117
  export class IndexQuery<
112
- A extends object,
113
- PI extends UnitIndex<A>,
114
- I extends Index<A>,
118
+ Document extends object,
119
+ PI extends UnitIndex<Document>,
120
+ I extends Index<Document>,
115
121
  Ix extends object,
116
- > extends TableQuery<A, PI, Ix> {
122
+ > extends TableQuery<Document, PI, Ix> {
117
123
  private readonly index: I;
118
- private readonly indexTable?: BTree<I["keyType"], Array<A>>;
124
+ private readonly indexTable?: BTree<I["keyType"], Array<Document>>;
119
125
  private readonly isPrimary: boolean;
120
126
  private start?: I["keyType"];
121
127
  private skipStart = false;
122
128
  private direction: IteratorDirection;
123
129
 
124
130
  constructor(
125
- table: Table<A, PI, Ix>,
131
+ table: Table<Document, PI, Ix>,
126
132
  indexKey: keyof Ix & string,
127
133
  direction: IteratorDirection,
128
134
  start?: I["keyType"],
@@ -140,7 +146,7 @@ export class IndexQuery<
140
146
  this.start = start;
141
147
  }
142
148
 
143
- [Symbol.iterator](): IterableIterator<Readonly<A>> {
149
+ [Symbol.iterator](): IterableIterator<Readonly<Document>> {
144
150
  return this.isPrimary
145
151
  ? primaryIndexIterator(this.table.primary, this.direction, this.start, this.skipStart)
146
152
  : indexIterator(this.indexTable!, this.direction, this.start, this.skipStart);
@@ -157,7 +163,7 @@ export class IndexQuery<
157
163
  }
158
164
  }
159
165
 
160
- equals(value: I["keyType"]): ArrayQuery<A, PI, Ix> {
166
+ equals(value: I["keyType"]): ArrayQuery<Document, PI, Ix> {
161
167
  if (this.isPrimary) {
162
168
  const item = this.table.get(value);
163
169
  return new ArrayQuery(this.table, item === undefined ? [] : [item]);
@@ -198,43 +204,45 @@ export class IndexQuery<
198
204
  }
199
205
 
200
206
  export class ChainQuery<
201
- A extends object,
202
- PI extends UnitIndex<A>,
207
+ Document extends object,
208
+ PI extends UnitIndex<Document>,
203
209
  Ix extends object,
204
- > extends TableQuery<A, PI, Ix> {
205
- private readonly parent: Query<A>;
210
+ > extends TableQuery<Document, PI, Ix> {
211
+ private readonly parent: Query<Document>;
206
212
  private readonly iterator: (
207
- parentIterator: IterableIterator<Readonly<A>>,
208
- ) => IterableIterator<Readonly<A>>;
213
+ parentIterator: IterableIterator<Readonly<Document>>,
214
+ ) => IterableIterator<Readonly<Document>>;
209
215
 
210
216
  constructor(
211
- table: Table<A, PI, Ix>,
212
- parent: Query<A>,
213
- iterator: (parentIterator: IterableIterator<Readonly<A>>) => IterableIterator<Readonly<A>>,
217
+ table: Table<Document, PI, Ix>,
218
+ parent: Query<Document>,
219
+ iterator: (
220
+ parentIterator: IterableIterator<Readonly<Document>>,
221
+ ) => IterableIterator<Readonly<Document>>,
214
222
  ) {
215
223
  super(table);
216
224
  this.parent = parent;
217
225
  this.iterator = iterator;
218
226
  }
219
227
 
220
- [Symbol.iterator](): IterableIterator<Readonly<A>> {
228
+ [Symbol.iterator](): IterableIterator<Readonly<Document>> {
221
229
  return this.iterator(this.parent[Symbol.iterator]());
222
230
  }
223
231
  }
224
232
 
225
233
  export class ArrayQuery<
226
- A extends object,
227
- PI extends UnitIndex<A>,
234
+ Document extends object,
235
+ PI extends UnitIndex<Document>,
228
236
  Ix extends object,
229
- > extends TableQuery<A, PI, Ix> {
230
- private readonly values: Array<Readonly<A>>;
237
+ > extends TableQuery<Document, PI, Ix> {
238
+ private readonly values: Array<Readonly<Document>>;
231
239
 
232
- constructor(table: Table<A, PI, Ix>, values: Array<Readonly<A>>) {
240
+ constructor(table: Table<Document, PI, Ix>, values: Array<Readonly<Document>>) {
233
241
  super(table);
234
242
  this.values = values;
235
243
  }
236
244
 
237
- [Symbol.iterator](): IterableIterator<Readonly<A>> {
245
+ [Symbol.iterator](): IterableIterator<Readonly<Document>> {
238
246
  return this.values[Symbol.iterator]();
239
247
  }
240
248
  }