@travetto/model-memory 8.0.0-alpha.2 → 8.0.0-alpha.20
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 +1 -1
- package/package.json +8 -7
- package/src/service.ts +220 -65
package/README.md
CHANGED
|
@@ -16,5 +16,5 @@ yarn add @travetto/model-memory
|
|
|
16
16
|
This module provides a memory-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). Supported features:
|
|
17
17
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11)
|
|
18
18
|
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10)
|
|
19
|
-
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/indexed.ts#L11)
|
|
20
19
|
* [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8)
|
|
20
|
+
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L16)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-memory",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Memory backing for the travetto model module.",
|
|
6
6
|
"keywords": [
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
"directory": "module/model-memory"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
30
|
-
"@travetto/di": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/
|
|
29
|
+
"@travetto/config": "^8.0.0-alpha.18",
|
|
30
|
+
"@travetto/di": "^8.0.0-alpha.17",
|
|
31
|
+
"@travetto/model": "^8.0.0-alpha.18",
|
|
32
|
+
"@travetto/model-indexed": "^8.0.0-alpha.20",
|
|
33
|
+
"@travetto/schema": "^8.0.0-alpha.18"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
36
|
-
"@travetto/test": "^8.0.0-alpha.
|
|
36
|
+
"@travetto/cli": "^8.0.0-alpha.23",
|
|
37
|
+
"@travetto/test": "^8.0.0-alpha.17"
|
|
37
38
|
},
|
|
38
39
|
"peerDependenciesMeta": {
|
|
39
40
|
"@travetto/cli": {
|
package/src/service.ts
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type Class, type TimeSpan,
|
|
3
|
-
type ByteRange, type BinaryType, BinaryUtil, type BinaryArray, JSONUtil, BinaryMetadataUtil
|
|
2
|
+
type Class, type TimeSpan, castTo, type BinaryMetadata,
|
|
3
|
+
type ByteRange, type BinaryType, BinaryUtil, type BinaryArray, JSONUtil, BinaryMetadataUtil,
|
|
4
4
|
} from '@travetto/runtime';
|
|
5
5
|
import { Injectable, PostConstruct } from '@travetto/di';
|
|
6
6
|
import { Config } from '@travetto/config';
|
|
7
7
|
import {
|
|
8
|
-
type ModelType, type
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
type ModelType, type ModelCrudSupport, type ModelExpirySupport, type ModelStorageSupport, ModelRegistryIndex,
|
|
9
|
+
NotFoundError, ExistsError, type OptionalId, type ModelBlobSupport, ModelCrudUtil, ModelExpiryUtil, ModelStorageUtil,
|
|
10
|
+
IndexNotSupported,
|
|
11
|
+
type ModelListOptions,
|
|
11
12
|
} from '@travetto/model';
|
|
13
|
+
import {
|
|
14
|
+
type ModelIndexedSupport, type KeyedIndexSelection, type KeyedIndexBody, type ModelPageOptions, ModelIndexedUtil,
|
|
15
|
+
type SingleItemIndex, type SortedIndexSelection, type ModelPageResult, type SortedIndex,
|
|
16
|
+
type AllIndexes, isModelIndexedIndex, type FullKeyedIndexBody, type FullKeyedIndexWithPartialBody, ModelIndexedComputedIndex,
|
|
17
|
+
type ModelIndexedSearchOptions, type SortedIndexSelectionType,
|
|
18
|
+
} from '@travetto/model-indexed';
|
|
12
19
|
|
|
13
20
|
const ModelBlobNamespace = '__blobs';
|
|
14
21
|
const ModelBlobMetaNamespace = `${ModelBlobNamespace}_meta`;
|
|
15
22
|
|
|
16
23
|
type StoreType = Map<string, BinaryArray>;
|
|
17
24
|
|
|
25
|
+
const sortValue = (a: string | number, b: string | number): number =>
|
|
26
|
+
(typeof a === 'number' && typeof b === 'number') ?
|
|
27
|
+
a - b : (typeof a === 'string' && typeof b === 'string') ? a.localeCompare(b) : 0;
|
|
28
|
+
|
|
18
29
|
@Config('model.memory')
|
|
19
30
|
export class MemoryModelConfig {
|
|
20
31
|
modifyStorage?: boolean = true;
|
|
@@ -22,8 +33,8 @@ export class MemoryModelConfig {
|
|
|
22
33
|
cullRate?: number | TimeSpan;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
function indexName<T extends ModelType>(cls: Class<T>, idx:
|
|
26
|
-
return [cls.Ⲑid,
|
|
36
|
+
function indexName<T extends ModelType>(cls: Class<T>, idx: AllIndexes<T>, suffix?: string): string {
|
|
37
|
+
return [cls.Ⲑid, idx.name, suffix].filter(part => !!part).join(':');
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number): string | undefined {
|
|
@@ -40,13 +51,16 @@ function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | n
|
|
|
40
51
|
* Standard in-memory support
|
|
41
52
|
*/
|
|
42
53
|
@Injectable()
|
|
43
|
-
export class MemoryModelService implements
|
|
54
|
+
export class MemoryModelService implements
|
|
55
|
+
ModelCrudSupport, ModelBlobSupport,
|
|
56
|
+
ModelExpirySupport, ModelStorageSupport,
|
|
57
|
+
ModelIndexedSupport {
|
|
44
58
|
|
|
45
59
|
#store = new Map<string, StoreType>();
|
|
46
60
|
#indices = {
|
|
47
|
-
sorted: new Map<string, Map<string, Map<string, number>>>(),
|
|
48
|
-
|
|
49
|
-
};
|
|
61
|
+
'indexed:sorted': new Map<string, Map<string, Map<string, number | string>>>(),
|
|
62
|
+
'indexed:keyed': new Map<string, Map<string, Set<string>>>(),
|
|
63
|
+
} as const;
|
|
50
64
|
|
|
51
65
|
idSource = ModelCrudUtil.uuidSource();
|
|
52
66
|
config: MemoryModelConfig;
|
|
@@ -73,10 +87,16 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
73
87
|
async #removeIndices<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
|
|
74
88
|
try {
|
|
75
89
|
const item = await this.get(cls, id);
|
|
76
|
-
for (const idx of ModelRegistryIndex.getIndices(cls
|
|
90
|
+
for (const idx of ModelRegistryIndex.getIndices(cls)) {
|
|
91
|
+
if (!isModelIndexedIndex(idx)) {
|
|
92
|
+
continue; // Only support ModelIndexed indices
|
|
93
|
+
}
|
|
77
94
|
const idxName = indexName(cls, idx);
|
|
78
|
-
const
|
|
79
|
-
|
|
95
|
+
const computed = ModelIndexedComputedIndex.get(idx, item).validate({ sort: true });
|
|
96
|
+
switch (idx.type) {
|
|
97
|
+
case 'indexed:sorted':
|
|
98
|
+
case 'indexed:keyed': this.#indices[idx.type].get(idxName)?.get(computed.getKey())?.delete(id); break;
|
|
99
|
+
}
|
|
80
100
|
}
|
|
81
101
|
} catch (error) {
|
|
82
102
|
if (!(error instanceof NotFoundError)) {
|
|
@@ -86,14 +106,28 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
async #writeIndices<T extends ModelType>(cls: Class<T>, item: T): Promise<void> {
|
|
89
|
-
for (const idx of ModelRegistryIndex.getIndices(cls
|
|
109
|
+
for (const idx of ModelRegistryIndex.getIndices(cls)) {
|
|
110
|
+
if (!isModelIndexedIndex(idx)) {
|
|
111
|
+
continue; // Only support ModelIndexed indices
|
|
112
|
+
}
|
|
90
113
|
const idxName = indexName(cls, idx);
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
const computed = ModelIndexedComputedIndex.get(idx, item).validate({ sort: true });
|
|
115
|
+
const key = computed.getKey();
|
|
116
|
+
switch (idx.type) {
|
|
117
|
+
case 'indexed:keyed': {
|
|
118
|
+
if (idx.unique) {
|
|
119
|
+
const existing = this.#indices[idx.type].get(idxName)?.get(key);
|
|
120
|
+
if (existing && existing.size > 0 && !existing.has(item.id)) {
|
|
121
|
+
throw new ExistsError(cls, key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.#indices[idx.type].getOrInsert(idxName, new Map()).getOrInsert(key, new Set()).add(item.id);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'indexed:sorted': {
|
|
128
|
+
this.#indices[idx.type].getOrInsert(idxName, new Map()).getOrInsert(key, new Map()).set(item.id, computed.getSort());
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
97
131
|
}
|
|
98
132
|
}
|
|
99
133
|
}
|
|
@@ -112,22 +146,87 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
112
146
|
}
|
|
113
147
|
}
|
|
114
148
|
|
|
115
|
-
async #getIdByIndex<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
149
|
+
async #getIdByIndex<
|
|
150
|
+
T extends ModelType,
|
|
151
|
+
K extends KeyedIndexSelection<T>,
|
|
152
|
+
S extends SortedIndexSelection<T>
|
|
153
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<string> {
|
|
154
|
+
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
155
|
+
|
|
156
|
+
const index = this.#indices[idx.type].get(indexName(cls, idx))?.get(computed.getKey());
|
|
119
157
|
let id: string | undefined;
|
|
120
158
|
if (index) {
|
|
121
|
-
if (
|
|
122
|
-
|
|
159
|
+
if (computed.idPart) {
|
|
160
|
+
if (index.has(computed.idPart.value)) {
|
|
161
|
+
id = computed.idPart.value;
|
|
162
|
+
} else {
|
|
163
|
+
throw new NotFoundError(cls, computed.getKey({ sort: true }));
|
|
164
|
+
}
|
|
123
165
|
} else {
|
|
124
|
-
|
|
166
|
+
if (index instanceof Map) {
|
|
167
|
+
id = getFirstId(index, computed.getSort()); // Grab first id
|
|
168
|
+
} else if (index instanceof Set) {
|
|
169
|
+
id = getFirstId(index); // Grab first id
|
|
170
|
+
}
|
|
125
171
|
}
|
|
126
172
|
}
|
|
127
173
|
if (id) {
|
|
128
174
|
return id;
|
|
129
175
|
}
|
|
130
|
-
throw new NotFoundError(cls,
|
|
176
|
+
throw new NotFoundError(cls, computed.getKey({ sort: true }));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async * #iterateIds(
|
|
180
|
+
ids: string[],
|
|
181
|
+
options?: ModelListOptions & ModelPageOptions<number>,
|
|
182
|
+
): AsyncIterable<string[]> {
|
|
183
|
+
let offset = options && 'offset' in options ? options.offset ?? 0 : 0;
|
|
184
|
+
const batchSize = options?.batchSizeHint ?? 100;
|
|
185
|
+
let produced = 0;
|
|
186
|
+
const maxCount = options?.limit ?? Number.MAX_SAFE_INTEGER;
|
|
187
|
+
while (offset < ids.length && produced < maxCount) {
|
|
188
|
+
if (options?.abort?.aborted) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const end = Math.min(offset + batchSize, ids.length, maxCount - produced + offset);
|
|
192
|
+
const batch = ids.slice(offset, end);
|
|
193
|
+
if (batch.length) {
|
|
194
|
+
yield batch;
|
|
195
|
+
}
|
|
196
|
+
offset += batchSize;
|
|
197
|
+
produced += batch.length;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async * #getIndexIds<
|
|
202
|
+
T extends ModelType,
|
|
203
|
+
>(
|
|
204
|
+
cls: Class<T>,
|
|
205
|
+
idx: AllIndexes<T>,
|
|
206
|
+
body: KeyedIndexBody<T>,
|
|
207
|
+
options?: ModelListOptions & ModelPageOptions<number>,
|
|
208
|
+
filterIndex?: (indexValue: string | number) => boolean
|
|
209
|
+
): AsyncIterable<string[]> {
|
|
210
|
+
const computed = ModelIndexedComputedIndex.get(idx, body).validate();
|
|
211
|
+
if (!isModelIndexedIndex(idx)) {
|
|
212
|
+
throw new IndexNotSupported(cls, idx, 'Only ModelIndexed indices can be used with MemoryModelService');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const base = this.#indices[idx.type].get(indexName(cls, idx));
|
|
216
|
+
const index = base?.get(computed.getKey());
|
|
217
|
+
let ids: string[];
|
|
218
|
+
if (!index) {
|
|
219
|
+
ids = [];
|
|
220
|
+
} else if (index instanceof Map) {
|
|
221
|
+
ids = [...index.entries()]
|
|
222
|
+
.filter(([, sort]) => filterIndex ? filterIndex(sort) : true)
|
|
223
|
+
.sort((a, b) => sortValue(a[1], b[1]))
|
|
224
|
+
.map(([id,]) => id);
|
|
225
|
+
} else {
|
|
226
|
+
ids = [...index];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
yield* this.#iterateIds(ids, options);
|
|
131
230
|
}
|
|
132
231
|
|
|
133
232
|
@PostConstruct()
|
|
@@ -136,12 +235,9 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
136
235
|
ModelExpiryUtil.registerCull(this);
|
|
137
236
|
|
|
138
237
|
for (const cls of ModelRegistryIndex.getClasses()) {
|
|
139
|
-
for (const idx of ModelRegistryIndex.getConfig(cls).indices ??
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
console.error('Unique indices are not supported for', { cls: cls.Ⲑid, idx: idx.name });
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
238
|
+
for (const idx of Object.values(ModelRegistryIndex.getConfig(cls).indices ?? {})) {
|
|
239
|
+
if (!isModelIndexedIndex(idx)) {
|
|
240
|
+
console.error(`Indices of type ${idx.type} are not supported for`, { cls: cls.Ⲑid, name: idx.name, type: idx.type });
|
|
145
241
|
}
|
|
146
242
|
}
|
|
147
243
|
}
|
|
@@ -203,15 +299,9 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
203
299
|
await this.#persist(cls, where, 'remove');
|
|
204
300
|
}
|
|
205
301
|
|
|
206
|
-
async * list<T extends ModelType>(cls: Class<T
|
|
207
|
-
for (const
|
|
208
|
-
|
|
209
|
-
yield await this.get(cls, id);
|
|
210
|
-
} catch (error) {
|
|
211
|
-
if (!(error instanceof NotFoundError)) {
|
|
212
|
-
throw error;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
302
|
+
async * list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]> {
|
|
303
|
+
for await (const batch of this.#iterateIds([...this.#getStore(cls).keys()], options)) {
|
|
304
|
+
yield ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id)));
|
|
215
305
|
}
|
|
216
306
|
}
|
|
217
307
|
|
|
@@ -285,13 +375,14 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
285
375
|
|
|
286
376
|
async deleteStorage(): Promise<void> {
|
|
287
377
|
this.#store.clear();
|
|
288
|
-
this.#indices
|
|
289
|
-
|
|
378
|
+
for (const value of Object.values(this.#indices)) {
|
|
379
|
+
value.clear();
|
|
380
|
+
}
|
|
290
381
|
}
|
|
291
382
|
|
|
292
383
|
async upsertModel<T extends ModelType>(cls: Class<T>): Promise<void> {
|
|
293
|
-
for (const idx of ModelRegistryIndex.
|
|
294
|
-
if (idx
|
|
384
|
+
for (const idx of ModelRegistryIndex.getIndices(cls)) {
|
|
385
|
+
if (isModelIndexedIndex(idx)) {
|
|
295
386
|
this.#indices[idx.type].set(indexName(cls, idx), new Map());
|
|
296
387
|
}
|
|
297
388
|
}
|
|
@@ -307,33 +398,97 @@ export class MemoryModelService implements ModelCrudSupport, ModelBlobSupport, M
|
|
|
307
398
|
}
|
|
308
399
|
|
|
309
400
|
// Indexed
|
|
310
|
-
async getByIndex<
|
|
401
|
+
async getByIndex<
|
|
402
|
+
T extends ModelType,
|
|
403
|
+
K extends KeyedIndexSelection<T>,
|
|
404
|
+
S extends SortedIndexSelection<T>
|
|
405
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<T> {
|
|
311
406
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
407
|
+
|
|
312
408
|
}
|
|
313
409
|
|
|
314
|
-
async deleteByIndex<
|
|
410
|
+
async deleteByIndex<
|
|
411
|
+
T extends ModelType,
|
|
412
|
+
K extends KeyedIndexSelection<T>,
|
|
413
|
+
S extends SortedIndexSelection<T>
|
|
414
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<void> {
|
|
315
415
|
await this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
316
416
|
}
|
|
317
417
|
|
|
318
|
-
upsertByIndex<
|
|
418
|
+
upsertByIndex<
|
|
419
|
+
T extends ModelType,
|
|
420
|
+
K extends KeyedIndexSelection<T>,
|
|
421
|
+
S extends SortedIndexSelection<T>
|
|
422
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: OptionalId<T>): Promise<T> {
|
|
319
423
|
return ModelIndexedUtil.naiveUpsert(this, cls, idx, body);
|
|
320
424
|
}
|
|
321
425
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
426
|
+
updateByIndex<
|
|
427
|
+
T extends ModelType,
|
|
428
|
+
K extends KeyedIndexSelection<T>,
|
|
429
|
+
S extends SortedIndexSelection<T>
|
|
430
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: T): Promise<T> {
|
|
431
|
+
return ModelIndexedUtil.naiveUpdate(this, cls, idx, body);
|
|
432
|
+
}
|
|
326
433
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
434
|
+
async updatePartialByIndex<
|
|
435
|
+
T extends ModelType,
|
|
436
|
+
K extends KeyedIndexSelection<T>,
|
|
437
|
+
S extends SortedIndexSelection<T>
|
|
438
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexWithPartialBody<T, K, S>): Promise<T> {
|
|
439
|
+
const item = await ModelCrudUtil.naivePartialUpdate(cls, () => this.getByIndex(cls, idx, castTo(body)), castTo(body));
|
|
440
|
+
return this.update(cls, item);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async pageByIndex<
|
|
444
|
+
T extends ModelType,
|
|
445
|
+
K extends KeyedIndexSelection<T>,
|
|
446
|
+
S extends SortedIndexSelection<T>
|
|
447
|
+
>(
|
|
448
|
+
cls: Class<T>,
|
|
449
|
+
idx: SortedIndex<T, K, S>,
|
|
450
|
+
body: KeyedIndexBody<T, K>,
|
|
451
|
+
options?: ModelPageOptions
|
|
452
|
+
): Promise<ModelPageResult<T>> {
|
|
453
|
+
const offset = options?.offset ? JSONUtil.fromBase64<number>(options.offset) : 0;
|
|
454
|
+
let produced = 0;
|
|
455
|
+
|
|
456
|
+
const items: T[] = [];
|
|
457
|
+
for await (const batch of this.#getIndexIds(cls, idx, body, { limit: 100, ...options, offset })) {
|
|
458
|
+
produced += batch.length;
|
|
459
|
+
items.push(...await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id))));
|
|
460
|
+
}
|
|
461
|
+
return { items, nextOffset: items.length ? JSONUtil.toBase64(offset + produced) : undefined };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async * listByIndex<
|
|
465
|
+
T extends ModelType,
|
|
466
|
+
K extends KeyedIndexSelection<T>,
|
|
467
|
+
S extends SortedIndexSelection<T>
|
|
468
|
+
>(
|
|
469
|
+
cls: Class<T>,
|
|
470
|
+
idx: SortedIndex<T, K, S>,
|
|
471
|
+
body: KeyedIndexBody<T, K>,
|
|
472
|
+
options?: ModelListOptions
|
|
473
|
+
): AsyncIterable<T[]> {
|
|
474
|
+
for await (const batch of this.#getIndexIds(cls, idx, body, options)) {
|
|
475
|
+
yield ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id)));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async suggestByIndex<
|
|
480
|
+
T extends ModelType,
|
|
481
|
+
S extends SortedIndexSelection<T>,
|
|
482
|
+
K extends KeyedIndexSelection<T>,
|
|
483
|
+
B extends SortedIndexSelectionType<T, S> & string
|
|
484
|
+
>(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>, prefix: B, options?: ModelIndexedSearchOptions): Promise<T[]> {
|
|
485
|
+
const items: T[] = [];
|
|
486
|
+
for await (const batch of this.#getIndexIds(
|
|
487
|
+
cls, idx, body, options,
|
|
488
|
+
id => (typeof id === 'string' && id.startsWith(prefix))
|
|
489
|
+
)) {
|
|
490
|
+
items.push(...await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id))));
|
|
337
491
|
}
|
|
492
|
+
return items;
|
|
338
493
|
}
|
|
339
494
|
}
|