@peerbit/document 8.1.2 → 8.2.0-a4ac71a
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/dist/benchmark/iterate-replicate-2.js +7 -1
- package/dist/benchmark/iterate-replicate-2.js.map +1 -1
- package/dist/src/domain.d.ts +9 -3
- package/dist/src/domain.d.ts.map +1 -1
- package/dist/src/domain.js.map +1 -1
- package/dist/src/program.d.ts +2 -1
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +15 -4
- package/dist/src/program.js.map +1 -1
- package/dist/src/resumable-iterator.d.ts +6 -3
- package/dist/src/resumable-iterator.d.ts.map +1 -1
- package/dist/src/resumable-iterator.js +6 -2
- package/dist/src/resumable-iterator.js.map +1 -1
- package/dist/src/search.d.ts +33 -29
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +261 -60
- package/dist/src/search.js.map +1 -1
- package/package.json +74 -74
- package/src/domain.ts +12 -3
- package/src/program.ts +19 -8
- package/src/resumable-iterator.ts +10 -3
- package/src/search.ts +564 -174
package/src/search.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type AbstractType, field, serialize, variant } from "@dao-xyz/borsh";
|
|
2
|
+
import type { PeerId } from "@libp2p/interface";
|
|
2
3
|
import { Cache } from "@peerbit/cache";
|
|
3
4
|
import {
|
|
4
5
|
type MaybePromise,
|
|
5
6
|
PublicSignKey,
|
|
7
|
+
getPublicKeyFromPeerId,
|
|
6
8
|
sha256Base64Sync,
|
|
7
9
|
} from "@peerbit/crypto";
|
|
8
10
|
import * as types from "@peerbit/document-interface";
|
|
@@ -20,7 +22,7 @@ import {
|
|
|
20
22
|
} from "@peerbit/rpc";
|
|
21
23
|
import { type ReplicationDomain, SharedLog } from "@peerbit/shared-log";
|
|
22
24
|
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
23
|
-
import { AbortError } from "@peerbit/time";
|
|
25
|
+
import { AbortError, waitFor } from "@peerbit/time";
|
|
24
26
|
import { concat, fromString } from "uint8arrays";
|
|
25
27
|
import { copySerialization } from "./borsh.js";
|
|
26
28
|
import { MAX_BATCH_SIZE } from "./constants.js";
|
|
@@ -30,9 +32,9 @@ import { ResumableIterators } from "./resumable-iterator.js";
|
|
|
30
32
|
|
|
31
33
|
const logger = loggerFn({ module: "document-index" });
|
|
32
34
|
|
|
33
|
-
type BufferedResult<T
|
|
35
|
+
type BufferedResult<T, I extends Record<string, any>> = {
|
|
34
36
|
value: T;
|
|
35
|
-
indexed:
|
|
37
|
+
indexed: I;
|
|
36
38
|
context: types.Context;
|
|
37
39
|
from: PublicSignKey;
|
|
38
40
|
};
|
|
@@ -44,11 +46,16 @@ export type RemoteQueryOptions<R, D> = RPCRequestAllOptions<R> & {
|
|
|
44
46
|
domain?: ExtractArgs<D>;
|
|
45
47
|
eager?: boolean; // whether to query newly joined peers before they have matured
|
|
46
48
|
};
|
|
47
|
-
export type QueryOptions<R, D> = {
|
|
48
|
-
remote?: boolean | RemoteQueryOptions<types.AbstractSearchResult
|
|
49
|
+
export type QueryOptions<R, D, Resolve extends boolean | undefined> = {
|
|
50
|
+
remote?: boolean | RemoteQueryOptions<types.AbstractSearchResult, D>;
|
|
49
51
|
local?: boolean;
|
|
52
|
+
resolve?: Resolve;
|
|
50
53
|
};
|
|
51
|
-
export type SearchOptions<
|
|
54
|
+
export type SearchOptions<
|
|
55
|
+
R,
|
|
56
|
+
D,
|
|
57
|
+
Resolve extends boolean | undefined,
|
|
58
|
+
> = QueryOptions<R, D, Resolve>;
|
|
52
59
|
|
|
53
60
|
type Transformer<T, I> = (obj: T, context: types.Context) => MaybePromise<I>;
|
|
54
61
|
|
|
@@ -59,37 +66,104 @@ export type ResultsIterator<T> = {
|
|
|
59
66
|
all: () => Promise<T[]>;
|
|
60
67
|
};
|
|
61
68
|
|
|
62
|
-
type QueryDetailedOptions<
|
|
69
|
+
type QueryDetailedOptions<
|
|
70
|
+
T,
|
|
71
|
+
D,
|
|
72
|
+
Resolve extends boolean | undefined,
|
|
73
|
+
> = QueryOptions<T, D, Resolve> & {
|
|
63
74
|
onResponse?: (
|
|
64
|
-
response: types.AbstractSearchResult
|
|
75
|
+
response: types.AbstractSearchResult,
|
|
65
76
|
from: PublicSignKey,
|
|
66
77
|
) => void | Promise<void>;
|
|
67
78
|
};
|
|
68
79
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
type QueryLike /* <Resolve extends boolean | undefined> */ = {
|
|
81
|
+
query?: indexerTypes.Query[] | indexerTypes.QueryLike;
|
|
82
|
+
sort?: indexerTypes.Sort[] | indexerTypes.Sort | indexerTypes.SortLike;
|
|
83
|
+
/* resolve?: Resolve; */
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/* type ExtractResolve<R> =
|
|
87
|
+
R extends QueryLike<infer X>
|
|
88
|
+
? X extends boolean // if X is a boolean (true or false)
|
|
89
|
+
? X
|
|
90
|
+
: true // else default to true
|
|
91
|
+
: true; // if R isn't QueryLike at all, default to true */
|
|
92
|
+
|
|
93
|
+
type ExtractResolveFromOptions<O> =
|
|
94
|
+
O extends QueryOptions<any, any, infer X>
|
|
95
|
+
? X extends boolean // if X is a boolean (true or false)
|
|
96
|
+
? X
|
|
97
|
+
: true // else default to true
|
|
98
|
+
: true; // if R isn't QueryLike at all, default to true
|
|
99
|
+
|
|
100
|
+
const coerceQuery = <Resolve extends boolean | undefined>(
|
|
101
|
+
query: types.SearchRequest | types.SearchRequestIndexed | QueryLike,
|
|
102
|
+
options?: QueryOptions<any, any, Resolve>,
|
|
103
|
+
) => {
|
|
104
|
+
let replicate =
|
|
105
|
+
typeof options?.remote !== "boolean" ? options?.remote?.replicate : false;
|
|
106
|
+
|
|
107
|
+
if (
|
|
108
|
+
query instanceof types.SearchRequestIndexed &&
|
|
109
|
+
query.replicate === false &&
|
|
110
|
+
replicate
|
|
111
|
+
) {
|
|
112
|
+
query.replicate = true;
|
|
113
|
+
return query;
|
|
114
|
+
}
|
|
115
|
+
if (query instanceof types.SearchRequest) {
|
|
116
|
+
return query;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const queryObject = query as QueryLike;
|
|
120
|
+
|
|
121
|
+
return options?.resolve || options?.resolve == null
|
|
122
|
+
? new types.SearchRequest({
|
|
123
|
+
query: indexerTypes.toQuery(queryObject.query),
|
|
124
|
+
sort: indexerTypes.toSort(query.sort),
|
|
125
|
+
})
|
|
126
|
+
: new types.SearchRequestIndexed({
|
|
127
|
+
query: indexerTypes.toQuery(queryObject.query),
|
|
128
|
+
sort: indexerTypes.toSort(query.sort),
|
|
129
|
+
replicate,
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const introduceEntries = async <
|
|
134
|
+
T,
|
|
135
|
+
I,
|
|
136
|
+
D,
|
|
137
|
+
R extends types.SearchRequest | types.SearchRequestIndexed,
|
|
138
|
+
>(
|
|
139
|
+
queryRequest: R,
|
|
140
|
+
responses: RPCResponse<types.AbstractSearchResult>[],
|
|
141
|
+
documentType: AbstractType<T>,
|
|
142
|
+
indexedType: AbstractType<I>,
|
|
73
143
|
sync: (
|
|
74
|
-
|
|
75
|
-
|
|
144
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
145
|
+
response: types.Results<any>,
|
|
76
146
|
) => Promise<void>,
|
|
77
|
-
options?: QueryDetailedOptions<T, D>,
|
|
78
|
-
): Promise<RPCResponse<types.Results<
|
|
79
|
-
const results: RPCResponse<types.Results<
|
|
147
|
+
options?: QueryDetailedOptions<T, D, any>,
|
|
148
|
+
): Promise<RPCResponse<types.Results<types.ResultTypeFromRequest<R>>>[]> => {
|
|
149
|
+
const results: RPCResponse<types.Results<any>>[] = [];
|
|
80
150
|
for (const response of responses) {
|
|
81
151
|
if (!response.from) {
|
|
82
152
|
logger.error("Missing from for response");
|
|
83
153
|
}
|
|
84
154
|
|
|
85
155
|
if (response.response instanceof types.Results) {
|
|
86
|
-
response.response.results.forEach((r) =>
|
|
156
|
+
response.response.results.forEach((r) =>
|
|
157
|
+
r instanceof types.ResultValue
|
|
158
|
+
? r.init(documentType)
|
|
159
|
+
: r.init(indexedType),
|
|
160
|
+
);
|
|
87
161
|
if (typeof options?.remote !== "boolean" && options?.remote?.replicate) {
|
|
88
162
|
await sync(queryRequest, response.response);
|
|
89
163
|
}
|
|
90
164
|
options?.onResponse &&
|
|
91
165
|
(await options.onResponse(response.response, response.from!)); // TODO fix types
|
|
92
|
-
results.push(response as RPCResponse<types.Results<
|
|
166
|
+
results.push(response as RPCResponse<types.Results<any>>);
|
|
93
167
|
} else if (response.response instanceof types.NoAccess) {
|
|
94
168
|
logger.error("Search resulted in access error");
|
|
95
169
|
} else {
|
|
@@ -134,6 +208,17 @@ export type CanRead<T> = (
|
|
|
134
208
|
from: PublicSignKey,
|
|
135
209
|
) => Promise<boolean> | boolean;
|
|
136
210
|
|
|
211
|
+
export type CanReadIndexed<I> = (
|
|
212
|
+
result: I,
|
|
213
|
+
from: PublicSignKey,
|
|
214
|
+
) => Promise<boolean> | boolean;
|
|
215
|
+
|
|
216
|
+
type ValueTypeFromRequest<
|
|
217
|
+
Resolve extends boolean | undefined,
|
|
218
|
+
T,
|
|
219
|
+
I,
|
|
220
|
+
> = Resolve extends false ? I : T;
|
|
221
|
+
|
|
137
222
|
@variant(0)
|
|
138
223
|
export class IndexableContext implements types.Context {
|
|
139
224
|
@field({ type: "u64" })
|
|
@@ -205,14 +290,19 @@ export type OpenOptions<
|
|
|
205
290
|
documentType: AbstractType<T>;
|
|
206
291
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
207
292
|
log: SharedLog<Operation, D, any>;
|
|
208
|
-
canRead?: CanRead<
|
|
293
|
+
canRead?: CanRead<I>;
|
|
209
294
|
canSearch?: CanSearch;
|
|
210
|
-
|
|
211
|
-
request: types.SearchRequest,
|
|
212
|
-
|
|
295
|
+
replicate: (
|
|
296
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
297
|
+
results: types.Results<
|
|
298
|
+
types.ResultTypeFromRequest<
|
|
299
|
+
types.SearchRequest | types.SearchRequestIndexed
|
|
300
|
+
>
|
|
301
|
+
>,
|
|
213
302
|
) => Promise<void>;
|
|
214
303
|
indexBy?: string | string[];
|
|
215
304
|
transform?: TransformOptions<T, I>;
|
|
305
|
+
compatibility: 6 | 7 | 8 | undefined;
|
|
216
306
|
};
|
|
217
307
|
|
|
218
308
|
type IndexableClass<I> = new (
|
|
@@ -227,7 +317,7 @@ export class DocumentIndex<
|
|
|
227
317
|
D extends ReplicationDomain<any, Operation, any>,
|
|
228
318
|
> extends Program<OpenOptions<T, I, D>> {
|
|
229
319
|
@field({ type: RPC })
|
|
230
|
-
_query: RPC<types.AbstractSearchRequest, types.AbstractSearchResult
|
|
320
|
+
_query: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
231
321
|
|
|
232
322
|
// Original document representation
|
|
233
323
|
documentType: AbstractType<T>;
|
|
@@ -237,6 +327,7 @@ export class DocumentIndex<
|
|
|
237
327
|
|
|
238
328
|
// The indexed document wrapped in a context
|
|
239
329
|
wrappedIndexedType: IndexableClass<I>;
|
|
330
|
+
indexedType: AbstractType<I>;
|
|
240
331
|
|
|
241
332
|
// The database type, for recursive indexing
|
|
242
333
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
@@ -248,14 +339,16 @@ export class DocumentIndex<
|
|
|
248
339
|
index: indexerTypes.Index<IDocumentWithContext<I>>;
|
|
249
340
|
private _resumableIterators: ResumableIterators<IDocumentWithContext<I>>;
|
|
250
341
|
|
|
342
|
+
compatibility: 6 | 7 | 8 | undefined;
|
|
343
|
+
|
|
251
344
|
// Transformation, indexer
|
|
252
345
|
/* fields: IndexableFields<T, I>; */
|
|
253
346
|
|
|
254
347
|
private _valueEncoding: Encoding<T>;
|
|
255
348
|
|
|
256
|
-
private _sync: (
|
|
257
|
-
|
|
258
|
-
|
|
349
|
+
private _sync: <V extends types.ResultValue<T> | types.ResultIndexedValue<I>>(
|
|
350
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
351
|
+
results: types.Results<V>,
|
|
259
352
|
) => Promise<void>;
|
|
260
353
|
|
|
261
354
|
private _log: SharedLog<Operation, D, any>;
|
|
@@ -275,7 +368,7 @@ export class DocumentIndex<
|
|
|
275
368
|
>;
|
|
276
369
|
|
|
277
370
|
constructor(properties?: {
|
|
278
|
-
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult
|
|
371
|
+
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
279
372
|
}) {
|
|
280
373
|
super();
|
|
281
374
|
this._query = properties?.query || new RPC();
|
|
@@ -293,6 +386,8 @@ export class DocumentIndex<
|
|
|
293
386
|
!properties.transform?.type ||
|
|
294
387
|
properties.transform?.type === properties.documentType;
|
|
295
388
|
|
|
389
|
+
this.compatibility = properties.compatibility;
|
|
390
|
+
|
|
296
391
|
@variant(0)
|
|
297
392
|
class IndexedClassWithContext {
|
|
298
393
|
@field({ type: IndexableContext })
|
|
@@ -305,10 +400,8 @@ export class DocumentIndex<
|
|
|
305
400
|
}
|
|
306
401
|
|
|
307
402
|
// copy all prototype values from indexedType to IndexedClassWithContext
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
IndexedClassWithContext,
|
|
311
|
-
);
|
|
403
|
+
this.indexedType = (properties.transform?.type || properties.documentType)!;
|
|
404
|
+
copySerialization(this.indexedType, IndexedClassWithContext);
|
|
312
405
|
|
|
313
406
|
this.wrappedIndexedType = IndexedClassWithContext as new (
|
|
314
407
|
value: I,
|
|
@@ -319,7 +412,56 @@ export class DocumentIndex<
|
|
|
319
412
|
this._isProgramValues = this.documentType instanceof Program;
|
|
320
413
|
this.dbType = properties.dbType;
|
|
321
414
|
this._resultQueue = new Map();
|
|
322
|
-
this._sync =
|
|
415
|
+
this._sync = async (request, results) => {
|
|
416
|
+
/*
|
|
417
|
+
let allPromises: Promise<void> | undefined = undefined
|
|
418
|
+
if (waitForValue) {
|
|
419
|
+
let promises: Map<string, DeferredPromise<T>> = new Map();
|
|
420
|
+
|
|
421
|
+
for (const result of results) {
|
|
422
|
+
for (let i = 0; i < result.results.length; i++) {
|
|
423
|
+
let promise = defer<T>();
|
|
424
|
+
let r = result.results[i];
|
|
425
|
+
promises.set(r.context.head, promise);
|
|
426
|
+
const head = result.results[0].context.head;
|
|
427
|
+
let listeners = this.hashToValueListener.get(head);
|
|
428
|
+
if (!listeners) {
|
|
429
|
+
listeners = [];
|
|
430
|
+
this.hashToValueListener.set(head, listeners);
|
|
431
|
+
}
|
|
432
|
+
listeners.push(async (value) => {
|
|
433
|
+
promise.resolve(value);
|
|
434
|
+
result.results[i] = new types.ResultValue<T>({
|
|
435
|
+
context: r.context,
|
|
436
|
+
value,
|
|
437
|
+
source: serialize(value),
|
|
438
|
+
indexed: r.indexed,
|
|
439
|
+
}) as any;
|
|
440
|
+
});
|
|
441
|
+
promise.promise.finally(() => {
|
|
442
|
+
this.hashToValueListener.delete(head);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let timeout = setTimeout(() => {
|
|
448
|
+
for (const promise of promises!) {
|
|
449
|
+
promise[1].reject("Timed out resolving search result from value");
|
|
450
|
+
}
|
|
451
|
+
}, 1e4);
|
|
452
|
+
|
|
453
|
+
allPromises = Promise.all([...promises.values()].map((x) => x.promise)).then(
|
|
454
|
+
() => {
|
|
455
|
+
clearTimeout(timeout);
|
|
456
|
+
},
|
|
457
|
+
);
|
|
458
|
+
} */
|
|
459
|
+
|
|
460
|
+
await properties.replicate(request, results);
|
|
461
|
+
/* if (allPromises) {
|
|
462
|
+
await allPromises;
|
|
463
|
+
} */
|
|
464
|
+
};
|
|
323
465
|
|
|
324
466
|
const transformOptions = properties.transform;
|
|
325
467
|
this.transformer = transformOptions
|
|
@@ -391,7 +533,7 @@ export class DocumentIndex<
|
|
|
391
533
|
const results = await this.processQuery(
|
|
392
534
|
query as
|
|
393
535
|
| types.SearchRequest
|
|
394
|
-
| types.
|
|
536
|
+
| types.SearchRequestIndexed
|
|
395
537
|
| types.CollectNextRequest,
|
|
396
538
|
ctx.from,
|
|
397
539
|
false,
|
|
@@ -440,10 +582,20 @@ export class DocumentIndex<
|
|
|
440
582
|
return dropped;
|
|
441
583
|
}
|
|
442
584
|
|
|
443
|
-
public async get(
|
|
585
|
+
public async get<Options extends QueryOptions<T, D, true | undefined>>(
|
|
444
586
|
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
445
|
-
options?:
|
|
446
|
-
): Promise<T
|
|
587
|
+
options?: Options,
|
|
588
|
+
): Promise<T>;
|
|
589
|
+
|
|
590
|
+
public async get<Options extends QueryOptions<T, D, false>>(
|
|
591
|
+
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
592
|
+
options?: Options,
|
|
593
|
+
): Promise<I>;
|
|
594
|
+
|
|
595
|
+
public async get<
|
|
596
|
+
Options extends QueryOptions<T, D, Resolve>,
|
|
597
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
598
|
+
>(key: indexerTypes.Ideable | indexerTypes.IdKey, options?: Options) {
|
|
447
599
|
return (
|
|
448
600
|
await this.getDetailed(
|
|
449
601
|
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key),
|
|
@@ -497,14 +649,26 @@ export class DocumentIndex<
|
|
|
497
649
|
});
|
|
498
650
|
}
|
|
499
651
|
|
|
500
|
-
public async getDetailed
|
|
652
|
+
public async getDetailed<
|
|
653
|
+
Options extends QueryOptions<T, D, Resolve>,
|
|
654
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
655
|
+
RT extends types.Result = Resolve extends true
|
|
656
|
+
? types.ResultValue<T>
|
|
657
|
+
: types.ResultIndexedValue<I>,
|
|
658
|
+
>(
|
|
501
659
|
key: indexerTypes.IdKey | indexerTypes.IdPrimitive,
|
|
502
|
-
options?: QueryOptions<T, D>,
|
|
503
|
-
): Promise<types.Results<
|
|
504
|
-
let results:
|
|
660
|
+
options?: QueryOptions<T, D, Resolve>,
|
|
661
|
+
): Promise<types.Results<RT>[] | undefined> {
|
|
662
|
+
let results:
|
|
663
|
+
| types.Results<types.ResultValue<T> | types.ResultIndexedValue<I>>[]
|
|
664
|
+
| undefined;
|
|
665
|
+
const resolve = options?.resolve || options?.resolve == null;
|
|
666
|
+
let requestClazz = resolve
|
|
667
|
+
? types.SearchRequest
|
|
668
|
+
: types.SearchRequestIndexed;
|
|
505
669
|
if (key instanceof Uint8Array) {
|
|
506
|
-
results = await this.
|
|
507
|
-
new
|
|
670
|
+
results = await this.queryCommence(
|
|
671
|
+
new requestClazz({
|
|
508
672
|
query: [
|
|
509
673
|
new indexerTypes.ByteMatchQuery({ key: this.indexBy, value: key }),
|
|
510
674
|
],
|
|
@@ -518,8 +682,8 @@ export class DocumentIndex<
|
|
|
518
682
|
typeof indexableKey === "number" ||
|
|
519
683
|
typeof indexableKey === "bigint"
|
|
520
684
|
) {
|
|
521
|
-
results = await this.
|
|
522
|
-
new
|
|
685
|
+
results = await this.queryCommence(
|
|
686
|
+
new requestClazz({
|
|
523
687
|
query: [
|
|
524
688
|
new indexerTypes.IntegerCompare({
|
|
525
689
|
key: this.indexBy,
|
|
@@ -531,8 +695,8 @@ export class DocumentIndex<
|
|
|
531
695
|
options,
|
|
532
696
|
);
|
|
533
697
|
} else if (typeof indexableKey === "string") {
|
|
534
|
-
results = await this.
|
|
535
|
-
new
|
|
698
|
+
results = await this.queryCommence(
|
|
699
|
+
new requestClazz({
|
|
536
700
|
query: [
|
|
537
701
|
new indexerTypes.StringMatch({
|
|
538
702
|
key: this.indexBy,
|
|
@@ -543,8 +707,8 @@ export class DocumentIndex<
|
|
|
543
707
|
options,
|
|
544
708
|
);
|
|
545
709
|
} else if (indexableKey instanceof Uint8Array) {
|
|
546
|
-
results = await this.
|
|
547
|
-
new
|
|
710
|
+
results = await this.queryCommence(
|
|
711
|
+
new requestClazz({
|
|
548
712
|
query: [
|
|
549
713
|
new indexerTypes.ByteMatchQuery({
|
|
550
714
|
key: this.indexBy,
|
|
@@ -557,19 +721,56 @@ export class DocumentIndex<
|
|
|
557
721
|
}
|
|
558
722
|
}
|
|
559
723
|
|
|
560
|
-
|
|
724
|
+
if (
|
|
725
|
+
resolve &&
|
|
726
|
+
requestClazz === types.SearchRequestIndexed &&
|
|
727
|
+
!this.indexedTypeIsDocumentType &&
|
|
728
|
+
results
|
|
729
|
+
) {
|
|
730
|
+
for (const set of results) {
|
|
731
|
+
let coercedResult: types.ResultValue<T>[] = [];
|
|
732
|
+
|
|
733
|
+
for (const value of set.results) {
|
|
734
|
+
const resolved =
|
|
735
|
+
value instanceof types.ResultIndexedValue
|
|
736
|
+
? (
|
|
737
|
+
await this.resolveDocument({
|
|
738
|
+
indexed: value.value,
|
|
739
|
+
head: value.context.head,
|
|
740
|
+
})
|
|
741
|
+
)?.value
|
|
742
|
+
: value.value;
|
|
743
|
+
if (resolved) {
|
|
744
|
+
coercedResult.push(
|
|
745
|
+
new types.ResultValue({
|
|
746
|
+
context: value.context,
|
|
747
|
+
value: resolved,
|
|
748
|
+
}),
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
set.results = coercedResult;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return results as any as types.Results<RT>[];
|
|
561
757
|
}
|
|
562
758
|
|
|
563
759
|
getSize(): Promise<number> | number {
|
|
564
760
|
return this.index.getSize();
|
|
565
761
|
}
|
|
566
762
|
|
|
567
|
-
private async resolveDocument(
|
|
568
|
-
|
|
569
|
-
|
|
763
|
+
private async resolveDocument(value: {
|
|
764
|
+
id?: indexerTypes.IdPrimitive;
|
|
765
|
+
indexed: I;
|
|
766
|
+
head: string;
|
|
767
|
+
}): Promise<{ value: T } | undefined> {
|
|
768
|
+
const id =
|
|
769
|
+
value.id ??
|
|
770
|
+
indexerTypes.toId(this.indexByResolver(value.indexed)).primitive;
|
|
771
|
+
|
|
570
772
|
const cached =
|
|
571
|
-
this._resolverCache.get(
|
|
572
|
-
this._resolverProgramCache?.get(value.id.primitive);
|
|
773
|
+
this._resolverCache.get(id) || this._resolverProgramCache?.get(id);
|
|
573
774
|
if (cached != null) {
|
|
574
775
|
return { value: cached };
|
|
575
776
|
}
|
|
@@ -578,13 +779,13 @@ export class DocumentIndex<
|
|
|
578
779
|
// cast value to T, i.e. convert the class but keep all properties except the __context
|
|
579
780
|
const obj = Object.assign(
|
|
580
781
|
Object.create(this.documentType.prototype),
|
|
581
|
-
value.
|
|
782
|
+
value.indexed,
|
|
582
783
|
);
|
|
583
784
|
delete obj.__context;
|
|
584
785
|
return { value: obj as T };
|
|
585
786
|
}
|
|
586
787
|
|
|
587
|
-
const head = await this._log.log.get(value.
|
|
788
|
+
const head = await this._log.log.get(value.head);
|
|
588
789
|
if (!head) {
|
|
589
790
|
return undefined; // we could end up here if we recently pruned the document and other peers never persisted the entry
|
|
590
791
|
// TODO update changes in index before removing entries from log entry storage
|
|
@@ -603,14 +804,19 @@ export class DocumentIndex<
|
|
|
603
804
|
);
|
|
604
805
|
}
|
|
605
806
|
|
|
606
|
-
async processQuery
|
|
607
|
-
|
|
807
|
+
async processQuery<
|
|
808
|
+
R extends
|
|
809
|
+
| types.SearchRequest
|
|
810
|
+
| types.SearchRequestIndexed
|
|
811
|
+
| types.CollectNextRequest,
|
|
812
|
+
>(
|
|
813
|
+
query: R,
|
|
608
814
|
from: PublicSignKey,
|
|
609
815
|
isLocal: boolean,
|
|
610
816
|
options?: {
|
|
611
|
-
canRead?: CanRead<
|
|
817
|
+
canRead?: CanRead<I>;
|
|
612
818
|
},
|
|
613
|
-
): Promise<types.Results<
|
|
819
|
+
): Promise<types.Results<types.ResultTypeFromRequest<R>>> {
|
|
614
820
|
// We do special case for querying the id as we can do it faster than iterating
|
|
615
821
|
|
|
616
822
|
let prevQueued = isLocal
|
|
@@ -623,9 +829,16 @@ export class DocumentIndex<
|
|
|
623
829
|
let indexedResult:
|
|
624
830
|
| indexerTypes.IndexedResults<IDocumentWithContext<I>>
|
|
625
831
|
| undefined = undefined;
|
|
626
|
-
|
|
832
|
+
|
|
833
|
+
let fromQuery: types.SearchRequest | types.SearchRequestIndexed | undefined;
|
|
834
|
+
if (
|
|
835
|
+
query instanceof types.SearchRequest ||
|
|
836
|
+
query instanceof types.SearchRequestIndexed
|
|
837
|
+
) {
|
|
838
|
+
fromQuery = query;
|
|
627
839
|
indexedResult = await this._resumableIterators.iterateAndFetch(query);
|
|
628
840
|
} else if (query instanceof types.CollectNextRequest) {
|
|
841
|
+
fromQuery = this._resumableIterators.queues.get(query.idString)?.request;
|
|
629
842
|
indexedResult =
|
|
630
843
|
prevQueued?.keptInIndex === 0
|
|
631
844
|
? []
|
|
@@ -633,7 +846,7 @@ export class DocumentIndex<
|
|
|
633
846
|
} else {
|
|
634
847
|
throw new Error("Unsupported");
|
|
635
848
|
}
|
|
636
|
-
|
|
849
|
+
|
|
637
850
|
let resultSize = 0;
|
|
638
851
|
|
|
639
852
|
let toIterate = prevQueued
|
|
@@ -660,6 +873,8 @@ export class DocumentIndex<
|
|
|
660
873
|
this._resultQueue.set(query.idString, prevQueued);
|
|
661
874
|
}
|
|
662
875
|
|
|
876
|
+
const filteredResults: types.Result[] = [];
|
|
877
|
+
|
|
663
878
|
for (const result of toIterate) {
|
|
664
879
|
if (!isLocal) {
|
|
665
880
|
resultSize += result.value.__context.size;
|
|
@@ -668,25 +883,55 @@ export class DocumentIndex<
|
|
|
668
883
|
continue;
|
|
669
884
|
}
|
|
670
885
|
}
|
|
886
|
+
const indexedUnwrapped = Object.assign(
|
|
887
|
+
Object.create(this.indexedType.prototype),
|
|
888
|
+
result.value,
|
|
889
|
+
);
|
|
671
890
|
|
|
672
|
-
const value = await this.resolveDocument(result);
|
|
673
891
|
if (
|
|
674
|
-
|
|
675
|
-
|
|
892
|
+
options?.canRead &&
|
|
893
|
+
!(await options.canRead(indexedUnwrapped, from))
|
|
676
894
|
) {
|
|
677
895
|
continue;
|
|
678
896
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
new types.ResultWithSource({
|
|
682
|
-
context: result.value.__context.toContext(),
|
|
683
|
-
value: value.value,
|
|
684
|
-
source: serialize(value.value),
|
|
897
|
+
if (fromQuery instanceof types.SearchRequest) {
|
|
898
|
+
const value = await this.resolveDocument({
|
|
685
899
|
indexed: result.value,
|
|
686
|
-
|
|
687
|
-
|
|
900
|
+
head: result.value.__context.head,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
if (!value) {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
filteredResults.push(
|
|
908
|
+
new types.ResultValue({
|
|
909
|
+
context: result.value.__context.toContext(),
|
|
910
|
+
value: value.value,
|
|
911
|
+
source: serialize(value.value),
|
|
912
|
+
indexed: indexedUnwrapped,
|
|
913
|
+
}),
|
|
914
|
+
);
|
|
915
|
+
} else if (fromQuery instanceof types.SearchRequestIndexed) {
|
|
916
|
+
const context = result.value.__context.toContext();
|
|
917
|
+
const head = await this._log.log.get(context.head);
|
|
918
|
+
// assume remote peer will start to replicate (TODO is this ideal?)
|
|
919
|
+
if (fromQuery.replicate) {
|
|
920
|
+
this._log.addPeersToGidPeerHistory(context.gid, [from.hashcode()]);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
filteredResults.push(
|
|
924
|
+
new types.ResultIndexedValue({
|
|
925
|
+
context,
|
|
926
|
+
source: serialize(indexedUnwrapped),
|
|
927
|
+
indexed: indexedUnwrapped,
|
|
928
|
+
entries: head ? [head] : [],
|
|
929
|
+
}),
|
|
930
|
+
);
|
|
931
|
+
}
|
|
688
932
|
}
|
|
689
|
-
|
|
933
|
+
|
|
934
|
+
const results: types.Results<any> = new types.Results<any>({
|
|
690
935
|
results: filteredResults,
|
|
691
936
|
kept: BigInt(kept + (prevQueued?.queue.length || 0)),
|
|
692
937
|
});
|
|
@@ -701,6 +946,7 @@ export class DocumentIndex<
|
|
|
701
946
|
private clearResultsQueue(
|
|
702
947
|
query:
|
|
703
948
|
| types.SearchRequest
|
|
949
|
+
| types.SearchRequestIndexed
|
|
704
950
|
| types.CollectNextRequest
|
|
705
951
|
| types.CloseIteratorRequest,
|
|
706
952
|
) {
|
|
@@ -738,14 +984,18 @@ export class DocumentIndex<
|
|
|
738
984
|
* @param options
|
|
739
985
|
* @returns
|
|
740
986
|
*/
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
987
|
+
private async queryCommence<
|
|
988
|
+
R extends types.SearchRequest | types.SearchRequestIndexed,
|
|
989
|
+
RT extends types.Result = R extends types.SearchRequest
|
|
990
|
+
? types.ResultValue<T>
|
|
991
|
+
: types.ResultIndexedValue<I>,
|
|
992
|
+
>(
|
|
993
|
+
queryRequest: R,
|
|
994
|
+
options?: QueryDetailedOptions<T, D, boolean | undefined>,
|
|
995
|
+
): Promise<types.Results<RT>[]> {
|
|
745
996
|
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
746
|
-
let remote:
|
|
747
|
-
|
|
748
|
-
| undefined = undefined;
|
|
997
|
+
let remote: RemoteQueryOptions<types.AbstractSearchResult, D> | undefined =
|
|
998
|
+
undefined;
|
|
749
999
|
if (typeof options?.remote === "boolean") {
|
|
750
1000
|
if (options?.remote) {
|
|
751
1001
|
remote = {};
|
|
@@ -767,7 +1017,7 @@ export class DocumentIndex<
|
|
|
767
1017
|
"Expecting either 'options.remote' or 'options.local' to be true",
|
|
768
1018
|
);
|
|
769
1019
|
}
|
|
770
|
-
const allResults: types.Results<
|
|
1020
|
+
const allResults: types.Results<types.ResultTypeFromRequest<R>>[] = [];
|
|
771
1021
|
|
|
772
1022
|
if (local) {
|
|
773
1023
|
const results = await this.processQuery(
|
|
@@ -782,7 +1032,7 @@ export class DocumentIndex<
|
|
|
782
1032
|
}
|
|
783
1033
|
}
|
|
784
1034
|
|
|
785
|
-
let resolved: types.Results<
|
|
1035
|
+
let resolved: types.Results<types.ResultTypeFromRequest<R>>[] = [];
|
|
786
1036
|
if (remote) {
|
|
787
1037
|
const replicatorGroups = await this._log.getCover(
|
|
788
1038
|
remote.domain ?? (undefined as any),
|
|
@@ -795,15 +1045,17 @@ export class DocumentIndex<
|
|
|
795
1045
|
if (replicatorGroups) {
|
|
796
1046
|
const groupHashes: string[][] = replicatorGroups.map((x) => [x]);
|
|
797
1047
|
const responseHandler = async (
|
|
798
|
-
results: RPCResponse<types.AbstractSearchResult
|
|
1048
|
+
results: RPCResponse<types.AbstractSearchResult>[],
|
|
799
1049
|
) => {
|
|
800
|
-
|
|
1050
|
+
const resultInitialized = await introduceEntries(
|
|
801
1051
|
queryRequest,
|
|
802
1052
|
results,
|
|
803
1053
|
this.documentType,
|
|
1054
|
+
this.indexedType,
|
|
804
1055
|
this._sync,
|
|
805
1056
|
options,
|
|
806
|
-
)
|
|
1057
|
+
);
|
|
1058
|
+
for (const r of resultInitialized) {
|
|
807
1059
|
resolved.push(r.response);
|
|
808
1060
|
}
|
|
809
1061
|
};
|
|
@@ -851,58 +1103,129 @@ export class DocumentIndex<
|
|
|
851
1103
|
}
|
|
852
1104
|
}
|
|
853
1105
|
}
|
|
854
|
-
return allResults;
|
|
1106
|
+
return allResults as any; // TODO types
|
|
855
1107
|
}
|
|
856
1108
|
|
|
1109
|
+
public search(
|
|
1110
|
+
queryRequest: QueryLike,
|
|
1111
|
+
options?: SearchOptions<T, D, true>,
|
|
1112
|
+
): Promise<ValueTypeFromRequest<true, T, I>[]>;
|
|
1113
|
+
public search(
|
|
1114
|
+
queryRequest: QueryLike,
|
|
1115
|
+
options?: SearchOptions<T, D, false>,
|
|
1116
|
+
): Promise<ValueTypeFromRequest<false, T, I>[]>;
|
|
1117
|
+
|
|
857
1118
|
/**
|
|
858
1119
|
* Query and retrieve results
|
|
859
1120
|
* @param queryRequest
|
|
860
1121
|
* @param options
|
|
861
1122
|
* @returns
|
|
862
1123
|
*/
|
|
863
|
-
public async search
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1124
|
+
public async search<
|
|
1125
|
+
R extends types.SearchRequest | types.SearchRequestIndexed | QueryLike,
|
|
1126
|
+
O extends SearchOptions<T, D, Resolve>,
|
|
1127
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<O>,
|
|
1128
|
+
>(
|
|
1129
|
+
queryRequest: R,
|
|
1130
|
+
options?: SearchOptions<T, D, Resolve>,
|
|
1131
|
+
): Promise<ValueTypeFromRequest<Resolve, T, I>[]> {
|
|
867
1132
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
868
|
-
|
|
1133
|
+
const coercedRequest: types.SearchRequest | types.SearchRequestIndexed =
|
|
1134
|
+
coerceQuery(queryRequest, options);
|
|
1135
|
+
coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
|
|
869
1136
|
|
|
870
1137
|
// So that the iterator is pre-fetching the right amount of entries
|
|
871
|
-
const iterator = this.iterate(
|
|
1138
|
+
const iterator = this.iterate(coercedRequest, options);
|
|
872
1139
|
|
|
873
1140
|
// So that this call will not do any remote requests
|
|
874
|
-
const allResults: T[] = [];
|
|
875
|
-
|
|
1141
|
+
const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
1142
|
+
|
|
1143
|
+
while (
|
|
1144
|
+
iterator.done() !== true &&
|
|
1145
|
+
coercedRequest.fetch > allResults.length
|
|
1146
|
+
) {
|
|
876
1147
|
// We might need to pull .next multiple time due to data message size limitations
|
|
1148
|
+
|
|
877
1149
|
for (const result of await iterator.next(
|
|
878
|
-
|
|
1150
|
+
coercedRequest.fetch - allResults.length,
|
|
879
1151
|
)) {
|
|
880
|
-
allResults.push(result);
|
|
1152
|
+
allResults.push(result as ValueTypeFromRequest<Resolve, T, I>);
|
|
881
1153
|
}
|
|
882
1154
|
}
|
|
883
1155
|
|
|
884
1156
|
await iterator.close();
|
|
885
1157
|
|
|
886
|
-
//
|
|
1158
|
+
// Deduplicate and return values directly
|
|
887
1159
|
return dedup(allResults, this.indexByResolver);
|
|
888
1160
|
}
|
|
889
1161
|
|
|
1162
|
+
public iterate(
|
|
1163
|
+
query: QueryLike,
|
|
1164
|
+
options?: QueryOptions<T, D, undefined>,
|
|
1165
|
+
): ResultsIterator<ValueTypeFromRequest<true, T, I>>;
|
|
1166
|
+
public iterate<Resolve extends boolean>(
|
|
1167
|
+
query: QueryLike,
|
|
1168
|
+
options?: QueryOptions<T, D, Resolve>,
|
|
1169
|
+
): ResultsIterator<ValueTypeFromRequest<Resolve, T, I>>;
|
|
1170
|
+
|
|
890
1171
|
/**
|
|
891
1172
|
* Query and retrieve documents in a iterator
|
|
892
1173
|
* @param queryRequest
|
|
893
1174
|
* @param options
|
|
894
1175
|
* @returns
|
|
895
1176
|
*/
|
|
896
|
-
public iterate
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1177
|
+
public iterate<
|
|
1178
|
+
R extends types.SearchRequest | types.SearchRequestIndexed | QueryLike,
|
|
1179
|
+
O extends SearchOptions<T, D, Resolve>,
|
|
1180
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<O>,
|
|
1181
|
+
>(
|
|
1182
|
+
queryRequest: R,
|
|
1183
|
+
options?: QueryOptions<T, D, Resolve>,
|
|
1184
|
+
): ResultsIterator<ValueTypeFromRequest<Resolve, T, I>> {
|
|
1185
|
+
let queryRequestCoerced: types.SearchRequest | types.SearchRequestIndexed =
|
|
1186
|
+
coerceQuery(queryRequest, options);
|
|
1187
|
+
|
|
1188
|
+
let resolve = false;
|
|
1189
|
+
if (
|
|
1190
|
+
options?.remote &&
|
|
1191
|
+
typeof options.remote !== "boolean" &&
|
|
1192
|
+
options.remote.replicate &&
|
|
1193
|
+
options?.resolve !== false
|
|
1194
|
+
) {
|
|
1195
|
+
if (
|
|
1196
|
+
(queryRequest instanceof types.SearchRequestIndexed === false &&
|
|
1197
|
+
this.compatibility == null) ||
|
|
1198
|
+
(this.compatibility != null && this.compatibility > 8)
|
|
1199
|
+
) {
|
|
1200
|
+
queryRequestCoerced = new types.SearchRequestIndexed({
|
|
1201
|
+
query: queryRequestCoerced.query,
|
|
1202
|
+
fetch: queryRequestCoerced.fetch,
|
|
1203
|
+
sort: queryRequestCoerced.sort,
|
|
1204
|
+
});
|
|
1205
|
+
resolve = true;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
let replicate =
|
|
1210
|
+
options?.remote &&
|
|
1211
|
+
typeof options.remote !== "boolean" &&
|
|
1212
|
+
options.remote.replicate;
|
|
1213
|
+
if (
|
|
1214
|
+
replicate &&
|
|
1215
|
+
queryRequestCoerced instanceof types.SearchRequestIndexed
|
|
1216
|
+
) {
|
|
1217
|
+
queryRequestCoerced.replicate = true;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
900
1220
|
let fetchPromise: Promise<any> | undefined = undefined;
|
|
901
1221
|
const peerBufferMap: Map<
|
|
902
1222
|
string,
|
|
903
1223
|
{
|
|
904
1224
|
kept: number;
|
|
905
|
-
buffer: BufferedResult<
|
|
1225
|
+
buffer: BufferedResult<
|
|
1226
|
+
types.ResultValue<T> | types.ResultIndexedValue<I>,
|
|
1227
|
+
I
|
|
1228
|
+
>[];
|
|
906
1229
|
}
|
|
907
1230
|
> = new Map();
|
|
908
1231
|
const visited = new Set<string | number | bigint>();
|
|
@@ -914,8 +1237,8 @@ export class DocumentIndex<
|
|
|
914
1237
|
const controller = new AbortController();
|
|
915
1238
|
|
|
916
1239
|
const peerBuffers = (): {
|
|
917
|
-
indexed:
|
|
918
|
-
value: T
|
|
1240
|
+
indexed: I;
|
|
1241
|
+
value: types.ResultValue<T> | types.ResultIndexedValue<I>;
|
|
919
1242
|
from: PublicSignKey;
|
|
920
1243
|
context: types.Context;
|
|
921
1244
|
}[] => {
|
|
@@ -924,8 +1247,8 @@ export class DocumentIndex<
|
|
|
924
1247
|
|
|
925
1248
|
const fetchFirst = async (n: number): Promise<boolean> => {
|
|
926
1249
|
done = true; // Assume we are donne
|
|
927
|
-
|
|
928
|
-
await this.
|
|
1250
|
+
queryRequestCoerced.fetch = n;
|
|
1251
|
+
await this.queryCommence(queryRequestCoerced, {
|
|
929
1252
|
...options,
|
|
930
1253
|
onResponse: async (response, from) => {
|
|
931
1254
|
if (!from) {
|
|
@@ -936,7 +1259,9 @@ export class DocumentIndex<
|
|
|
936
1259
|
logger.error("Dont have access");
|
|
937
1260
|
return;
|
|
938
1261
|
} else if (response instanceof types.Results) {
|
|
939
|
-
const results = response as types.Results<
|
|
1262
|
+
const results = response as types.Results<
|
|
1263
|
+
types.ResultTypeFromRequest<R>
|
|
1264
|
+
>;
|
|
940
1265
|
if (results.kept === 0n && results.results.length === 0) {
|
|
941
1266
|
return;
|
|
942
1267
|
}
|
|
@@ -944,24 +1269,42 @@ export class DocumentIndex<
|
|
|
944
1269
|
if (results.kept > 0n) {
|
|
945
1270
|
done = false; // we have more to do later!
|
|
946
1271
|
}
|
|
947
|
-
const buffer: BufferedResult<
|
|
1272
|
+
const buffer: BufferedResult<types.ResultTypeFromRequest<R>, I>[] =
|
|
1273
|
+
[];
|
|
948
1274
|
|
|
949
1275
|
for (const result of results.results) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1276
|
+
if (result instanceof types.ResultValue) {
|
|
1277
|
+
const indexKey = indexerTypes.toId(
|
|
1278
|
+
this.indexByResolver(result.value),
|
|
1279
|
+
).primitive;
|
|
1280
|
+
if (visited.has(indexKey)) {
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
visited.add(indexKey);
|
|
1284
|
+
buffer.push({
|
|
1285
|
+
value: result.value,
|
|
1286
|
+
context: result.context,
|
|
1287
|
+
from,
|
|
1288
|
+
indexed:
|
|
1289
|
+
(result.indexed as I) ||
|
|
1290
|
+
(await this.transformer(result.value, result.context)),
|
|
1291
|
+
});
|
|
1292
|
+
} else {
|
|
1293
|
+
const indexKey = indexerTypes.toId(
|
|
1294
|
+
this.indexByResolver(result.value),
|
|
1295
|
+
).primitive;
|
|
1296
|
+
|
|
1297
|
+
if (visited.has(indexKey)) {
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
visited.add(indexKey);
|
|
1301
|
+
buffer.push({
|
|
1302
|
+
value: result.value,
|
|
1303
|
+
context: result.context,
|
|
1304
|
+
from,
|
|
1305
|
+
indexed: result.indexed || result.value,
|
|
1306
|
+
});
|
|
955
1307
|
}
|
|
956
|
-
visited.add(indexKey);
|
|
957
|
-
buffer.push({
|
|
958
|
-
value: result.value,
|
|
959
|
-
context: result.context,
|
|
960
|
-
from,
|
|
961
|
-
indexed:
|
|
962
|
-
result.indexed ||
|
|
963
|
-
(await this.transformer(result.value, result.context)),
|
|
964
|
-
});
|
|
965
1308
|
}
|
|
966
1309
|
|
|
967
1310
|
peerBufferMap.set(from.hashcode(), {
|
|
@@ -977,7 +1320,7 @@ export class DocumentIndex<
|
|
|
977
1320
|
});
|
|
978
1321
|
|
|
979
1322
|
if (done) {
|
|
980
|
-
this.clearResultsQueue(
|
|
1323
|
+
this.clearResultsQueue(queryRequestCoerced);
|
|
981
1324
|
}
|
|
982
1325
|
|
|
983
1326
|
return done;
|
|
@@ -1015,7 +1358,7 @@ export class DocumentIndex<
|
|
|
1015
1358
|
// TODO buffer more than deleted?
|
|
1016
1359
|
// TODO batch to multiple 'to's
|
|
1017
1360
|
const collectRequest = new types.CollectNextRequest({
|
|
1018
|
-
id:
|
|
1361
|
+
id: queryRequestCoerced.id,
|
|
1019
1362
|
amount: n - buffer.buffer.length,
|
|
1020
1363
|
});
|
|
1021
1364
|
// Fetch locally?
|
|
@@ -1086,57 +1429,54 @@ export class DocumentIndex<
|
|
|
1086
1429
|
})
|
|
1087
1430
|
.then((response) =>
|
|
1088
1431
|
introduceEntries(
|
|
1089
|
-
|
|
1432
|
+
queryRequestCoerced,
|
|
1090
1433
|
response,
|
|
1091
1434
|
this.documentType,
|
|
1435
|
+
this.indexedType,
|
|
1092
1436
|
this._sync,
|
|
1093
1437
|
options,
|
|
1094
1438
|
)
|
|
1095
|
-
.then((responses) => {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
if (response.response.results.length === 0) {
|
|
1104
|
-
if (peerBufferMap.get(peer)?.buffer.length === 0) {
|
|
1105
|
-
peerBufferMap.delete(peer); // No more results
|
|
1106
|
-
}
|
|
1107
|
-
} else {
|
|
1108
|
-
const peerBuffer = peerBufferMap.get(peer);
|
|
1109
|
-
if (!peerBuffer) {
|
|
1439
|
+
.then(async (responses) => {
|
|
1440
|
+
return Promise.all(
|
|
1441
|
+
responses.map(async (response, i) => {
|
|
1442
|
+
resultsLeft += Number(response.response.kept);
|
|
1443
|
+
const from = responses[i].from;
|
|
1444
|
+
if (!from) {
|
|
1445
|
+
logger.error("Missing from for sorted query");
|
|
1110
1446
|
return;
|
|
1111
1447
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
if (
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
continue;
|
|
1448
|
+
|
|
1449
|
+
if (response.response.results.length === 0) {
|
|
1450
|
+
if (peerBufferMap.get(peer)?.buffer.length === 0) {
|
|
1451
|
+
peerBufferMap.delete(peer); // No more results
|
|
1452
|
+
}
|
|
1453
|
+
} else {
|
|
1454
|
+
const peerBuffer = peerBufferMap.get(peer);
|
|
1455
|
+
if (!peerBuffer) {
|
|
1456
|
+
return;
|
|
1122
1457
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1458
|
+
peerBuffer.kept = Number(response.response.kept);
|
|
1459
|
+
for (const result of response.response.results) {
|
|
1460
|
+
const idPrimitive = indexerTypes.toId(
|
|
1125
1461
|
this.indexByResolver(result.value),
|
|
1126
|
-
).primitive
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
result.
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1462
|
+
).primitive;
|
|
1463
|
+
if (visited.has(idPrimitive)) {
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
visited.add(idPrimitive);
|
|
1467
|
+
peerBuffer.buffer.push({
|
|
1468
|
+
value: result.value,
|
|
1469
|
+
context: result.context,
|
|
1470
|
+
from: from!,
|
|
1471
|
+
indexed: await this.transformer(
|
|
1472
|
+
result.value,
|
|
1473
|
+
result.context,
|
|
1474
|
+
),
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1137
1477
|
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1478
|
+
}),
|
|
1479
|
+
);
|
|
1140
1480
|
})
|
|
1141
1481
|
.catch((e) => {
|
|
1142
1482
|
logger.error(
|
|
@@ -1177,7 +1517,7 @@ export class DocumentIndex<
|
|
|
1177
1517
|
indexerTypes.extractSortCompare(
|
|
1178
1518
|
a.indexed,
|
|
1179
1519
|
b.indexed,
|
|
1180
|
-
|
|
1520
|
+
queryRequestCoerced.sort,
|
|
1181
1521
|
),
|
|
1182
1522
|
);
|
|
1183
1523
|
|
|
@@ -1198,18 +1538,38 @@ export class DocumentIndex<
|
|
|
1198
1538
|
}
|
|
1199
1539
|
|
|
1200
1540
|
done = fetchedAll && !pendingMoreResults;
|
|
1541
|
+
let coercedBatch: ValueTypeFromRequest<Resolve, T, I>[];
|
|
1542
|
+
if (resolve) {
|
|
1543
|
+
coercedBatch = (
|
|
1544
|
+
await Promise.all(
|
|
1545
|
+
batch.map(async (x) =>
|
|
1546
|
+
x.value instanceof this.documentType
|
|
1547
|
+
? x.value
|
|
1548
|
+
: (
|
|
1549
|
+
await this.resolveDocument({
|
|
1550
|
+
head: x.context.head,
|
|
1551
|
+
indexed: x.indexed,
|
|
1552
|
+
})
|
|
1553
|
+
)?.value,
|
|
1554
|
+
),
|
|
1555
|
+
)
|
|
1556
|
+
).filter((x) => !!x) as ValueTypeFromRequest<Resolve, T, I>[];
|
|
1557
|
+
} else {
|
|
1558
|
+
coercedBatch = batch.map((x) => x.value) as ValueTypeFromRequest<
|
|
1559
|
+
Resolve,
|
|
1560
|
+
T,
|
|
1561
|
+
I
|
|
1562
|
+
>[];
|
|
1563
|
+
}
|
|
1201
1564
|
|
|
1202
|
-
return dedup(
|
|
1203
|
-
batch.map((x) => x.value),
|
|
1204
|
-
this.indexByResolver,
|
|
1205
|
-
);
|
|
1565
|
+
return dedup(coercedBatch, this.indexByResolver);
|
|
1206
1566
|
};
|
|
1207
1567
|
|
|
1208
1568
|
const close = async () => {
|
|
1209
1569
|
controller.abort(new AbortError("Iterator closed"));
|
|
1210
1570
|
|
|
1211
1571
|
const closeRequest = new types.CloseIteratorRequest({
|
|
1212
|
-
id:
|
|
1572
|
+
id: queryRequestCoerced.id,
|
|
1213
1573
|
});
|
|
1214
1574
|
const promises: Promise<any>[] = [];
|
|
1215
1575
|
for (const [peer, buffer] of peerBufferMap) {
|
|
@@ -1243,7 +1603,7 @@ export class DocumentIndex<
|
|
|
1243
1603
|
next,
|
|
1244
1604
|
done: doneFn,
|
|
1245
1605
|
all: async () => {
|
|
1246
|
-
let result: T[] = [];
|
|
1606
|
+
let result: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
1247
1607
|
while (doneFn() !== true) {
|
|
1248
1608
|
let batch = await next(100);
|
|
1249
1609
|
result.push(...batch);
|
|
@@ -1252,4 +1612,34 @@ export class DocumentIndex<
|
|
|
1252
1612
|
},
|
|
1253
1613
|
};
|
|
1254
1614
|
}
|
|
1615
|
+
|
|
1616
|
+
public async waitFor(
|
|
1617
|
+
other:
|
|
1618
|
+
| PublicSignKey
|
|
1619
|
+
| PeerId
|
|
1620
|
+
| string
|
|
1621
|
+
| (PublicSignKey | string | PeerId)[],
|
|
1622
|
+
options?: { signal?: AbortSignal; timeout?: number },
|
|
1623
|
+
): Promise<void> {
|
|
1624
|
+
await super.waitFor(other, options);
|
|
1625
|
+
const ids = Array.isArray(other) ? other : [other];
|
|
1626
|
+
const expectedHashes = new Set(
|
|
1627
|
+
ids.map((x) =>
|
|
1628
|
+
typeof x === "string"
|
|
1629
|
+
? x
|
|
1630
|
+
: x instanceof PublicSignKey
|
|
1631
|
+
? x.hashcode()
|
|
1632
|
+
: getPublicKeyFromPeerId(x).hashcode(),
|
|
1633
|
+
),
|
|
1634
|
+
);
|
|
1635
|
+
|
|
1636
|
+
for (const key of expectedHashes) {
|
|
1637
|
+
await waitFor(
|
|
1638
|
+
async () =>
|
|
1639
|
+
(await this._log.replicationIndex.count({ query: { hash: key } })) >
|
|
1640
|
+
0,
|
|
1641
|
+
options,
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1255
1645
|
}
|