@travetto/model-firestore 8.0.0-alpha.11 → 8.0.0-alpha.13
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/package.json +6 -6
- package/src/service.ts +76 -63
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-firestore",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.13",
|
|
4
4
|
"description": "Firestore backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@google-cloud/firestore": "^8.3.0",
|
|
29
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
30
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/model-indexed": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/runtime": "^8.0.0-alpha.
|
|
29
|
+
"@travetto/config": "^8.0.0-alpha.12",
|
|
30
|
+
"@travetto/model": "^8.0.0-alpha.12",
|
|
31
|
+
"@travetto/model-indexed": "^8.0.0-alpha.13",
|
|
32
|
+
"@travetto/runtime": "^8.0.0-alpha.11"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
35
|
+
"@travetto/cli": "^8.0.0-alpha.17"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@travetto/cli": {
|
package/src/service.ts
CHANGED
|
@@ -4,10 +4,11 @@ import { castTo, JSONUtil, ShutdownManager, type Class } from '@travetto/runtime
|
|
|
4
4
|
import { Injectable, PostConstruct } from '@travetto/di';
|
|
5
5
|
import {
|
|
6
6
|
type ModelCrudSupport, ModelRegistryIndex, type ModelStorageSupport, type ModelType, NotFoundError, type OptionalId, ModelCrudUtil,
|
|
7
|
+
type ModelListOptions,
|
|
7
8
|
} from '@travetto/model';
|
|
8
9
|
import {
|
|
9
|
-
type ModelIndexedSupport, type KeyedIndexSelection, type KeyedIndexBody, type
|
|
10
|
-
type SingleItemIndex, type SortedIndexSelection, type
|
|
10
|
+
type ModelIndexedSupport, type KeyedIndexSelection, type KeyedIndexBody, type ModelPageOptions, ModelIndexedUtil,
|
|
11
|
+
type SingleItemIndex, type SortedIndexSelection, type ModelPageResult, type SortedIndex, type FullKeyedIndexBody,
|
|
11
12
|
type FullKeyedIndexWithPartialBody, ModelIndexedComputedIndex
|
|
12
13
|
} from '@travetto/model-indexed';
|
|
13
14
|
|
|
@@ -41,6 +42,25 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
41
42
|
return this.client.collection(this.#resolveTable(cls));
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
async #getIdByIndex<
|
|
46
|
+
T extends ModelType,
|
|
47
|
+
K extends KeyedIndexSelection<T>,
|
|
48
|
+
S extends SortedIndexSelection<T>
|
|
49
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<string> {
|
|
50
|
+
ModelCrudUtil.ensureNotSubType(cls);
|
|
51
|
+
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
52
|
+
const query = [...computed.allParts, ...(computed.idPart ? [computed.idPart] : [])].reduce<Query>(
|
|
53
|
+
(result, { path, value }) => result.where(path.join('.'), '==', value),
|
|
54
|
+
this.#getCollection(cls)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const item = await query.get();
|
|
58
|
+
if (!item || item.empty) {
|
|
59
|
+
throw new NotFoundError(`${cls.name} Index=${idx}`, computed.getKey());
|
|
60
|
+
}
|
|
61
|
+
return item.docs[0].id;
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
#buildIndexQuery<
|
|
45
65
|
T extends ModelType,
|
|
46
66
|
K extends KeyedIndexSelection<T>,
|
|
@@ -58,6 +78,39 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
58
78
|
return query;
|
|
59
79
|
}
|
|
60
80
|
|
|
81
|
+
async * #scanCollection<T extends ModelType>(
|
|
82
|
+
cls: Class<T>,
|
|
83
|
+
queryBuilder: () => Query,
|
|
84
|
+
options?: ModelListOptions & ModelPageOptions<number>
|
|
85
|
+
): AsyncIterable<{ items: T[], nextOffset?: number }> {
|
|
86
|
+
const limit = options?.limit ?? Number.MAX_SAFE_INTEGER;
|
|
87
|
+
const batchSize = Math.min(options?.batchSizeHint ?? 100, limit);
|
|
88
|
+
|
|
89
|
+
let offset = options?.offset ?? 0;
|
|
90
|
+
let produced = 0;
|
|
91
|
+
|
|
92
|
+
while (!(options?.abort?.aborted) && produced < limit) {
|
|
93
|
+
const query = queryBuilder().limit(batchSize).offset(offset);
|
|
94
|
+
|
|
95
|
+
let { docs } = await query.get();
|
|
96
|
+
if (docs.length === 0) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (produced + docs.length > limit) {
|
|
101
|
+
docs = docs.slice(0, limit - produced);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
offset += docs.length;
|
|
105
|
+
|
|
106
|
+
const items = await ModelCrudUtil.filterOutNotFound(
|
|
107
|
+
docs.map(item => ModelCrudUtil.load(cls, item.data()!)));
|
|
108
|
+
produced += items.length;
|
|
109
|
+
|
|
110
|
+
yield { items, nextOffset: offset };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
61
114
|
@PostConstruct()
|
|
62
115
|
async initializeClient(): Promise<void> {
|
|
63
116
|
globalThis.devProcessWarningExclusions?.push((_, category) => category === 'MetadataLookupWarning');
|
|
@@ -69,10 +122,9 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
69
122
|
async createStorage(): Promise<void> { }
|
|
70
123
|
async deleteStorage(): Promise<void> { }
|
|
71
124
|
|
|
72
|
-
async deleteModel(cls: Class): Promise<void> {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
125
|
+
async deleteModel<T extends ModelType>(cls: Class<T>): Promise<void> {
|
|
126
|
+
const docs = await this.#getCollection(cls).listDocuments();
|
|
127
|
+
await Promise.all(docs.map(doc => doc.delete()));
|
|
76
128
|
}
|
|
77
129
|
|
|
78
130
|
// Crud
|
|
@@ -126,39 +178,12 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
126
178
|
}
|
|
127
179
|
}
|
|
128
180
|
|
|
129
|
-
async * list<T extends ModelType>(cls: Class<T
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
yield await this.get(cls, item.id);
|
|
134
|
-
} catch (error) {
|
|
135
|
-
if (!(error instanceof NotFoundError)) {
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
181
|
+
async * list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]> {
|
|
182
|
+
for await (const { items } of this.#scanCollection(cls, () => this.#getCollection(cls), options)) {
|
|
183
|
+
yield items;
|
|
139
184
|
}
|
|
140
185
|
}
|
|
141
186
|
|
|
142
|
-
// Indexed
|
|
143
|
-
async #getIdByIndex<
|
|
144
|
-
T extends ModelType,
|
|
145
|
-
K extends KeyedIndexSelection<T>,
|
|
146
|
-
S extends SortedIndexSelection<T>
|
|
147
|
-
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<string> {
|
|
148
|
-
ModelCrudUtil.ensureNotSubType(cls);
|
|
149
|
-
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
150
|
-
const query = [...computed.allParts, ...(computed.idPart ? [computed.idPart] : [])].reduce<Query>(
|
|
151
|
-
(result, { path, value }) => result.where(path.join('.'), '==', value),
|
|
152
|
-
this.#getCollection(cls)
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
const item = await query.get();
|
|
156
|
-
if (!item || item.empty) {
|
|
157
|
-
throw new NotFoundError(`${cls.name} Index=${idx}`, computed.getKey());
|
|
158
|
-
}
|
|
159
|
-
return item.docs[0].id;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
187
|
// Indexed contract
|
|
163
188
|
|
|
164
189
|
async getByIndex<
|
|
@@ -210,20 +235,20 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
210
235
|
cls: Class<T>,
|
|
211
236
|
idx: SortedIndex<T, K, S>,
|
|
212
237
|
body: KeyedIndexBody<T, K>,
|
|
213
|
-
options?:
|
|
214
|
-
): Promise<
|
|
215
|
-
const offset = options?.offset ? JSONUtil.fromBase64<number>(options.offset) : 0;
|
|
216
|
-
const limit = options?.limit ?? 100;
|
|
217
|
-
const query = this.#buildIndexQuery(cls, idx, body)
|
|
218
|
-
.limit(limit)
|
|
219
|
-
.offset(offset);
|
|
220
|
-
|
|
238
|
+
options?: ModelPageOptions,
|
|
239
|
+
): Promise<ModelPageResult<T>> {
|
|
221
240
|
const items: T[] = [];
|
|
222
|
-
|
|
223
|
-
|
|
241
|
+
let nextOffset: number | undefined;
|
|
242
|
+
for await (const batch of this.#scanCollection(cls, () => this.#buildIndexQuery(cls, idx, body), {
|
|
243
|
+
limit: 100,
|
|
244
|
+
...options,
|
|
245
|
+
offset: options?.offset ? JSONUtil.fromBase64<number>(options.offset) : 0
|
|
246
|
+
})) {
|
|
247
|
+
items.push(...batch.items);
|
|
248
|
+
nextOffset = batch.nextOffset;
|
|
224
249
|
}
|
|
225
250
|
|
|
226
|
-
return { items, nextOffset:
|
|
251
|
+
return { items, nextOffset: nextOffset ? JSONUtil.toBase64(nextOffset) : undefined };
|
|
227
252
|
}
|
|
228
253
|
|
|
229
254
|
async * listByIndex<
|
|
@@ -234,22 +259,10 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
234
259
|
cls: Class<T>,
|
|
235
260
|
idx: SortedIndex<T, K, S>,
|
|
236
261
|
body: KeyedIndexBody<T, K>,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.limit(100)
|
|
242
|
-
.offset(offset);
|
|
243
|
-
|
|
244
|
-
const { docs: items } = await query.get();
|
|
245
|
-
if (items.length === 0) {
|
|
246
|
-
offset = -1;
|
|
247
|
-
} else {
|
|
248
|
-
for (const item of items) {
|
|
249
|
-
yield await ModelCrudUtil.load(cls, item.data()!);
|
|
250
|
-
}
|
|
251
|
-
offset += items.length;
|
|
252
|
-
}
|
|
262
|
+
options?: ModelListOptions
|
|
263
|
+
): AsyncIterable<T[]> {
|
|
264
|
+
for await (const { items } of this.#scanCollection(cls, () => this.#buildIndexQuery(cls, idx, body), options)) {
|
|
265
|
+
yield items;
|
|
253
266
|
}
|
|
254
267
|
}
|
|
255
268
|
}
|