@peerbit/document 6.0.7-aa577a5 → 6.0.7-cccc078
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/index.js +13 -14
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/memory/index.d.ts +2 -0
- package/dist/benchmark/memory/index.d.ts.map +1 -0
- package/dist/benchmark/memory/index.js +122 -0
- package/dist/benchmark/memory/index.js.map +1 -0
- package/dist/benchmark/memory/insert.d.ts +2 -0
- package/dist/benchmark/memory/insert.d.ts.map +1 -0
- package/dist/benchmark/memory/insert.js +133 -0
- package/dist/benchmark/memory/insert.js.map +1 -0
- package/dist/benchmark/memory/utils.d.ts +13 -0
- package/dist/benchmark/memory/utils.d.ts.map +1 -0
- package/dist/benchmark/memory/utils.js +2 -0
- package/dist/benchmark/memory/utils.js.map +1 -0
- package/dist/benchmark/replication.js +27 -29
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/borsh.d.ts +2 -0
- package/dist/src/borsh.d.ts.map +1 -0
- package/dist/src/borsh.js +16 -0
- package/dist/src/borsh.js.map +1 -0
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/program.d.ts +16 -19
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +57 -68
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +46 -32
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +236 -133
- package/dist/src/search.js.map +1 -1
- package/package.json +16 -11
- package/src/borsh.ts +19 -0
- package/src/index.ts +0 -1
- package/src/program.ts +118 -118
- package/src/search.ts +438 -218
package/src/search.ts
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import { type AbstractType, field, serialize, variant } from "@dao-xyz/borsh";
|
|
2
|
+
import { Cache } from "@peerbit/cache";
|
|
3
|
+
import {
|
|
4
|
+
type MaybePromise,
|
|
5
|
+
PublicSignKey,
|
|
6
|
+
sha256Base64Sync,
|
|
7
|
+
} from "@peerbit/crypto";
|
|
8
|
+
import * as types from "@peerbit/document-interface";
|
|
9
|
+
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
10
|
+
import { HashmapIndex } from "@peerbit/indexer-simple";
|
|
2
11
|
import { BORSH_ENCODING, type Encoding, Entry } from "@peerbit/log";
|
|
12
|
+
import { logger as loggerFn } from "@peerbit/logger";
|
|
3
13
|
import { Program } from "@peerbit/program";
|
|
4
|
-
import * as types from "@peerbit/document-interface";
|
|
5
14
|
import {
|
|
15
|
+
MissingResponsesError,
|
|
6
16
|
RPC,
|
|
17
|
+
type RPCRequestAllOptions,
|
|
7
18
|
type RPCResponse,
|
|
8
19
|
queryAll,
|
|
9
|
-
MissingResponsesError,
|
|
10
|
-
type RPCRequestAllOptions
|
|
11
20
|
} from "@peerbit/rpc";
|
|
12
|
-
import { logger as loggerFn } from "@peerbit/logger";
|
|
13
|
-
import { PublicSignKey, sha256Base64Sync } from "@peerbit/crypto";
|
|
14
21
|
import { SharedLog } from "@peerbit/shared-log";
|
|
15
|
-
import { concat, fromString } from "uint8arrays";
|
|
16
22
|
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
17
23
|
import { AbortError } from "@peerbit/time";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
24
|
+
import { concat, fromString } from "uint8arrays";
|
|
25
|
+
import { copySerialization } from "./borsh.js";
|
|
20
26
|
import { MAX_BATCH_SIZE } from "./constants.js";
|
|
21
27
|
|
|
22
28
|
const logger = loggerFn({ module: "document-index" });
|
|
@@ -29,7 +35,7 @@ type BufferedResult<T> = {
|
|
|
29
35
|
};
|
|
30
36
|
|
|
31
37
|
@variant(0)
|
|
32
|
-
export class Operation /* <T> */ {
|
|
38
|
+
export class Operation /* <T> */ {}
|
|
33
39
|
|
|
34
40
|
export const BORSH_ENCODING_OPERATION = BORSH_ENCODING(Operation);
|
|
35
41
|
|
|
@@ -44,12 +50,9 @@ export class PutOperation extends Operation /* <T> */ {
|
|
|
44
50
|
|
|
45
51
|
/* _value?: T; */
|
|
46
52
|
|
|
47
|
-
constructor(props
|
|
53
|
+
constructor(props: { data: Uint8Array /* value?: T */ }) {
|
|
48
54
|
super();
|
|
49
|
-
|
|
50
|
-
this.data = props.data;
|
|
51
|
-
/* this._value = props.value; */
|
|
52
|
-
}
|
|
55
|
+
this.data = props.data;
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -72,10 +75,10 @@ export class PutAllOperation<T> extends Operation<T> {
|
|
|
72
75
|
*/
|
|
73
76
|
@variant(2)
|
|
74
77
|
export class DeleteOperation extends Operation {
|
|
75
|
-
@field({ type:
|
|
76
|
-
key:
|
|
78
|
+
@field({ type: indexerTypes.IdKey })
|
|
79
|
+
key: indexerTypes.IdKey;
|
|
77
80
|
|
|
78
|
-
constructor(props: { key:
|
|
81
|
+
constructor(props: { key: indexerTypes.IdKey }) {
|
|
79
82
|
super();
|
|
80
83
|
this.key = props.key;
|
|
81
84
|
}
|
|
@@ -90,13 +93,11 @@ export type QueryOptions<R> = {
|
|
|
90
93
|
remote?: boolean | RemoteQueryOptions<types.AbstractSearchResult<R>>;
|
|
91
94
|
local?: boolean;
|
|
92
95
|
};
|
|
93
|
-
export type SearchOptions<R> =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
context: types.Context
|
|
97
|
-
) => Record<string, any> | Promise<Record<string, any>>;
|
|
96
|
+
export type SearchOptions<R> = QueryOptions<R>;
|
|
97
|
+
|
|
98
|
+
type Transformer<T, I> = (obj: T, context: types.Context) => MaybePromise<I>;
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
type ResultsIterator<T> = {
|
|
100
101
|
close: () => Promise<void>;
|
|
101
102
|
next: (number: number) => Promise<T[]>;
|
|
102
103
|
done: () => boolean;
|
|
@@ -105,7 +106,7 @@ export type ResultsIterator<T> = {
|
|
|
105
106
|
type QueryDetailedOptions<T> = QueryOptions<T> & {
|
|
106
107
|
onResponse?: (
|
|
107
108
|
response: types.AbstractSearchResult<T>,
|
|
108
|
-
from: PublicSignKey
|
|
109
|
+
from: PublicSignKey,
|
|
109
110
|
) => void | Promise<void>;
|
|
110
111
|
};
|
|
111
112
|
|
|
@@ -113,7 +114,7 @@ const introduceEntries = async <T>(
|
|
|
113
114
|
responses: RPCResponse<types.AbstractSearchResult<T>>[],
|
|
114
115
|
type: AbstractType<T>,
|
|
115
116
|
sync: (result: types.Results<T>) => Promise<void>,
|
|
116
|
-
options?: QueryDetailedOptions<T
|
|
117
|
+
options?: QueryDetailedOptions<T>,
|
|
117
118
|
): Promise<RPCResponse<types.Results<T>>[]> => {
|
|
118
119
|
const results: RPCResponse<types.Results<T>>[] = [];
|
|
119
120
|
for (const response of responses) {
|
|
@@ -140,16 +141,17 @@ const introduceEntries = async <T>(
|
|
|
140
141
|
|
|
141
142
|
const dedup = <T>(
|
|
142
143
|
allResult: T[],
|
|
143
|
-
dedupBy: (obj: any) => string | Uint8Array | number | bigint
|
|
144
|
+
dedupBy: (obj: any) => string | Uint8Array | number | bigint,
|
|
144
145
|
) => {
|
|
145
|
-
const unique: Set<
|
|
146
|
+
const unique: Set<indexerTypes.IdPrimitive> = new Set();
|
|
146
147
|
const dedup: T[] = [];
|
|
147
148
|
for (const result of allResult) {
|
|
148
|
-
const key =
|
|
149
|
-
|
|
149
|
+
const key = indexerTypes.toId(dedupBy(result));
|
|
150
|
+
const primitive = key.primitive;
|
|
151
|
+
if (unique.has(primitive)) {
|
|
150
152
|
continue;
|
|
151
153
|
}
|
|
152
|
-
unique.add(
|
|
154
|
+
unique.add(primitive);
|
|
153
155
|
dedup.push(result);
|
|
154
156
|
}
|
|
155
157
|
return dedup;
|
|
@@ -163,44 +165,84 @@ if (!(await this.canRead(message.sender))) {
|
|
|
163
165
|
} */
|
|
164
166
|
|
|
165
167
|
export type CanSearch = (
|
|
166
|
-
request:
|
|
167
|
-
from: PublicSignKey
|
|
168
|
+
request: indexerTypes.SearchRequest | indexerTypes.CollectNextRequest,
|
|
169
|
+
from: PublicSignKey,
|
|
168
170
|
) => Promise<boolean> | boolean;
|
|
169
171
|
|
|
170
172
|
export type CanRead<T> = (
|
|
171
173
|
result: T,
|
|
172
|
-
from: PublicSignKey
|
|
174
|
+
from: PublicSignKey,
|
|
173
175
|
) => Promise<boolean> | boolean;
|
|
174
176
|
|
|
175
|
-
export type
|
|
176
|
-
|
|
177
|
+
export type IDocumentWithContext<I> = {
|
|
178
|
+
__context: types.Context;
|
|
179
|
+
} & I;
|
|
180
|
+
|
|
181
|
+
export type TransformerAsConstructor<T, I> = {
|
|
182
|
+
type?: new (arg: T, context: types.Context) => I;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export type TransformerAsFunction<T, I> = {
|
|
186
|
+
type: AbstractType<I>;
|
|
187
|
+
transform: (arg: T, context: types.Context) => I | Promise<I>;
|
|
188
|
+
};
|
|
189
|
+
export type TransformOptions<T, I> =
|
|
190
|
+
| TransformerAsConstructor<T, I>
|
|
191
|
+
| TransformerAsFunction<T, I>;
|
|
192
|
+
|
|
193
|
+
const isTransformerWithFunction = <T, I>(
|
|
194
|
+
options: TransformOptions<T, I>,
|
|
195
|
+
): options is TransformerAsFunction<T, I> => {
|
|
196
|
+
return (options as TransformerAsFunction<T, I>).transform != null;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export type OpenOptions<T, I> = {
|
|
200
|
+
documentType: AbstractType<T>;
|
|
201
|
+
|
|
177
202
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
178
203
|
log: SharedLog<Operation>;
|
|
179
204
|
canRead?: CanRead<T>;
|
|
180
205
|
canSearch?: CanSearch;
|
|
181
|
-
engine?: types.IndexEngine;
|
|
182
206
|
sync: (result: types.Results<T>) => Promise<void>;
|
|
183
207
|
indexBy?: string | string[];
|
|
184
|
-
|
|
208
|
+
transform?: TransformOptions<T, I>;
|
|
185
209
|
};
|
|
186
210
|
|
|
211
|
+
type IndexableClass<I> = new (
|
|
212
|
+
value: I,
|
|
213
|
+
context: types.Context,
|
|
214
|
+
) => IDocumentWithContext<I>; /* IDocumentWithContext<T>; */
|
|
215
|
+
|
|
187
216
|
@variant("documents_index")
|
|
188
|
-
export class DocumentIndex<T
|
|
217
|
+
export class DocumentIndex<T, I extends Record<string, any>> extends Program<
|
|
218
|
+
OpenOptions<T, I>
|
|
219
|
+
> {
|
|
189
220
|
@field({ type: RPC })
|
|
190
|
-
_query: RPC<
|
|
221
|
+
_query: RPC<
|
|
222
|
+
indexerTypes.AbstractSearchRequest,
|
|
223
|
+
types.AbstractSearchResult<T>
|
|
224
|
+
>;
|
|
225
|
+
|
|
226
|
+
// Original document representation
|
|
227
|
+
documentType: AbstractType<T>;
|
|
191
228
|
|
|
192
|
-
|
|
229
|
+
// transform options
|
|
230
|
+
transformer: Transformer<T, I>;
|
|
193
231
|
|
|
194
|
-
|
|
232
|
+
// The indexed document wrapped in a context
|
|
233
|
+
wrappedIndexedType: IndexableClass<I>;
|
|
234
|
+
|
|
235
|
+
// The database type, for recursive indexing
|
|
195
236
|
dbType: AbstractType<types.IDocumentStore<T>>;
|
|
237
|
+
indexedTypeIsDocumentType: boolean;
|
|
196
238
|
|
|
197
239
|
// Index key
|
|
198
|
-
private indexBy: string
|
|
199
|
-
private indexByArr: string[];
|
|
240
|
+
private indexBy: string[];
|
|
200
241
|
private indexByResolver: (obj: any) => string | Uint8Array;
|
|
242
|
+
index: indexerTypes.Index<IDocumentWithContext<I>>;
|
|
201
243
|
|
|
202
244
|
// Transformation, indexer
|
|
203
|
-
fields: IndexableFields<T>;
|
|
245
|
+
/* fields: IndexableFields<T, I>; */
|
|
204
246
|
|
|
205
247
|
private _valueEncoding: Encoding<T>;
|
|
206
248
|
|
|
@@ -211,8 +253,22 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
211
253
|
private _resolverProgramCache?: Map<string | number | bigint, T>;
|
|
212
254
|
private _resolverCache: Cache<T>;
|
|
213
255
|
private _isProgramValues: boolean;
|
|
256
|
+
|
|
257
|
+
private _resultQueue: Map<
|
|
258
|
+
string,
|
|
259
|
+
{
|
|
260
|
+
from: PublicSignKey;
|
|
261
|
+
keptInIndex: number;
|
|
262
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
263
|
+
queue: indexerTypes.IndexedResult<IDocumentWithContext<I>>[];
|
|
264
|
+
}
|
|
265
|
+
>;
|
|
266
|
+
|
|
214
267
|
constructor(properties?: {
|
|
215
|
-
query?: RPC<
|
|
268
|
+
query?: RPC<
|
|
269
|
+
indexerTypes.AbstractSearchRequest,
|
|
270
|
+
types.AbstractSearchResult<T>
|
|
271
|
+
>;
|
|
216
272
|
}) {
|
|
217
273
|
super();
|
|
218
274
|
this._query = properties?.query || new RPC();
|
|
@@ -222,47 +278,84 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
222
278
|
return this._valueEncoding;
|
|
223
279
|
}
|
|
224
280
|
|
|
225
|
-
async open(properties: OpenOptions<T>) {
|
|
281
|
+
async open(properties: OpenOptions<T, I>) {
|
|
226
282
|
this._log = properties.log;
|
|
227
|
-
|
|
283
|
+
|
|
284
|
+
this.documentType = properties.documentType;
|
|
285
|
+
this.indexedTypeIsDocumentType =
|
|
286
|
+
!properties.transform?.type ||
|
|
287
|
+
properties.transform?.type === properties.documentType;
|
|
288
|
+
|
|
289
|
+
class IndexedClassWithContex {
|
|
290
|
+
@field({ type: types.Context })
|
|
291
|
+
__context: types.Context;
|
|
292
|
+
|
|
293
|
+
constructor(value: I, context: types.Context) {
|
|
294
|
+
Object.assign(this, value);
|
|
295
|
+
this.__context = context;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// copy all prototype values from indexedType to IndexedClassWithContex
|
|
300
|
+
copySerialization(
|
|
301
|
+
(properties.transform?.type || properties.documentType)!,
|
|
302
|
+
IndexedClassWithContex,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
this.wrappedIndexedType = IndexedClassWithContex as new (
|
|
306
|
+
value: I,
|
|
307
|
+
context: types.Context,
|
|
308
|
+
) => IDocumentWithContext<I>;
|
|
309
|
+
|
|
228
310
|
// if this.type is a class that extends Program we want to do special functionality
|
|
229
|
-
this._isProgramValues = this.
|
|
311
|
+
this._isProgramValues = this.documentType instanceof Program;
|
|
230
312
|
this.dbType = properties.dbType;
|
|
313
|
+
this._resultQueue = new Map();
|
|
231
314
|
this._sync = properties.sync;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.
|
|
235
|
-
?
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
315
|
+
|
|
316
|
+
const transformOptions = properties.transform;
|
|
317
|
+
this.transformer = transformOptions
|
|
318
|
+
? isTransformerWithFunction(transformOptions)
|
|
319
|
+
? (obj, context) => transformOptions.transform(obj, context)
|
|
320
|
+
: transformOptions.type
|
|
321
|
+
? (obj, context) => new transformOptions.type!(obj, context)
|
|
322
|
+
: (obj) => obj as any as I
|
|
323
|
+
: (obj) => obj as any as I; // TODO types
|
|
324
|
+
|
|
325
|
+
const maybeArr = properties.indexBy || DEFAULT_INDEX_BY;
|
|
326
|
+
this.indexBy = Array.isArray(maybeArr) ? maybeArr : [maybeArr];
|
|
327
|
+
this.indexByResolver = (obj: any) =>
|
|
328
|
+
indexerTypes.extractFieldValue(obj, this.indexBy);
|
|
329
|
+
|
|
330
|
+
this._valueEncoding = BORSH_ENCODING(this.documentType);
|
|
243
331
|
|
|
244
332
|
if (this._isProgramValues) {
|
|
245
333
|
this._resolverProgramCache = new Map();
|
|
246
334
|
}
|
|
247
|
-
this._resolverCache = new Cache({ max:
|
|
248
|
-
|
|
249
|
-
this.
|
|
335
|
+
this._resolverCache = new Cache({ max: 10 }); // TODO choose limit better (adaptive)
|
|
336
|
+
|
|
337
|
+
this.index =
|
|
338
|
+
(await (
|
|
339
|
+
await this.node.indexer.scope(
|
|
340
|
+
sha256Base64Sync(
|
|
341
|
+
concat([this._log.log.id, fromString("/document-index")]),
|
|
342
|
+
),
|
|
343
|
+
)
|
|
344
|
+
).init({
|
|
345
|
+
indexBy: this.indexBy,
|
|
346
|
+
schema: this.wrappedIndexedType,
|
|
347
|
+
nested: {
|
|
348
|
+
match: (obj: any): obj is types.IDocumentStore<any> =>
|
|
349
|
+
obj instanceof this.dbType,
|
|
350
|
+
query: async (obj: types.IDocumentStore<any>, query) =>
|
|
351
|
+
obj.index.search(query),
|
|
352
|
+
},
|
|
353
|
+
/* maxBatchSize: MAX_BATCH_SIZE */
|
|
354
|
+
})) || new HashmapIndex<IDocumentWithContext<I>>();
|
|
250
355
|
|
|
251
|
-
await this.engine.init({
|
|
252
|
-
indexBy: this.indexBy,
|
|
253
|
-
nested: {
|
|
254
|
-
match: (obj: any): obj is types.IDocumentStore<any> =>
|
|
255
|
-
obj instanceof this.dbType,
|
|
256
|
-
query: async (obj: types.IDocumentStore<any>, query) =>
|
|
257
|
-
obj.index.search(query)
|
|
258
|
-
},
|
|
259
|
-
maxBatchSize: MAX_BATCH_SIZE
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
await this.engine.start?.();
|
|
263
356
|
await this._query.open({
|
|
264
357
|
topic: sha256Base64Sync(
|
|
265
|
-
concat([this._log.log.id, fromString("/document")])
|
|
358
|
+
concat([this._log.log.id, fromString("/document")]),
|
|
266
359
|
),
|
|
267
360
|
responseHandler: async (query, ctx) => {
|
|
268
361
|
if (!ctx.from) {
|
|
@@ -272,71 +365,84 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
272
365
|
|
|
273
366
|
if (
|
|
274
367
|
properties.canSearch &&
|
|
275
|
-
(query instanceof
|
|
276
|
-
query instanceof
|
|
368
|
+
(query instanceof indexerTypes.SearchRequest ||
|
|
369
|
+
query instanceof indexerTypes.CollectNextRequest) &&
|
|
277
370
|
!(await properties.canSearch(
|
|
278
|
-
query as
|
|
279
|
-
|
|
371
|
+
query as
|
|
372
|
+
| indexerTypes.SearchRequest
|
|
373
|
+
| indexerTypes.CollectNextRequest,
|
|
374
|
+
ctx.from,
|
|
280
375
|
))
|
|
281
376
|
) {
|
|
282
377
|
return new types.NoAccess();
|
|
283
378
|
}
|
|
284
379
|
|
|
285
|
-
if (query instanceof
|
|
380
|
+
if (query instanceof indexerTypes.CloseIteratorRequest) {
|
|
286
381
|
this.processCloseIteratorRequest(query, ctx.from);
|
|
287
382
|
} else {
|
|
288
383
|
const results = await this.processQuery(
|
|
289
384
|
query as
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
385
|
+
| indexerTypes.SearchRequest
|
|
386
|
+
| indexerTypes.SearchRequest
|
|
387
|
+
| indexerTypes.CollectNextRequest,
|
|
293
388
|
ctx.from,
|
|
389
|
+
false,
|
|
294
390
|
{
|
|
295
|
-
canRead: properties.canRead
|
|
296
|
-
}
|
|
391
|
+
canRead: properties.canRead,
|
|
392
|
+
},
|
|
297
393
|
);
|
|
298
394
|
|
|
299
395
|
return new types.Results({
|
|
300
396
|
// Even if results might have length 0, respond, because then we now at least there are no matching results
|
|
301
397
|
results: results.results,
|
|
302
|
-
kept: results.kept
|
|
398
|
+
kept: results.kept,
|
|
303
399
|
});
|
|
304
400
|
}
|
|
305
401
|
},
|
|
306
402
|
responseType: types.AbstractSearchResult,
|
|
307
|
-
queryType:
|
|
403
|
+
queryType: indexerTypes.AbstractSearchRequest,
|
|
308
404
|
});
|
|
309
405
|
}
|
|
310
406
|
|
|
407
|
+
getPending(cursorId: string): number | undefined {
|
|
408
|
+
const queue = this._resultQueue.get(cursorId);
|
|
409
|
+
if (queue) {
|
|
410
|
+
return queue.queue.length + queue.keptInIndex;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return this.index.getPending(cursorId);
|
|
414
|
+
}
|
|
415
|
+
|
|
311
416
|
async close(from?: Program): Promise<boolean> {
|
|
312
417
|
const closed = await super.close(from);
|
|
313
418
|
if (closed) {
|
|
314
|
-
await this.
|
|
419
|
+
await this.index.stop?.();
|
|
315
420
|
}
|
|
316
421
|
return closed;
|
|
317
422
|
}
|
|
318
423
|
|
|
319
424
|
async drop(from?: Program): Promise<boolean> {
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
await this.
|
|
425
|
+
const dropped = await super.drop(from);
|
|
426
|
+
if (dropped) {
|
|
427
|
+
await this.index.drop?.();
|
|
428
|
+
await this.index.stop?.();
|
|
323
429
|
}
|
|
324
|
-
return
|
|
430
|
+
return dropped;
|
|
325
431
|
}
|
|
326
432
|
|
|
327
433
|
public async get(
|
|
328
|
-
key:
|
|
329
|
-
options?: QueryOptions<T
|
|
434
|
+
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
435
|
+
options?: QueryOptions<T>,
|
|
330
436
|
): Promise<T | undefined> {
|
|
331
437
|
return (
|
|
332
438
|
await this.getDetailed(
|
|
333
|
-
key instanceof
|
|
334
|
-
options
|
|
439
|
+
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key),
|
|
440
|
+
options,
|
|
335
441
|
)
|
|
336
442
|
)?.[0]?.results[0]?.value;
|
|
337
443
|
}
|
|
338
444
|
|
|
339
|
-
public async put(value: T, entry: Entry<Operation>, id:
|
|
445
|
+
public async put(value: T, entry: Entry<Operation>, id: indexerTypes.IdKey) {
|
|
340
446
|
const idString = id.primitive;
|
|
341
447
|
if (this._isProgramValues) {
|
|
342
448
|
this._resolverProgramCache!.set(idString, value);
|
|
@@ -344,82 +450,94 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
344
450
|
this._resolverCache.add(idString, value);
|
|
345
451
|
}
|
|
346
452
|
|
|
453
|
+
const existing = await this.index.get(id);
|
|
347
454
|
const context = new types.Context({
|
|
348
455
|
created:
|
|
349
|
-
|
|
456
|
+
existing?.value.__context.created ||
|
|
350
457
|
entry.meta.clock.timestamp.wallTime,
|
|
351
458
|
modified: entry.meta.clock.timestamp.wallTime,
|
|
352
459
|
head: entry.hash,
|
|
353
|
-
gid: entry.gid
|
|
460
|
+
gid: entry.gid,
|
|
461
|
+
size: entry.payloadByteLength,
|
|
354
462
|
});
|
|
355
463
|
|
|
356
|
-
const valueToIndex = await this.
|
|
357
|
-
this.
|
|
358
|
-
|
|
359
|
-
indexed: valueToIndex,
|
|
464
|
+
const valueToIndex = await this.transformer(value, context);
|
|
465
|
+
const wrappedValueToIndex = new this.wrappedIndexedType(
|
|
466
|
+
valueToIndex as I,
|
|
360
467
|
context,
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
valueToIndex === value || value instanceof Program
|
|
364
|
-
? { value }
|
|
365
|
-
: undefined */
|
|
366
|
-
});
|
|
468
|
+
);
|
|
469
|
+
await this.index.put(wrappedValueToIndex);
|
|
367
470
|
}
|
|
368
471
|
|
|
369
|
-
public del(key:
|
|
370
|
-
const keyObject = types.toId(key);
|
|
472
|
+
public del(key: indexerTypes.IdKey) {
|
|
371
473
|
if (this._isProgramValues) {
|
|
372
|
-
this._resolverProgramCache!.delete(key);
|
|
474
|
+
this._resolverProgramCache!.delete(key.primitive);
|
|
373
475
|
} else {
|
|
374
|
-
this._resolverCache.del(key);
|
|
476
|
+
this._resolverCache.del(key.primitive);
|
|
375
477
|
}
|
|
376
|
-
return this.
|
|
478
|
+
return this.index.del(
|
|
479
|
+
new indexerTypes.DeleteRequest({
|
|
480
|
+
query: [indexerTypes.getMatcher(this.indexBy, key.key)],
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
377
483
|
}
|
|
378
484
|
|
|
379
485
|
public async getDetailed(
|
|
380
|
-
key:
|
|
381
|
-
options?: QueryOptions<T
|
|
486
|
+
key: indexerTypes.IdKey | indexerTypes.IdPrimitive,
|
|
487
|
+
options?: QueryOptions<T>,
|
|
382
488
|
): Promise<types.Results<T>[] | undefined> {
|
|
383
489
|
let results: types.Results<T>[] | undefined;
|
|
384
490
|
if (key instanceof Uint8Array) {
|
|
385
491
|
results = await this.queryDetailed(
|
|
386
|
-
new
|
|
492
|
+
new indexerTypes.SearchRequest({
|
|
387
493
|
query: [
|
|
388
|
-
new
|
|
389
|
-
]
|
|
494
|
+
new indexerTypes.ByteMatchQuery({ key: this.indexBy, value: key }),
|
|
495
|
+
],
|
|
390
496
|
}),
|
|
391
|
-
options
|
|
497
|
+
options,
|
|
392
498
|
);
|
|
393
499
|
} else {
|
|
394
|
-
const indexableKey =
|
|
500
|
+
const indexableKey = indexerTypes.toIdeable(key);
|
|
395
501
|
|
|
396
502
|
if (
|
|
397
503
|
typeof indexableKey === "number" ||
|
|
398
504
|
typeof indexableKey === "bigint"
|
|
399
505
|
) {
|
|
400
506
|
results = await this.queryDetailed(
|
|
401
|
-
new
|
|
507
|
+
new indexerTypes.SearchRequest({
|
|
402
508
|
query: [
|
|
403
|
-
new
|
|
404
|
-
key: this.
|
|
405
|
-
compare:
|
|
406
|
-
value: indexableKey
|
|
407
|
-
})
|
|
408
|
-
]
|
|
509
|
+
new indexerTypes.IntegerCompare({
|
|
510
|
+
key: this.indexBy,
|
|
511
|
+
compare: indexerTypes.Compare.Equal,
|
|
512
|
+
value: indexableKey,
|
|
513
|
+
}),
|
|
514
|
+
],
|
|
409
515
|
}),
|
|
410
|
-
options
|
|
516
|
+
options,
|
|
411
517
|
);
|
|
412
|
-
} else {
|
|
518
|
+
} else if (typeof indexableKey === "string") {
|
|
519
|
+
results = await this.queryDetailed(
|
|
520
|
+
new indexerTypes.SearchRequest({
|
|
521
|
+
query: [
|
|
522
|
+
new indexerTypes.StringMatch({
|
|
523
|
+
key: this.indexBy,
|
|
524
|
+
value: indexableKey,
|
|
525
|
+
}),
|
|
526
|
+
],
|
|
527
|
+
}),
|
|
528
|
+
options,
|
|
529
|
+
);
|
|
530
|
+
} else if (indexableKey instanceof Uint8Array) {
|
|
413
531
|
results = await this.queryDetailed(
|
|
414
|
-
new
|
|
532
|
+
new indexerTypes.SearchRequest({
|
|
415
533
|
query: [
|
|
416
|
-
new
|
|
417
|
-
key: this.
|
|
418
|
-
value: indexableKey
|
|
419
|
-
})
|
|
420
|
-
]
|
|
534
|
+
new indexerTypes.ByteMatchQuery({
|
|
535
|
+
key: this.indexBy,
|
|
536
|
+
value: indexableKey,
|
|
537
|
+
}),
|
|
538
|
+
],
|
|
421
539
|
}),
|
|
422
|
-
options
|
|
540
|
+
options,
|
|
423
541
|
);
|
|
424
542
|
}
|
|
425
543
|
}
|
|
@@ -428,11 +546,11 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
428
546
|
}
|
|
429
547
|
|
|
430
548
|
getSize(): Promise<number> | number {
|
|
431
|
-
return this.
|
|
549
|
+
return this.index.getSize();
|
|
432
550
|
}
|
|
433
551
|
|
|
434
552
|
private async resolveDocument(
|
|
435
|
-
value:
|
|
553
|
+
value: indexerTypes.IndexedResult<IDocumentWithContext<I>>,
|
|
436
554
|
): Promise<{ value: T } | undefined> {
|
|
437
555
|
const cached =
|
|
438
556
|
this._resolverCache.get(value.id.primitive) ||
|
|
@@ -441,10 +559,17 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
441
559
|
return { value: cached };
|
|
442
560
|
}
|
|
443
561
|
|
|
444
|
-
if (
|
|
445
|
-
|
|
562
|
+
if (this.indexedTypeIsDocumentType) {
|
|
563
|
+
// cast value to T, i.e. convert the class but keep all properties except the __context
|
|
564
|
+
const obj = Object.assign(
|
|
565
|
+
Object.create(this.documentType.prototype),
|
|
566
|
+
value.value,
|
|
567
|
+
);
|
|
568
|
+
delete obj.__context;
|
|
569
|
+
return { value: obj as T };
|
|
446
570
|
}
|
|
447
|
-
|
|
571
|
+
|
|
572
|
+
const head = await this._log.log.get(value.value.__context.head);
|
|
448
573
|
if (!head) {
|
|
449
574
|
return undefined; // we could end up here if we recently pruned the document and other peers never persisted the entry
|
|
450
575
|
// TODO update changes in index before removing entries from log entry storage
|
|
@@ -452,36 +577,80 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
452
577
|
const payloadValue = await head.getPayloadValue();
|
|
453
578
|
if (payloadValue instanceof PutOperation) {
|
|
454
579
|
return {
|
|
455
|
-
value: this.valueEncoding.decoder(payloadValue.data)
|
|
580
|
+
value: this.valueEncoding.decoder(payloadValue.data),
|
|
456
581
|
/* size: payloadValue.data.byteLength */
|
|
457
582
|
};
|
|
458
583
|
}
|
|
459
584
|
|
|
460
585
|
throw new Error(
|
|
461
586
|
"Unexpected value type when getting document: " +
|
|
462
|
-
|
|
587
|
+
payloadValue?.constructor?.name || typeof payloadValue,
|
|
463
588
|
);
|
|
464
589
|
}
|
|
465
590
|
|
|
466
591
|
async processQuery(
|
|
467
|
-
query:
|
|
592
|
+
query: indexerTypes.SearchRequest | indexerTypes.CollectNextRequest,
|
|
468
593
|
from: PublicSignKey,
|
|
594
|
+
isLocal: boolean,
|
|
469
595
|
options?: {
|
|
470
596
|
canRead?: CanRead<T>;
|
|
471
|
-
}
|
|
597
|
+
},
|
|
472
598
|
): Promise<types.Results<T>> {
|
|
473
599
|
// We do special case for querying the id as we can do it faster than iterating
|
|
474
600
|
|
|
475
|
-
let
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
601
|
+
let prevQueued = isLocal
|
|
602
|
+
? undefined
|
|
603
|
+
: this._resultQueue.get(query.idString);
|
|
604
|
+
if (prevQueued && !from.equals(prevQueued.from)) {
|
|
605
|
+
throw new Error("Different from in queued results");
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
let indexedResult:
|
|
609
|
+
| indexerTypes.IndexedResults<IDocumentWithContext<I>>
|
|
610
|
+
| undefined = undefined;
|
|
611
|
+
if (query instanceof indexerTypes.SearchRequest) {
|
|
612
|
+
indexedResult = await this.index.query(query);
|
|
613
|
+
} else if (query instanceof indexerTypes.CollectNextRequest) {
|
|
614
|
+
indexedResult =
|
|
615
|
+
prevQueued?.keptInIndex === 0
|
|
616
|
+
? { kept: 0, results: [] }
|
|
617
|
+
: await this.index.next(query);
|
|
480
618
|
} else {
|
|
481
619
|
throw new Error("Unsupported");
|
|
482
620
|
}
|
|
483
621
|
const filteredResults: types.ResultWithSource<T>[] = [];
|
|
484
|
-
|
|
622
|
+
let resultSize = 0;
|
|
623
|
+
|
|
624
|
+
let toIterate = prevQueued
|
|
625
|
+
? [...prevQueued.queue, ...indexedResult.results]
|
|
626
|
+
: indexedResult.results;
|
|
627
|
+
|
|
628
|
+
if (prevQueued) {
|
|
629
|
+
this._resultQueue.delete(query.idString);
|
|
630
|
+
prevQueued = undefined;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (!isLocal) {
|
|
634
|
+
prevQueued = {
|
|
635
|
+
from,
|
|
636
|
+
queue: [],
|
|
637
|
+
timeout: setTimeout(() => {
|
|
638
|
+
this._resultQueue.delete(query.idString);
|
|
639
|
+
}, 6e4),
|
|
640
|
+
keptInIndex: indexedResult.kept,
|
|
641
|
+
};
|
|
642
|
+
this._resultQueue.set(query.idString, prevQueued);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
for (const result of toIterate) {
|
|
646
|
+
if (!isLocal) {
|
|
647
|
+
resultSize += result.value.__context.size;
|
|
648
|
+
if (resultSize > MAX_BATCH_SIZE) {
|
|
649
|
+
prevQueued!.queue.push(result);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
485
654
|
const value = await this.resolveDocument(result);
|
|
486
655
|
if (
|
|
487
656
|
!value ||
|
|
@@ -491,25 +660,48 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
491
660
|
}
|
|
492
661
|
filteredResults.push(
|
|
493
662
|
new types.ResultWithSource({
|
|
494
|
-
context: result.
|
|
663
|
+
context: result.value.__context,
|
|
495
664
|
value: value.value,
|
|
496
665
|
source: serialize(value.value),
|
|
497
|
-
indexed: result.
|
|
498
|
-
})
|
|
666
|
+
indexed: result.value,
|
|
667
|
+
}),
|
|
499
668
|
);
|
|
500
669
|
}
|
|
501
670
|
const results: types.Results<T> = new types.Results({
|
|
502
671
|
results: filteredResults,
|
|
503
|
-
kept: BigInt(indexedResult.kept)
|
|
672
|
+
kept: BigInt(indexedResult.kept + (prevQueued?.queue.length || 0)),
|
|
504
673
|
});
|
|
674
|
+
|
|
675
|
+
if (!isLocal && results.kept === 0n) {
|
|
676
|
+
this.clearResultsQueue(query);
|
|
677
|
+
}
|
|
678
|
+
|
|
505
679
|
return results;
|
|
506
680
|
}
|
|
507
681
|
|
|
682
|
+
clearResultsQueue(
|
|
683
|
+
query:
|
|
684
|
+
| indexerTypes.SearchRequest
|
|
685
|
+
| indexerTypes.CollectNextRequest
|
|
686
|
+
| indexerTypes.CloseIteratorRequest,
|
|
687
|
+
) {
|
|
688
|
+
const queue = this._resultQueue.get(query.idString);
|
|
689
|
+
if (queue) {
|
|
690
|
+
clearTimeout(queue.timeout);
|
|
691
|
+
this._resultQueue.delete(query.idString);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
508
694
|
async processCloseIteratorRequest(
|
|
509
|
-
query:
|
|
510
|
-
publicKey: PublicSignKey
|
|
695
|
+
query: indexerTypes.CloseIteratorRequest,
|
|
696
|
+
publicKey: PublicSignKey,
|
|
511
697
|
): Promise<void> {
|
|
512
|
-
|
|
698
|
+
const queueData = this._resultQueue.get(query.idString);
|
|
699
|
+
if (queueData && !queueData.from.equals(publicKey)) {
|
|
700
|
+
logger.info("Ignoring close iterator request from different peer");
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
this.clearResultsQueue(query);
|
|
704
|
+
return this.index.close(query);
|
|
513
705
|
}
|
|
514
706
|
|
|
515
707
|
/**
|
|
@@ -519,10 +711,10 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
519
711
|
* @returns
|
|
520
712
|
*/
|
|
521
713
|
public async queryDetailed(
|
|
522
|
-
queryRequest:
|
|
523
|
-
options?: QueryDetailedOptions<T
|
|
714
|
+
queryRequest: indexerTypes.SearchRequest,
|
|
715
|
+
options?: QueryDetailedOptions<T>,
|
|
524
716
|
): Promise<types.Results<T>[]> {
|
|
525
|
-
const local = typeof options?.local
|
|
717
|
+
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
526
718
|
let remote: RemoteQueryOptions<types.AbstractSearchResult<T>> | undefined =
|
|
527
719
|
undefined;
|
|
528
720
|
if (typeof options?.remote === "boolean") {
|
|
@@ -544,7 +736,7 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
544
736
|
const promises: Promise<types.Results<T>[] | undefined>[] = [];
|
|
545
737
|
if (!local && !remote) {
|
|
546
738
|
throw new Error(
|
|
547
|
-
"Expecting either 'options.remote' or 'options.local' to be true"
|
|
739
|
+
"Expecting either 'options.remote' or 'options.local' to be true",
|
|
548
740
|
);
|
|
549
741
|
}
|
|
550
742
|
const allResults: types.Results<T>[] = [];
|
|
@@ -552,7 +744,8 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
552
744
|
if (local) {
|
|
553
745
|
const results = await this.processQuery(
|
|
554
746
|
queryRequest,
|
|
555
|
-
this.node.identity.publicKey
|
|
747
|
+
this.node.identity.publicKey,
|
|
748
|
+
true,
|
|
556
749
|
);
|
|
557
750
|
if (results.results.length > 0) {
|
|
558
751
|
options?.onResponse &&
|
|
@@ -563,26 +756,27 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
563
756
|
|
|
564
757
|
if (remote) {
|
|
565
758
|
const replicatorGroups = await this._log.getReplicatorUnion(
|
|
566
|
-
remote.minAge
|
|
759
|
+
remote.minAge,
|
|
567
760
|
);
|
|
761
|
+
|
|
568
762
|
if (replicatorGroups) {
|
|
569
763
|
const groupHashes: string[][] = replicatorGroups.map((x) => [x]);
|
|
570
764
|
const fn = async () => {
|
|
571
765
|
const rs: types.Results<T>[] = [];
|
|
572
766
|
const responseHandler = async (
|
|
573
|
-
results: RPCResponse<types.AbstractSearchResult<T>>[]
|
|
767
|
+
results: RPCResponse<types.AbstractSearchResult<T>>[],
|
|
574
768
|
) => {
|
|
575
769
|
for (const r of await introduceEntries(
|
|
576
770
|
results,
|
|
577
|
-
this.
|
|
771
|
+
this.documentType,
|
|
578
772
|
this._sync,
|
|
579
|
-
options
|
|
773
|
+
options,
|
|
580
774
|
)) {
|
|
581
775
|
rs.push(r.response);
|
|
582
776
|
}
|
|
583
777
|
};
|
|
584
778
|
try {
|
|
585
|
-
if (queryRequest instanceof
|
|
779
|
+
if (queryRequest instanceof indexerTypes.CloseIteratorRequest) {
|
|
586
780
|
// don't wait for responses
|
|
587
781
|
await this._query.request(queryRequest, { mode: remote!.mode });
|
|
588
782
|
} else {
|
|
@@ -591,7 +785,7 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
591
785
|
groupHashes,
|
|
592
786
|
queryRequest,
|
|
593
787
|
responseHandler,
|
|
594
|
-
remote
|
|
788
|
+
remote,
|
|
595
789
|
);
|
|
596
790
|
}
|
|
597
791
|
} catch (error) {
|
|
@@ -639,11 +833,11 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
639
833
|
* @returns
|
|
640
834
|
*/
|
|
641
835
|
public async search(
|
|
642
|
-
queryRequest:
|
|
643
|
-
options?: SearchOptions<T
|
|
836
|
+
queryRequest: indexerTypes.SearchRequest,
|
|
837
|
+
options?: SearchOptions<T>,
|
|
644
838
|
): Promise<T[]> {
|
|
645
839
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
646
|
-
queryRequest.fetch =
|
|
840
|
+
queryRequest.fetch = queryRequest.fetch ?? 0xffffffff;
|
|
647
841
|
|
|
648
842
|
// So that the iterator is pre-fetching the right amount of entries
|
|
649
843
|
const iterator = this.iterate(queryRequest, options);
|
|
@@ -656,7 +850,7 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
656
850
|
) {
|
|
657
851
|
// We might need to pull .next multiple time due to data message size limitations
|
|
658
852
|
for (const result of await iterator.next(
|
|
659
|
-
queryRequest.fetch - allResults.length
|
|
853
|
+
queryRequest.fetch - allResults.length,
|
|
660
854
|
)) {
|
|
661
855
|
allResults.push(result);
|
|
662
856
|
}
|
|
@@ -675,8 +869,8 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
675
869
|
* @returns
|
|
676
870
|
*/
|
|
677
871
|
public iterate(
|
|
678
|
-
queryRequest:
|
|
679
|
-
options?: QueryOptions<T
|
|
872
|
+
queryRequest: indexerTypes.SearchRequest,
|
|
873
|
+
options?: QueryOptions<T>,
|
|
680
874
|
): ResultsIterator<T> {
|
|
681
875
|
let fetchPromise: Promise<any> | undefined = undefined;
|
|
682
876
|
const peerBufferMap: Map<
|
|
@@ -728,9 +922,9 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
728
922
|
const buffer: BufferedResult<T>[] = [];
|
|
729
923
|
|
|
730
924
|
for (const result of results.results) {
|
|
731
|
-
const indexKey =
|
|
732
|
-
this.indexByResolver(result.value)
|
|
733
|
-
);
|
|
925
|
+
const indexKey = indexerTypes.toId(
|
|
926
|
+
this.indexByResolver(result.value),
|
|
927
|
+
).primitive;
|
|
734
928
|
if (visited.has(indexKey)) {
|
|
735
929
|
continue;
|
|
736
930
|
}
|
|
@@ -738,25 +932,29 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
738
932
|
buffer.push({
|
|
739
933
|
value: result.value,
|
|
740
934
|
context: result.context,
|
|
741
|
-
from
|
|
935
|
+
from,
|
|
742
936
|
indexed:
|
|
743
937
|
result.indexed ||
|
|
744
|
-
(await this.
|
|
938
|
+
(await this.transformer(result.value, result.context)),
|
|
745
939
|
});
|
|
746
940
|
}
|
|
747
941
|
|
|
748
942
|
peerBufferMap.set(from.hashcode(), {
|
|
749
943
|
buffer,
|
|
750
|
-
kept: Number(response.kept)
|
|
944
|
+
kept: Number(response.kept),
|
|
751
945
|
});
|
|
752
946
|
} else {
|
|
753
947
|
throw new Error(
|
|
754
|
-
"Unsupported result type: " + response?.constructor?.name
|
|
948
|
+
"Unsupported result type: " + response?.constructor?.name,
|
|
755
949
|
);
|
|
756
950
|
}
|
|
757
|
-
}
|
|
951
|
+
},
|
|
758
952
|
});
|
|
759
953
|
|
|
954
|
+
if (done) {
|
|
955
|
+
this.clearResultsQueue(queryRequest);
|
|
956
|
+
}
|
|
957
|
+
|
|
760
958
|
return done;
|
|
761
959
|
};
|
|
762
960
|
|
|
@@ -787,14 +985,18 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
787
985
|
|
|
788
986
|
// TODO buffer more than deleted?
|
|
789
987
|
// TODO batch to multiple 'to's
|
|
790
|
-
const collectRequest = new
|
|
988
|
+
const collectRequest = new indexerTypes.CollectNextRequest({
|
|
791
989
|
id: queryRequest.id,
|
|
792
|
-
amount: n - buffer.buffer.length
|
|
990
|
+
amount: n - buffer.buffer.length,
|
|
793
991
|
});
|
|
794
992
|
// Fetch locally?
|
|
795
993
|
if (peer === this.node.identity.publicKey.hashcode()) {
|
|
796
994
|
promises.push(
|
|
797
|
-
this.processQuery(
|
|
995
|
+
this.processQuery(
|
|
996
|
+
collectRequest,
|
|
997
|
+
this.node.identity.publicKey,
|
|
998
|
+
true,
|
|
999
|
+
)
|
|
798
1000
|
.then(async (results) => {
|
|
799
1001
|
resultsLeft += Number(results.kept);
|
|
800
1002
|
|
|
@@ -812,13 +1014,15 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
812
1014
|
for (const result of results.results) {
|
|
813
1015
|
if (
|
|
814
1016
|
visited.has(
|
|
815
|
-
|
|
1017
|
+
indexerTypes.toId(this.indexByResolver(result.value))
|
|
1018
|
+
.primitive,
|
|
816
1019
|
)
|
|
817
1020
|
) {
|
|
818
1021
|
continue;
|
|
819
1022
|
}
|
|
820
1023
|
visited.add(
|
|
821
|
-
|
|
1024
|
+
indexerTypes.toId(this.indexByResolver(result.value))
|
|
1025
|
+
.primitive,
|
|
822
1026
|
);
|
|
823
1027
|
peerBuffer.buffer.push({
|
|
824
1028
|
value: result.value,
|
|
@@ -826,17 +1030,20 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
826
1030
|
from: this.node.identity.publicKey,
|
|
827
1031
|
indexed:
|
|
828
1032
|
result.indexed ||
|
|
829
|
-
(await this.
|
|
1033
|
+
(await this.transformer(
|
|
1034
|
+
result.value,
|
|
1035
|
+
result.context,
|
|
1036
|
+
)),
|
|
830
1037
|
});
|
|
831
1038
|
}
|
|
832
1039
|
}
|
|
833
1040
|
})
|
|
834
1041
|
.catch((e) => {
|
|
835
1042
|
logger.error(
|
|
836
|
-
"Failed to collect sorted results from self. " + e?.message
|
|
1043
|
+
"Failed to collect sorted results from self. " + e?.message,
|
|
837
1044
|
);
|
|
838
1045
|
peerBufferMap.delete(peer);
|
|
839
|
-
})
|
|
1046
|
+
}),
|
|
840
1047
|
);
|
|
841
1048
|
} else {
|
|
842
1049
|
// Fetch remotely
|
|
@@ -846,10 +1053,15 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
846
1053
|
...options,
|
|
847
1054
|
signal: controller.signal,
|
|
848
1055
|
priority: 1,
|
|
849
|
-
mode: new SilentDelivery({ to: [peer], redundancy: 1 })
|
|
1056
|
+
mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
|
|
850
1057
|
})
|
|
851
1058
|
.then((response) =>
|
|
852
|
-
introduceEntries(
|
|
1059
|
+
introduceEntries(
|
|
1060
|
+
response,
|
|
1061
|
+
this.documentType,
|
|
1062
|
+
this._sync,
|
|
1063
|
+
options,
|
|
1064
|
+
)
|
|
853
1065
|
.then((responses) => {
|
|
854
1066
|
responses.map((response) => {
|
|
855
1067
|
resultsLeft += Number(response.response.kept);
|
|
@@ -871,23 +1083,26 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
871
1083
|
for (const result of response.response.results) {
|
|
872
1084
|
if (
|
|
873
1085
|
visited.has(
|
|
874
|
-
|
|
875
|
-
this.indexByResolver(result.value)
|
|
876
|
-
)
|
|
1086
|
+
indexerTypes.toId(
|
|
1087
|
+
this.indexByResolver(result.value),
|
|
1088
|
+
).primitive,
|
|
877
1089
|
)
|
|
878
1090
|
) {
|
|
879
1091
|
continue;
|
|
880
1092
|
}
|
|
881
1093
|
visited.add(
|
|
882
|
-
|
|
883
|
-
this.indexByResolver(result.value)
|
|
884
|
-
)
|
|
1094
|
+
indexerTypes.toId(
|
|
1095
|
+
this.indexByResolver(result.value),
|
|
1096
|
+
).primitive,
|
|
885
1097
|
);
|
|
886
1098
|
peerBuffer.buffer.push({
|
|
887
1099
|
value: result.value,
|
|
888
1100
|
context: result.context,
|
|
889
1101
|
from: response.from!,
|
|
890
|
-
indexed: this.
|
|
1102
|
+
indexed: this.transformer(
|
|
1103
|
+
result.value,
|
|
1104
|
+
result.context,
|
|
1105
|
+
),
|
|
891
1106
|
});
|
|
892
1107
|
}
|
|
893
1108
|
}
|
|
@@ -896,13 +1111,13 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
896
1111
|
.catch((e) => {
|
|
897
1112
|
logger.error(
|
|
898
1113
|
"Failed to collect sorted results from: " +
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1114
|
+
peer +
|
|
1115
|
+
". " +
|
|
1116
|
+
e?.message,
|
|
902
1117
|
);
|
|
903
1118
|
peerBufferMap.delete(peer);
|
|
904
|
-
})
|
|
905
|
-
)
|
|
1119
|
+
}),
|
|
1120
|
+
),
|
|
906
1121
|
);
|
|
907
1122
|
}
|
|
908
1123
|
} else {
|
|
@@ -927,9 +1142,13 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
927
1142
|
const fetchedAll = await fetchAtLeast(n);
|
|
928
1143
|
|
|
929
1144
|
// get n next top entries, shift and pull more results
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
1145
|
+
const peerBuffersArr = peerBuffers();
|
|
1146
|
+
const results = peerBuffersArr.sort((a, b) =>
|
|
1147
|
+
indexerTypes.extractSortCompare(
|
|
1148
|
+
a.indexed,
|
|
1149
|
+
b.indexed,
|
|
1150
|
+
queryRequest.sort,
|
|
1151
|
+
),
|
|
933
1152
|
);
|
|
934
1153
|
|
|
935
1154
|
const pendingMoreResults = n < results.length;
|
|
@@ -942,24 +1161,25 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
942
1161
|
logger.error("Unexpected empty result buffer");
|
|
943
1162
|
continue;
|
|
944
1163
|
}
|
|
945
|
-
const idx = arr.buffer.findIndex((x) => x.value
|
|
1164
|
+
const idx = arr.buffer.findIndex((x) => x.value === result.value);
|
|
946
1165
|
if (idx >= 0) {
|
|
947
1166
|
arr.buffer.splice(idx, 1);
|
|
948
1167
|
}
|
|
949
1168
|
}
|
|
950
1169
|
|
|
951
1170
|
done = fetchedAll && !pendingMoreResults;
|
|
1171
|
+
|
|
952
1172
|
return dedup(
|
|
953
1173
|
batch.map((x) => x.value),
|
|
954
|
-
this.indexByResolver
|
|
1174
|
+
this.indexByResolver,
|
|
955
1175
|
);
|
|
956
1176
|
};
|
|
957
1177
|
|
|
958
1178
|
const close = async () => {
|
|
959
1179
|
controller.abort(new AbortError("Iterator closed"));
|
|
960
1180
|
|
|
961
|
-
const closeRequest = new
|
|
962
|
-
id: queryRequest.id
|
|
1181
|
+
const closeRequest = new indexerTypes.CloseIteratorRequest({
|
|
1182
|
+
id: queryRequest.id,
|
|
963
1183
|
});
|
|
964
1184
|
const promises: Promise<any>[] = [];
|
|
965
1185
|
for (const [peer, buffer] of peerBufferMap) {
|
|
@@ -972,16 +1192,16 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
972
1192
|
promises.push(
|
|
973
1193
|
this.processCloseIteratorRequest(
|
|
974
1194
|
closeRequest,
|
|
975
|
-
this.node.identity.publicKey
|
|
976
|
-
)
|
|
1195
|
+
this.node.identity.publicKey,
|
|
1196
|
+
),
|
|
977
1197
|
);
|
|
978
1198
|
} else {
|
|
979
1199
|
// Close remote
|
|
980
1200
|
promises.push(
|
|
981
1201
|
this._query.send(closeRequest, {
|
|
982
1202
|
...options,
|
|
983
|
-
mode: new SilentDelivery({ to: [peer], redundancy: 1 })
|
|
984
|
-
})
|
|
1203
|
+
mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
|
|
1204
|
+
}),
|
|
985
1205
|
);
|
|
986
1206
|
}
|
|
987
1207
|
}
|
|
@@ -991,7 +1211,7 @@ export class DocumentIndex<T> extends Program<OpenOptions<T>> {
|
|
|
991
1211
|
return {
|
|
992
1212
|
close,
|
|
993
1213
|
next,
|
|
994
|
-
done: () => done
|
|
1214
|
+
done: () => done,
|
|
995
1215
|
};
|
|
996
1216
|
}
|
|
997
1217
|
}
|