@peerbit/document 8.2.0 → 9.0.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/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 -2
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +15 -6
- 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 -31
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +262 -80
- package/dist/src/search.js.map +1 -1
- package/package.json +8 -8
- package/src/domain.ts +12 -3
- package/src/program.ts +19 -13
- package/src/resumable-iterator.ts +10 -3
- package/src/search.ts +563 -206
package/src/search.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type AbstractType, field, serialize, variant } from "@dao-xyz/borsh";
|
|
2
|
-
import {
|
|
2
|
+
import type { PeerId } from "@libp2p/interface";
|
|
3
3
|
import { Cache } from "@peerbit/cache";
|
|
4
4
|
import {
|
|
5
5
|
type MaybePromise,
|
|
6
6
|
PublicSignKey,
|
|
7
|
+
getPublicKeyFromPeerId,
|
|
7
8
|
sha256Base64Sync,
|
|
8
9
|
} from "@peerbit/crypto";
|
|
9
10
|
import * as types from "@peerbit/document-interface";
|
|
@@ -19,13 +20,9 @@ import {
|
|
|
19
20
|
type RPCResponse,
|
|
20
21
|
queryAll,
|
|
21
22
|
} from "@peerbit/rpc";
|
|
22
|
-
import {
|
|
23
|
-
BlocksMessage,
|
|
24
|
-
type ReplicationDomain,
|
|
25
|
-
SharedLog,
|
|
26
|
-
} from "@peerbit/shared-log";
|
|
23
|
+
import { type ReplicationDomain, SharedLog } from "@peerbit/shared-log";
|
|
27
24
|
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
28
|
-
import { AbortError } from "@peerbit/time";
|
|
25
|
+
import { AbortError, waitFor } from "@peerbit/time";
|
|
29
26
|
import { concat, fromString } from "uint8arrays";
|
|
30
27
|
import { copySerialization } from "./borsh.js";
|
|
31
28
|
import { MAX_BATCH_SIZE } from "./constants.js";
|
|
@@ -35,9 +32,9 @@ import { ResumableIterators } from "./resumable-iterator.js";
|
|
|
35
32
|
|
|
36
33
|
const logger = loggerFn({ module: "document-index" });
|
|
37
34
|
|
|
38
|
-
type BufferedResult<T
|
|
35
|
+
type BufferedResult<T, I extends Record<string, any>> = {
|
|
39
36
|
value: T;
|
|
40
|
-
indexed:
|
|
37
|
+
indexed: I;
|
|
41
38
|
context: types.Context;
|
|
42
39
|
from: PublicSignKey;
|
|
43
40
|
};
|
|
@@ -49,11 +46,16 @@ export type RemoteQueryOptions<R, D> = RPCRequestAllOptions<R> & {
|
|
|
49
46
|
domain?: ExtractArgs<D>;
|
|
50
47
|
eager?: boolean; // whether to query newly joined peers before they have matured
|
|
51
48
|
};
|
|
52
|
-
export type QueryOptions<R, D> = {
|
|
53
|
-
remote?: boolean | RemoteQueryOptions<types.AbstractSearchResult
|
|
49
|
+
export type QueryOptions<R, D, Resolve extends boolean | undefined> = {
|
|
50
|
+
remote?: boolean | RemoteQueryOptions<types.AbstractSearchResult, D>;
|
|
54
51
|
local?: boolean;
|
|
52
|
+
resolve?: Resolve;
|
|
55
53
|
};
|
|
56
|
-
export type SearchOptions<
|
|
54
|
+
export type SearchOptions<
|
|
55
|
+
R,
|
|
56
|
+
D,
|
|
57
|
+
Resolve extends boolean | undefined,
|
|
58
|
+
> = QueryOptions<R, D, Resolve>;
|
|
57
59
|
|
|
58
60
|
type Transformer<T, I> = (obj: T, context: types.Context) => MaybePromise<I>;
|
|
59
61
|
|
|
@@ -64,37 +66,104 @@ export type ResultsIterator<T> = {
|
|
|
64
66
|
all: () => Promise<T[]>;
|
|
65
67
|
};
|
|
66
68
|
|
|
67
|
-
type QueryDetailedOptions<
|
|
69
|
+
type QueryDetailedOptions<
|
|
70
|
+
T,
|
|
71
|
+
D,
|
|
72
|
+
Resolve extends boolean | undefined,
|
|
73
|
+
> = QueryOptions<T, D, Resolve> & {
|
|
68
74
|
onResponse?: (
|
|
69
|
-
response: types.AbstractSearchResult
|
|
75
|
+
response: types.AbstractSearchResult,
|
|
70
76
|
from: PublicSignKey,
|
|
71
77
|
) => void | Promise<void>;
|
|
72
78
|
};
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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>,
|
|
78
143
|
sync: (
|
|
79
|
-
|
|
80
|
-
|
|
144
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
145
|
+
response: types.Results<any>,
|
|
81
146
|
) => Promise<void>,
|
|
82
|
-
options?: QueryDetailedOptions<T, D>,
|
|
83
|
-
): Promise<RPCResponse<types.Results<
|
|
84
|
-
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>>[] = [];
|
|
85
150
|
for (const response of responses) {
|
|
86
151
|
if (!response.from) {
|
|
87
152
|
logger.error("Missing from for response");
|
|
88
153
|
}
|
|
89
154
|
|
|
90
155
|
if (response.response instanceof types.Results) {
|
|
91
|
-
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
|
+
);
|
|
92
161
|
if (typeof options?.remote !== "boolean" && options?.remote?.replicate) {
|
|
93
162
|
await sync(queryRequest, response.response);
|
|
94
163
|
}
|
|
95
164
|
options?.onResponse &&
|
|
96
165
|
(await options.onResponse(response.response, response.from!)); // TODO fix types
|
|
97
|
-
results.push(response as RPCResponse<types.Results<
|
|
166
|
+
results.push(response as RPCResponse<types.Results<any>>);
|
|
98
167
|
} else if (response.response instanceof types.NoAccess) {
|
|
99
168
|
logger.error("Search resulted in access error");
|
|
100
169
|
} else {
|
|
@@ -139,6 +208,17 @@ export type CanRead<T> = (
|
|
|
139
208
|
from: PublicSignKey,
|
|
140
209
|
) => Promise<boolean> | boolean;
|
|
141
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
|
+
|
|
142
222
|
@variant(0)
|
|
143
223
|
export class IndexableContext implements types.Context {
|
|
144
224
|
@field({ type: "u64" })
|
|
@@ -210,15 +290,19 @@ export type OpenOptions<
|
|
|
210
290
|
documentType: AbstractType<T>;
|
|
211
291
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
212
292
|
log: SharedLog<Operation, D, any>;
|
|
213
|
-
canRead?: CanRead<
|
|
293
|
+
canRead?: CanRead<I>;
|
|
214
294
|
canSearch?: CanSearch;
|
|
215
|
-
|
|
216
|
-
request: types.SearchRequest,
|
|
217
|
-
|
|
295
|
+
replicate: (
|
|
296
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
297
|
+
results: types.Results<
|
|
298
|
+
types.ResultTypeFromRequest<
|
|
299
|
+
types.SearchRequest | types.SearchRequestIndexed
|
|
300
|
+
>
|
|
301
|
+
>,
|
|
218
302
|
) => Promise<void>;
|
|
219
303
|
indexBy?: string | string[];
|
|
220
304
|
transform?: TransformOptions<T, I>;
|
|
221
|
-
|
|
305
|
+
compatibility: 6 | 7 | 8 | undefined;
|
|
222
306
|
};
|
|
223
307
|
|
|
224
308
|
type IndexableClass<I> = new (
|
|
@@ -233,7 +317,7 @@ export class DocumentIndex<
|
|
|
233
317
|
D extends ReplicationDomain<any, Operation, any>,
|
|
234
318
|
> extends Program<OpenOptions<T, I, D>> {
|
|
235
319
|
@field({ type: RPC })
|
|
236
|
-
_query: RPC<types.AbstractSearchRequest, types.AbstractSearchResult
|
|
320
|
+
_query: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
237
321
|
|
|
238
322
|
// Original document representation
|
|
239
323
|
documentType: AbstractType<T>;
|
|
@@ -243,6 +327,7 @@ export class DocumentIndex<
|
|
|
243
327
|
|
|
244
328
|
// The indexed document wrapped in a context
|
|
245
329
|
wrappedIndexedType: IndexableClass<I>;
|
|
330
|
+
indexedType: AbstractType<I>;
|
|
246
331
|
|
|
247
332
|
// The database type, for recursive indexing
|
|
248
333
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
@@ -254,16 +339,16 @@ export class DocumentIndex<
|
|
|
254
339
|
index: indexerTypes.Index<IDocumentWithContext<I>>;
|
|
255
340
|
private _resumableIterators: ResumableIterators<IDocumentWithContext<I>>;
|
|
256
341
|
|
|
257
|
-
|
|
342
|
+
compatibility: 6 | 7 | 8 | undefined;
|
|
258
343
|
|
|
259
344
|
// Transformation, indexer
|
|
260
345
|
/* fields: IndexableFields<T, I>; */
|
|
261
346
|
|
|
262
347
|
private _valueEncoding: Encoding<T>;
|
|
263
348
|
|
|
264
|
-
private _sync: (
|
|
265
|
-
|
|
266
|
-
|
|
349
|
+
private _sync: <V extends types.ResultValue<T> | types.ResultIndexedValue<I>>(
|
|
350
|
+
request: types.SearchRequest | types.SearchRequestIndexed,
|
|
351
|
+
results: types.Results<V>,
|
|
267
352
|
) => Promise<void>;
|
|
268
353
|
|
|
269
354
|
private _log: SharedLog<Operation, D, any>;
|
|
@@ -283,7 +368,7 @@ export class DocumentIndex<
|
|
|
283
368
|
>;
|
|
284
369
|
|
|
285
370
|
constructor(properties?: {
|
|
286
|
-
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult
|
|
371
|
+
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
287
372
|
}) {
|
|
288
373
|
super();
|
|
289
374
|
this._query = properties?.query || new RPC();
|
|
@@ -301,7 +386,7 @@ export class DocumentIndex<
|
|
|
301
386
|
!properties.transform?.type ||
|
|
302
387
|
properties.transform?.type === properties.documentType;
|
|
303
388
|
|
|
304
|
-
this.
|
|
389
|
+
this.compatibility = properties.compatibility;
|
|
305
390
|
|
|
306
391
|
@variant(0)
|
|
307
392
|
class IndexedClassWithContext {
|
|
@@ -315,10 +400,8 @@ export class DocumentIndex<
|
|
|
315
400
|
}
|
|
316
401
|
|
|
317
402
|
// copy all prototype values from indexedType to IndexedClassWithContext
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
IndexedClassWithContext,
|
|
321
|
-
);
|
|
403
|
+
this.indexedType = (properties.transform?.type || properties.documentType)!;
|
|
404
|
+
copySerialization(this.indexedType, IndexedClassWithContext);
|
|
322
405
|
|
|
323
406
|
this.wrappedIndexedType = IndexedClassWithContext as new (
|
|
324
407
|
value: I,
|
|
@@ -329,7 +412,56 @@ export class DocumentIndex<
|
|
|
329
412
|
this._isProgramValues = this.documentType instanceof Program;
|
|
330
413
|
this.dbType = properties.dbType;
|
|
331
414
|
this._resultQueue = new Map();
|
|
332
|
-
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
|
+
};
|
|
333
465
|
|
|
334
466
|
const transformOptions = properties.transform;
|
|
335
467
|
this.transformer = transformOptions
|
|
@@ -401,7 +533,7 @@ export class DocumentIndex<
|
|
|
401
533
|
const results = await this.processQuery(
|
|
402
534
|
query as
|
|
403
535
|
| types.SearchRequest
|
|
404
|
-
| types.
|
|
536
|
+
| types.SearchRequestIndexed
|
|
405
537
|
| types.CollectNextRequest,
|
|
406
538
|
ctx.from,
|
|
407
539
|
false,
|
|
@@ -410,29 +542,6 @@ export class DocumentIndex<
|
|
|
410
542
|
},
|
|
411
543
|
);
|
|
412
544
|
|
|
413
|
-
if (this.emitBlocksEagerly) {
|
|
414
|
-
await Promise.all(
|
|
415
|
-
results.results.map(async (x) => {
|
|
416
|
-
const hash = x.context.head;
|
|
417
|
-
const block = await this._log.log.blocks.get(hash);
|
|
418
|
-
if (!block) {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// todo dont do bytes -> entry -> bytes, but send the block directly
|
|
423
|
-
return this._log.rpc.send(
|
|
424
|
-
new BlocksMessage(new BlockResponse(hash, block)),
|
|
425
|
-
{
|
|
426
|
-
mode: new SilentDelivery({
|
|
427
|
-
to: [ctx.from!],
|
|
428
|
-
redundancy: 1,
|
|
429
|
-
}),
|
|
430
|
-
},
|
|
431
|
-
);
|
|
432
|
-
}),
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
545
|
return new types.Results({
|
|
437
546
|
// Even if results might have length 0, respond, because then we now at least there are no matching results
|
|
438
547
|
results: results.results,
|
|
@@ -473,10 +582,20 @@ export class DocumentIndex<
|
|
|
473
582
|
return dropped;
|
|
474
583
|
}
|
|
475
584
|
|
|
476
|
-
public async get(
|
|
585
|
+
public async get<Options extends QueryOptions<T, D, true | undefined>>(
|
|
477
586
|
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
478
|
-
options?:
|
|
479
|
-
): 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) {
|
|
480
599
|
return (
|
|
481
600
|
await this.getDetailed(
|
|
482
601
|
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key),
|
|
@@ -530,14 +649,26 @@ export class DocumentIndex<
|
|
|
530
649
|
});
|
|
531
650
|
}
|
|
532
651
|
|
|
533
|
-
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
|
+
>(
|
|
534
659
|
key: indexerTypes.IdKey | indexerTypes.IdPrimitive,
|
|
535
|
-
options?: QueryOptions<T, D>,
|
|
536
|
-
): Promise<types.Results<
|
|
537
|
-
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;
|
|
538
669
|
if (key instanceof Uint8Array) {
|
|
539
|
-
results = await this.
|
|
540
|
-
new
|
|
670
|
+
results = await this.queryCommence(
|
|
671
|
+
new requestClazz({
|
|
541
672
|
query: [
|
|
542
673
|
new indexerTypes.ByteMatchQuery({ key: this.indexBy, value: key }),
|
|
543
674
|
],
|
|
@@ -551,8 +682,8 @@ export class DocumentIndex<
|
|
|
551
682
|
typeof indexableKey === "number" ||
|
|
552
683
|
typeof indexableKey === "bigint"
|
|
553
684
|
) {
|
|
554
|
-
results = await this.
|
|
555
|
-
new
|
|
685
|
+
results = await this.queryCommence(
|
|
686
|
+
new requestClazz({
|
|
556
687
|
query: [
|
|
557
688
|
new indexerTypes.IntegerCompare({
|
|
558
689
|
key: this.indexBy,
|
|
@@ -564,8 +695,8 @@ export class DocumentIndex<
|
|
|
564
695
|
options,
|
|
565
696
|
);
|
|
566
697
|
} else if (typeof indexableKey === "string") {
|
|
567
|
-
results = await this.
|
|
568
|
-
new
|
|
698
|
+
results = await this.queryCommence(
|
|
699
|
+
new requestClazz({
|
|
569
700
|
query: [
|
|
570
701
|
new indexerTypes.StringMatch({
|
|
571
702
|
key: this.indexBy,
|
|
@@ -576,8 +707,8 @@ export class DocumentIndex<
|
|
|
576
707
|
options,
|
|
577
708
|
);
|
|
578
709
|
} else if (indexableKey instanceof Uint8Array) {
|
|
579
|
-
results = await this.
|
|
580
|
-
new
|
|
710
|
+
results = await this.queryCommence(
|
|
711
|
+
new requestClazz({
|
|
581
712
|
query: [
|
|
582
713
|
new indexerTypes.ByteMatchQuery({
|
|
583
714
|
key: this.indexBy,
|
|
@@ -590,19 +721,56 @@ export class DocumentIndex<
|
|
|
590
721
|
}
|
|
591
722
|
}
|
|
592
723
|
|
|
593
|
-
|
|
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>[];
|
|
594
757
|
}
|
|
595
758
|
|
|
596
759
|
getSize(): Promise<number> | number {
|
|
597
760
|
return this.index.getSize();
|
|
598
761
|
}
|
|
599
762
|
|
|
600
|
-
private async resolveDocument(
|
|
601
|
-
|
|
602
|
-
|
|
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
|
+
|
|
603
772
|
const cached =
|
|
604
|
-
this._resolverCache.get(
|
|
605
|
-
this._resolverProgramCache?.get(value.id.primitive);
|
|
773
|
+
this._resolverCache.get(id) || this._resolverProgramCache?.get(id);
|
|
606
774
|
if (cached != null) {
|
|
607
775
|
return { value: cached };
|
|
608
776
|
}
|
|
@@ -611,13 +779,13 @@ export class DocumentIndex<
|
|
|
611
779
|
// cast value to T, i.e. convert the class but keep all properties except the __context
|
|
612
780
|
const obj = Object.assign(
|
|
613
781
|
Object.create(this.documentType.prototype),
|
|
614
|
-
value.
|
|
782
|
+
value.indexed,
|
|
615
783
|
);
|
|
616
784
|
delete obj.__context;
|
|
617
785
|
return { value: obj as T };
|
|
618
786
|
}
|
|
619
787
|
|
|
620
|
-
const head = await this._log.log.get(value.
|
|
788
|
+
const head = await this._log.log.get(value.head);
|
|
621
789
|
if (!head) {
|
|
622
790
|
return undefined; // we could end up here if we recently pruned the document and other peers never persisted the entry
|
|
623
791
|
// TODO update changes in index before removing entries from log entry storage
|
|
@@ -636,14 +804,19 @@ export class DocumentIndex<
|
|
|
636
804
|
);
|
|
637
805
|
}
|
|
638
806
|
|
|
639
|
-
async processQuery
|
|
640
|
-
|
|
807
|
+
async processQuery<
|
|
808
|
+
R extends
|
|
809
|
+
| types.SearchRequest
|
|
810
|
+
| types.SearchRequestIndexed
|
|
811
|
+
| types.CollectNextRequest,
|
|
812
|
+
>(
|
|
813
|
+
query: R,
|
|
641
814
|
from: PublicSignKey,
|
|
642
815
|
isLocal: boolean,
|
|
643
816
|
options?: {
|
|
644
|
-
canRead?: CanRead<
|
|
817
|
+
canRead?: CanRead<I>;
|
|
645
818
|
},
|
|
646
|
-
): Promise<types.Results<
|
|
819
|
+
): Promise<types.Results<types.ResultTypeFromRequest<R>>> {
|
|
647
820
|
// We do special case for querying the id as we can do it faster than iterating
|
|
648
821
|
|
|
649
822
|
let prevQueued = isLocal
|
|
@@ -656,9 +829,16 @@ export class DocumentIndex<
|
|
|
656
829
|
let indexedResult:
|
|
657
830
|
| indexerTypes.IndexedResults<IDocumentWithContext<I>>
|
|
658
831
|
| undefined = undefined;
|
|
659
|
-
|
|
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;
|
|
660
839
|
indexedResult = await this._resumableIterators.iterateAndFetch(query);
|
|
661
840
|
} else if (query instanceof types.CollectNextRequest) {
|
|
841
|
+
fromQuery = this._resumableIterators.queues.get(query.idString)?.request;
|
|
662
842
|
indexedResult =
|
|
663
843
|
prevQueued?.keptInIndex === 0
|
|
664
844
|
? []
|
|
@@ -666,7 +846,7 @@ export class DocumentIndex<
|
|
|
666
846
|
} else {
|
|
667
847
|
throw new Error("Unsupported");
|
|
668
848
|
}
|
|
669
|
-
|
|
849
|
+
|
|
670
850
|
let resultSize = 0;
|
|
671
851
|
|
|
672
852
|
let toIterate = prevQueued
|
|
@@ -693,6 +873,8 @@ export class DocumentIndex<
|
|
|
693
873
|
this._resultQueue.set(query.idString, prevQueued);
|
|
694
874
|
}
|
|
695
875
|
|
|
876
|
+
const filteredResults: types.Result[] = [];
|
|
877
|
+
|
|
696
878
|
for (const result of toIterate) {
|
|
697
879
|
if (!isLocal) {
|
|
698
880
|
resultSize += result.value.__context.size;
|
|
@@ -701,25 +883,55 @@ export class DocumentIndex<
|
|
|
701
883
|
continue;
|
|
702
884
|
}
|
|
703
885
|
}
|
|
886
|
+
const indexedUnwrapped = Object.assign(
|
|
887
|
+
Object.create(this.indexedType.prototype),
|
|
888
|
+
result.value,
|
|
889
|
+
);
|
|
704
890
|
|
|
705
|
-
const value = await this.resolveDocument(result);
|
|
706
891
|
if (
|
|
707
|
-
|
|
708
|
-
|
|
892
|
+
options?.canRead &&
|
|
893
|
+
!(await options.canRead(indexedUnwrapped, from))
|
|
709
894
|
) {
|
|
710
895
|
continue;
|
|
711
896
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
new types.ResultWithSource({
|
|
715
|
-
context: result.value.__context.toContext(),
|
|
716
|
-
value: value.value,
|
|
717
|
-
source: serialize(value.value),
|
|
897
|
+
if (fromQuery instanceof types.SearchRequest) {
|
|
898
|
+
const value = await this.resolveDocument({
|
|
718
899
|
indexed: result.value,
|
|
719
|
-
|
|
720
|
-
|
|
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
|
+
}
|
|
721
932
|
}
|
|
722
|
-
|
|
933
|
+
|
|
934
|
+
const results: types.Results<any> = new types.Results<any>({
|
|
723
935
|
results: filteredResults,
|
|
724
936
|
kept: BigInt(kept + (prevQueued?.queue.length || 0)),
|
|
725
937
|
});
|
|
@@ -734,6 +946,7 @@ export class DocumentIndex<
|
|
|
734
946
|
private clearResultsQueue(
|
|
735
947
|
query:
|
|
736
948
|
| types.SearchRequest
|
|
949
|
+
| types.SearchRequestIndexed
|
|
737
950
|
| types.CollectNextRequest
|
|
738
951
|
| types.CloseIteratorRequest,
|
|
739
952
|
) {
|
|
@@ -771,14 +984,18 @@ export class DocumentIndex<
|
|
|
771
984
|
* @param options
|
|
772
985
|
* @returns
|
|
773
986
|
*/
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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>[]> {
|
|
778
996
|
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
779
|
-
let remote:
|
|
780
|
-
|
|
781
|
-
| undefined = undefined;
|
|
997
|
+
let remote: RemoteQueryOptions<types.AbstractSearchResult, D> | undefined =
|
|
998
|
+
undefined;
|
|
782
999
|
if (typeof options?.remote === "boolean") {
|
|
783
1000
|
if (options?.remote) {
|
|
784
1001
|
remote = {};
|
|
@@ -800,7 +1017,7 @@ export class DocumentIndex<
|
|
|
800
1017
|
"Expecting either 'options.remote' or 'options.local' to be true",
|
|
801
1018
|
);
|
|
802
1019
|
}
|
|
803
|
-
const allResults: types.Results<
|
|
1020
|
+
const allResults: types.Results<types.ResultTypeFromRequest<R>>[] = [];
|
|
804
1021
|
|
|
805
1022
|
if (local) {
|
|
806
1023
|
const results = await this.processQuery(
|
|
@@ -815,7 +1032,7 @@ export class DocumentIndex<
|
|
|
815
1032
|
}
|
|
816
1033
|
}
|
|
817
1034
|
|
|
818
|
-
let resolved: types.Results<
|
|
1035
|
+
let resolved: types.Results<types.ResultTypeFromRequest<R>>[] = [];
|
|
819
1036
|
if (remote) {
|
|
820
1037
|
const replicatorGroups = await this._log.getCover(
|
|
821
1038
|
remote.domain ?? (undefined as any),
|
|
@@ -828,15 +1045,17 @@ export class DocumentIndex<
|
|
|
828
1045
|
if (replicatorGroups) {
|
|
829
1046
|
const groupHashes: string[][] = replicatorGroups.map((x) => [x]);
|
|
830
1047
|
const responseHandler = async (
|
|
831
|
-
results: RPCResponse<types.AbstractSearchResult
|
|
1048
|
+
results: RPCResponse<types.AbstractSearchResult>[],
|
|
832
1049
|
) => {
|
|
833
|
-
|
|
1050
|
+
const resultInitialized = await introduceEntries(
|
|
834
1051
|
queryRequest,
|
|
835
1052
|
results,
|
|
836
1053
|
this.documentType,
|
|
1054
|
+
this.indexedType,
|
|
837
1055
|
this._sync,
|
|
838
1056
|
options,
|
|
839
|
-
)
|
|
1057
|
+
);
|
|
1058
|
+
for (const r of resultInitialized) {
|
|
840
1059
|
resolved.push(r.response);
|
|
841
1060
|
}
|
|
842
1061
|
};
|
|
@@ -884,58 +1103,129 @@ export class DocumentIndex<
|
|
|
884
1103
|
}
|
|
885
1104
|
}
|
|
886
1105
|
}
|
|
887
|
-
return allResults;
|
|
1106
|
+
return allResults as any; // TODO types
|
|
888
1107
|
}
|
|
889
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
|
+
|
|
890
1118
|
/**
|
|
891
1119
|
* Query and retrieve results
|
|
892
1120
|
* @param queryRequest
|
|
893
1121
|
* @param options
|
|
894
1122
|
* @returns
|
|
895
1123
|
*/
|
|
896
|
-
public async search
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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>[]> {
|
|
900
1132
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
901
|
-
|
|
1133
|
+
const coercedRequest: types.SearchRequest | types.SearchRequestIndexed =
|
|
1134
|
+
coerceQuery(queryRequest, options);
|
|
1135
|
+
coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
|
|
902
1136
|
|
|
903
1137
|
// So that the iterator is pre-fetching the right amount of entries
|
|
904
|
-
const iterator = this.iterate(
|
|
1138
|
+
const iterator = this.iterate(coercedRequest, options);
|
|
905
1139
|
|
|
906
1140
|
// So that this call will not do any remote requests
|
|
907
|
-
const allResults: T[] = [];
|
|
908
|
-
|
|
1141
|
+
const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
1142
|
+
|
|
1143
|
+
while (
|
|
1144
|
+
iterator.done() !== true &&
|
|
1145
|
+
coercedRequest.fetch > allResults.length
|
|
1146
|
+
) {
|
|
909
1147
|
// We might need to pull .next multiple time due to data message size limitations
|
|
1148
|
+
|
|
910
1149
|
for (const result of await iterator.next(
|
|
911
|
-
|
|
1150
|
+
coercedRequest.fetch - allResults.length,
|
|
912
1151
|
)) {
|
|
913
|
-
allResults.push(result);
|
|
1152
|
+
allResults.push(result as ValueTypeFromRequest<Resolve, T, I>);
|
|
914
1153
|
}
|
|
915
1154
|
}
|
|
916
1155
|
|
|
917
1156
|
await iterator.close();
|
|
918
1157
|
|
|
919
|
-
//
|
|
1158
|
+
// Deduplicate and return values directly
|
|
920
1159
|
return dedup(allResults, this.indexByResolver);
|
|
921
1160
|
}
|
|
922
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
|
+
|
|
923
1171
|
/**
|
|
924
1172
|
* Query and retrieve documents in a iterator
|
|
925
1173
|
* @param queryRequest
|
|
926
1174
|
* @param options
|
|
927
1175
|
* @returns
|
|
928
1176
|
*/
|
|
929
|
-
public iterate
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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
|
+
|
|
933
1220
|
let fetchPromise: Promise<any> | undefined = undefined;
|
|
934
1221
|
const peerBufferMap: Map<
|
|
935
1222
|
string,
|
|
936
1223
|
{
|
|
937
1224
|
kept: number;
|
|
938
|
-
buffer: BufferedResult<
|
|
1225
|
+
buffer: BufferedResult<
|
|
1226
|
+
types.ResultValue<T> | types.ResultIndexedValue<I>,
|
|
1227
|
+
I
|
|
1228
|
+
>[];
|
|
939
1229
|
}
|
|
940
1230
|
> = new Map();
|
|
941
1231
|
const visited = new Set<string | number | bigint>();
|
|
@@ -947,8 +1237,8 @@ export class DocumentIndex<
|
|
|
947
1237
|
const controller = new AbortController();
|
|
948
1238
|
|
|
949
1239
|
const peerBuffers = (): {
|
|
950
|
-
indexed:
|
|
951
|
-
value: T
|
|
1240
|
+
indexed: I;
|
|
1241
|
+
value: types.ResultValue<T> | types.ResultIndexedValue<I>;
|
|
952
1242
|
from: PublicSignKey;
|
|
953
1243
|
context: types.Context;
|
|
954
1244
|
}[] => {
|
|
@@ -957,8 +1247,8 @@ export class DocumentIndex<
|
|
|
957
1247
|
|
|
958
1248
|
const fetchFirst = async (n: number): Promise<boolean> => {
|
|
959
1249
|
done = true; // Assume we are donne
|
|
960
|
-
|
|
961
|
-
await this.
|
|
1250
|
+
queryRequestCoerced.fetch = n;
|
|
1251
|
+
await this.queryCommence(queryRequestCoerced, {
|
|
962
1252
|
...options,
|
|
963
1253
|
onResponse: async (response, from) => {
|
|
964
1254
|
if (!from) {
|
|
@@ -969,7 +1259,9 @@ export class DocumentIndex<
|
|
|
969
1259
|
logger.error("Dont have access");
|
|
970
1260
|
return;
|
|
971
1261
|
} else if (response instanceof types.Results) {
|
|
972
|
-
const results = response as types.Results<
|
|
1262
|
+
const results = response as types.Results<
|
|
1263
|
+
types.ResultTypeFromRequest<R>
|
|
1264
|
+
>;
|
|
973
1265
|
if (results.kept === 0n && results.results.length === 0) {
|
|
974
1266
|
return;
|
|
975
1267
|
}
|
|
@@ -977,24 +1269,42 @@ export class DocumentIndex<
|
|
|
977
1269
|
if (results.kept > 0n) {
|
|
978
1270
|
done = false; // we have more to do later!
|
|
979
1271
|
}
|
|
980
|
-
const buffer: BufferedResult<
|
|
1272
|
+
const buffer: BufferedResult<types.ResultTypeFromRequest<R>, I>[] =
|
|
1273
|
+
[];
|
|
981
1274
|
|
|
982
1275
|
for (const result of results.results) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
+
});
|
|
988
1307
|
}
|
|
989
|
-
visited.add(indexKey);
|
|
990
|
-
buffer.push({
|
|
991
|
-
value: result.value,
|
|
992
|
-
context: result.context,
|
|
993
|
-
from,
|
|
994
|
-
indexed:
|
|
995
|
-
result.indexed ||
|
|
996
|
-
(await this.transformer(result.value, result.context)),
|
|
997
|
-
});
|
|
998
1308
|
}
|
|
999
1309
|
|
|
1000
1310
|
peerBufferMap.set(from.hashcode(), {
|
|
@@ -1010,7 +1320,7 @@ export class DocumentIndex<
|
|
|
1010
1320
|
});
|
|
1011
1321
|
|
|
1012
1322
|
if (done) {
|
|
1013
|
-
this.clearResultsQueue(
|
|
1323
|
+
this.clearResultsQueue(queryRequestCoerced);
|
|
1014
1324
|
}
|
|
1015
1325
|
|
|
1016
1326
|
return done;
|
|
@@ -1048,7 +1358,7 @@ export class DocumentIndex<
|
|
|
1048
1358
|
// TODO buffer more than deleted?
|
|
1049
1359
|
// TODO batch to multiple 'to's
|
|
1050
1360
|
const collectRequest = new types.CollectNextRequest({
|
|
1051
|
-
id:
|
|
1361
|
+
id: queryRequestCoerced.id,
|
|
1052
1362
|
amount: n - buffer.buffer.length,
|
|
1053
1363
|
});
|
|
1054
1364
|
// Fetch locally?
|
|
@@ -1119,57 +1429,54 @@ export class DocumentIndex<
|
|
|
1119
1429
|
})
|
|
1120
1430
|
.then((response) =>
|
|
1121
1431
|
introduceEntries(
|
|
1122
|
-
|
|
1432
|
+
queryRequestCoerced,
|
|
1123
1433
|
response,
|
|
1124
1434
|
this.documentType,
|
|
1435
|
+
this.indexedType,
|
|
1125
1436
|
this._sync,
|
|
1126
1437
|
options,
|
|
1127
1438
|
)
|
|
1128
|
-
.then((responses) => {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
if (response.response.results.length === 0) {
|
|
1137
|
-
if (peerBufferMap.get(peer)?.buffer.length === 0) {
|
|
1138
|
-
peerBufferMap.delete(peer); // No more results
|
|
1139
|
-
}
|
|
1140
|
-
} else {
|
|
1141
|
-
const peerBuffer = peerBufferMap.get(peer);
|
|
1142
|
-
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");
|
|
1143
1446
|
return;
|
|
1144
1447
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
if (
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
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;
|
|
1155
1457
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1458
|
+
peerBuffer.kept = Number(response.response.kept);
|
|
1459
|
+
for (const result of response.response.results) {
|
|
1460
|
+
const idPrimitive = indexerTypes.toId(
|
|
1158
1461
|
this.indexByResolver(result.value),
|
|
1159
|
-
).primitive
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
result.
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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
|
+
}
|
|
1170
1477
|
}
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1478
|
+
}),
|
|
1479
|
+
);
|
|
1173
1480
|
})
|
|
1174
1481
|
.catch((e) => {
|
|
1175
1482
|
logger.error(
|
|
@@ -1210,7 +1517,7 @@ export class DocumentIndex<
|
|
|
1210
1517
|
indexerTypes.extractSortCompare(
|
|
1211
1518
|
a.indexed,
|
|
1212
1519
|
b.indexed,
|
|
1213
|
-
|
|
1520
|
+
queryRequestCoerced.sort,
|
|
1214
1521
|
),
|
|
1215
1522
|
);
|
|
1216
1523
|
|
|
@@ -1231,18 +1538,38 @@ export class DocumentIndex<
|
|
|
1231
1538
|
}
|
|
1232
1539
|
|
|
1233
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
|
+
}
|
|
1234
1564
|
|
|
1235
|
-
return dedup(
|
|
1236
|
-
batch.map((x) => x.value),
|
|
1237
|
-
this.indexByResolver,
|
|
1238
|
-
);
|
|
1565
|
+
return dedup(coercedBatch, this.indexByResolver);
|
|
1239
1566
|
};
|
|
1240
1567
|
|
|
1241
1568
|
const close = async () => {
|
|
1242
1569
|
controller.abort(new AbortError("Iterator closed"));
|
|
1243
1570
|
|
|
1244
1571
|
const closeRequest = new types.CloseIteratorRequest({
|
|
1245
|
-
id:
|
|
1572
|
+
id: queryRequestCoerced.id,
|
|
1246
1573
|
});
|
|
1247
1574
|
const promises: Promise<any>[] = [];
|
|
1248
1575
|
for (const [peer, buffer] of peerBufferMap) {
|
|
@@ -1276,7 +1603,7 @@ export class DocumentIndex<
|
|
|
1276
1603
|
next,
|
|
1277
1604
|
done: doneFn,
|
|
1278
1605
|
all: async () => {
|
|
1279
|
-
let result: T[] = [];
|
|
1606
|
+
let result: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
1280
1607
|
while (doneFn() !== true) {
|
|
1281
1608
|
let batch = await next(100);
|
|
1282
1609
|
result.push(...batch);
|
|
@@ -1285,4 +1612,34 @@ export class DocumentIndex<
|
|
|
1285
1612
|
},
|
|
1286
1613
|
};
|
|
1287
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
|
+
}
|
|
1288
1645
|
}
|