@peerbit/document 6.0.6 → 6.0.7-218a5bb

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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/dist/benchmark/index.d.ts +2 -0
  3. package/dist/benchmark/index.d.ts.map +1 -0
  4. package/dist/benchmark/index.js +126 -0
  5. package/dist/benchmark/index.js.map +1 -0
  6. package/dist/benchmark/replication.d.ts +2 -0
  7. package/dist/benchmark/replication.d.ts.map +1 -0
  8. package/dist/benchmark/replication.js +174 -0
  9. package/dist/benchmark/replication.js.map +1 -0
  10. package/dist/src/constants.d.ts +2 -0
  11. package/dist/src/constants.d.ts.map +1 -0
  12. package/dist/src/constants.js +2 -0
  13. package/dist/src/constants.js.map +1 -0
  14. package/dist/src/index.d.ts +5 -0
  15. package/dist/src/index.d.ts.map +1 -0
  16. package/dist/src/index.js +5 -0
  17. package/dist/src/index.js.map +1 -0
  18. package/dist/src/program.d.ts +90 -0
  19. package/dist/src/program.d.ts.map +1 -0
  20. package/{lib/esm/document-store.js → dist/src/program.js} +141 -109
  21. package/dist/src/program.js.map +1 -0
  22. package/dist/src/search.d.ts +118 -0
  23. package/dist/src/search.d.ts.map +1 -0
  24. package/{lib/esm/document-index.js → dist/src/search.js} +246 -446
  25. package/dist/src/search.js.map +1 -0
  26. package/package.json +69 -43
  27. package/src/constants.ts +1 -0
  28. package/src/index.ts +4 -3
  29. package/src/{document-store.ts → program.ts} +216 -183
  30. package/src/search.ts +997 -0
  31. package/LICENSE +0 -202
  32. package/lib/esm/document-index.d.ts +0 -147
  33. package/lib/esm/document-index.js.map +0 -1
  34. package/lib/esm/document-store.d.ts +0 -72
  35. package/lib/esm/document-store.js.map +0 -1
  36. package/lib/esm/index.d.ts +0 -3
  37. package/lib/esm/index.js +0 -4
  38. package/lib/esm/index.js.map +0 -1
  39. package/lib/esm/query.d.ts +0 -191
  40. package/lib/esm/query.js +0 -615
  41. package/lib/esm/query.js.map +0 -1
  42. package/lib/esm/utils.d.ts +0 -3
  43. package/lib/esm/utils.js +0 -12
  44. package/lib/esm/utils.js.map +0 -1
  45. package/src/document-index.ts +0 -1268
  46. package/src/query.ts +0 -525
  47. package/src/utils.ts +0 -17
@@ -7,69 +7,42 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { field, variant } from "@dao-xyz/borsh";
11
- import { asString } from "./utils.js";
12
- import { BORSH_ENCODING } from "@peerbit/log";
13
- import { equals } from "@peerbit/uint8arrays";
10
+ import { field, serialize, variant } from "@dao-xyz/borsh";
11
+ import { BORSH_ENCODING, Entry } from "@peerbit/log";
14
12
  import { Program } from "@peerbit/program";
15
- import { IntegerCompare, ByteMatchQuery, StringMatch, ResultWithSource, StateFieldQuery, compare, MissingField, StringMatchMethod, LogicalQuery, And, Or, BoolQuery, CollectNextRequest, AbstractSearchRequest, SearchRequest, SortDirection, CloseIteratorRequest, NoAccess, AbstractSearchResult } from "./query.js";
13
+ import * as types from "@peerbit/document-interface";
16
14
  import { RPC, queryAll, MissingResponsesError } from "@peerbit/rpc";
17
- import { Results } from "./query.js";
18
15
  import { logger as loggerFn } from "@peerbit/logger";
19
- import { Cache } from "@peerbit/cache";
20
- import { sha256Base64Sync } from "@peerbit/crypto";
16
+ import { PublicSignKey, sha256Base64Sync } from "@peerbit/crypto";
17
+ import { SharedLog } from "@peerbit/shared-log";
21
18
  import { concat, fromString } from "uint8arrays";
22
19
  import { SilentDelivery } from "@peerbit/stream-interface";
23
20
  import { AbortError } from "@peerbit/time";
21
+ import { Cache } from "@peerbit/cache";
22
+ import { HashmapIndexEngine } from "@peerbit/document-index-simple";
23
+ import { MAX_BATCH_SIZE } from "./constants.js";
24
24
  const logger = loggerFn({ module: "document-index" });
25
- const stringArraysEquals = (a, b) => {
26
- if (a.length !== b.length) {
27
- return false;
28
- }
29
- for (let i = 0; i < a.length; i++) {
30
- if (a[i] !== b[i]) {
31
- return false;
32
- }
33
- }
34
- return true;
35
- };
36
- let Operation = class Operation {
25
+ let Operation = class Operation /* <T> */ {
37
26
  };
38
27
  Operation = __decorate([
39
28
  variant(0)
40
29
  ], Operation);
41
30
  export { Operation };
42
31
  export const BORSH_ENCODING_OPERATION = BORSH_ENCODING(Operation);
43
- let PutOperation = class PutOperation extends Operation {
44
- key;
32
+ /**
33
+ * Put a complete document at a key
34
+ */
35
+ let PutOperation = class PutOperation extends Operation /* <T> */ {
45
36
  data;
46
- _value;
37
+ /* _value?: T; */
47
38
  constructor(props) {
48
39
  super();
49
40
  if (props) {
50
- this.key = props.key;
51
41
  this.data = props.data;
52
- this._value = props.value;
42
+ /* this._value = props.value; */
53
43
  }
54
44
  }
55
- get value() {
56
- if (!this._value) {
57
- throw new Error("Value not decoded, invoke getValue(...) once");
58
- }
59
- return this._value;
60
- }
61
- getValue(encoding) {
62
- if (this._value) {
63
- return this._value;
64
- }
65
- this._value = encoding.decoder(this.data);
66
- return this._value;
67
- }
68
45
  };
69
- __decorate([
70
- field({ type: "string" }),
71
- __metadata("design:type", String)
72
- ], PutOperation.prototype, "key", void 0);
73
46
  __decorate([
74
47
  field({ type: Uint8Array }),
75
48
  __metadata("design:type", Uint8Array)
@@ -92,80 +65,41 @@ export class PutAllOperation<T> extends Operation<T> {
92
65
  }
93
66
  }
94
67
  */
68
+ /**
69
+ * Delete a document at a key
70
+ */
95
71
  let DeleteOperation = class DeleteOperation extends Operation {
96
72
  key;
97
73
  constructor(props) {
98
74
  super();
99
- if (props) {
100
- this.key = props.key;
101
- }
75
+ this.key = props.key;
102
76
  }
103
77
  };
104
78
  __decorate([
105
- field({ type: "string" }),
106
- __metadata("design:type", String)
79
+ field({ type: types.IdKey }),
80
+ __metadata("design:type", types.IdKey)
107
81
  ], DeleteOperation.prototype, "key", void 0);
108
82
  DeleteOperation = __decorate([
109
83
  variant(2),
110
84
  __metadata("design:paramtypes", [Object])
111
85
  ], DeleteOperation);
112
86
  export { DeleteOperation };
113
- const sortCompare = (av, bv) => {
114
- if (typeof av === "string" && typeof bv === "string") {
115
- return av.localeCompare(bv);
116
- }
117
- if (av < bv) {
118
- return -1;
119
- }
120
- else if (av > bv) {
121
- return 1;
122
- }
123
- return 0;
124
- };
125
- const extractFieldValue = (doc, path) => {
126
- for (let i = 0; i < path.length; i++) {
127
- doc = doc[path[i]];
128
- }
129
- return doc;
130
- };
131
- const extractSortCompare = (a, b, sorts) => {
132
- for (const sort of sorts) {
133
- const av = extractFieldValue(a, sort.key);
134
- const bv = extractFieldValue(b, sort.key);
135
- const cmp = sortCompare(av, bv);
136
- if (cmp != 0) {
137
- if (sort.direction === SortDirection.ASC) {
138
- return cmp;
139
- }
140
- else {
141
- return -cmp;
142
- }
143
- }
144
- }
145
- return 0;
146
- };
147
- const resolvedSort = async (arr, index, sorts) => {
148
- await Promise.all(arr.map(async (result) => (result[SORT_TMP_KEY] = await index(result.value.value, result.context))));
149
- arr.sort((a, b) => extractSortCompare(a[SORT_TMP_KEY], b[SORT_TMP_KEY], sorts));
150
- return arr;
151
- };
152
- const SORT_TMP_KEY = "__sort_ref";
153
87
  const introduceEntries = async (responses, type, sync, options) => {
154
88
  const results = [];
155
89
  for (const response of responses) {
156
90
  if (!response.from) {
157
91
  logger.error("Missing from for response");
158
92
  }
159
- if (response.response instanceof Results) {
93
+ if (response.response instanceof types.Results) {
160
94
  response.response.results.forEach((r) => r.init(type));
161
95
  if (typeof options?.remote !== "boolean" && options?.remote?.sync) {
162
96
  await sync(response.response);
163
97
  }
164
98
  options?.onResponse &&
165
- options.onResponse(response.response, response.from); // TODO fix types
99
+ (await options.onResponse(response.response, response.from)); // TODO fix types
166
100
  results.push(response);
167
101
  }
168
- else if (response.response instanceof NoAccess) {
102
+ else if (response.response instanceof types.NoAccess) {
169
103
  logger.error("Search resulted in access error");
170
104
  }
171
105
  else {
@@ -178,7 +112,7 @@ const dedup = (allResult, dedupBy) => {
178
112
  const unique = new Set();
179
113
  const dedup = [];
180
114
  for (const result of allResult) {
181
- const key = asString(dedupBy(result));
115
+ const key = types.toIdeable(dedupBy(result));
182
116
  if (unique.has(key)) {
183
117
  continue;
184
118
  }
@@ -188,73 +122,61 @@ const dedup = (allResult, dedupBy) => {
188
122
  return dedup;
189
123
  };
190
124
  const DEFAULT_INDEX_BY = "id";
191
- /*
192
- if (!(await this.canRead(message.sender))) {
193
- throw new AccessError();
194
- } */
195
- export const MAX_DOCUMENT_SIZE = 5e6;
196
- const getBatchFromResults = (results, wantedSize, maxSize = MAX_DOCUMENT_SIZE) => {
197
- const batch = [];
198
- let size = 0;
199
- for (const result of results) {
200
- batch.push(result);
201
- size += result.value.last.data.length;
202
- if (size > maxSize) {
203
- break;
204
- }
205
- if (wantedSize <= batch.length) {
206
- break;
207
- }
208
- }
209
- results.splice(0, batch.length);
210
- return batch;
211
- };
212
125
  let DocumentIndex = class DocumentIndex extends Program {
213
126
  _query;
127
+ engine;
214
128
  type;
215
129
  dbType;
216
130
  // Index key
217
- _indexBy;
218
- _indexByArr;
219
- // Resolve doc value by index key
131
+ indexBy;
132
+ indexByArr;
220
133
  indexByResolver;
221
- // Indexed (transforms an docuemnt into an obj with fields that ought to be indexed)
222
- _toIndex;
134
+ // Transformation, indexer
135
+ fields;
223
136
  _valueEncoding;
224
137
  _sync;
225
- _index;
226
- _resultsCollectQueue;
227
138
  _log;
139
+ _resolverProgramCache;
140
+ _resolverCache;
141
+ _isProgramValues;
228
142
  constructor(properties) {
229
143
  super();
230
144
  this._query = properties?.query || new RPC();
231
145
  }
232
- get index() {
233
- return this._index;
234
- }
235
146
  get valueEncoding() {
236
147
  return this._valueEncoding;
237
148
  }
238
- get toIndex() {
239
- return this._toIndex;
240
- }
241
149
  async open(properties) {
242
- this._index = new Map();
243
150
  this._log = properties.log;
244
151
  this.type = properties.type;
152
+ // if this.type is a class that extends Program we want to do special functionality
153
+ this._isProgramValues = this.type instanceof Program;
245
154
  this.dbType = properties.dbType;
246
155
  this._sync = properties.sync;
247
- this._toIndex = properties.fields;
248
- this._indexBy = properties.indexBy || DEFAULT_INDEX_BY;
249
- this._indexByArr = Array.isArray(this._indexBy)
250
- ? this._indexBy
251
- : [this._indexBy];
156
+ this.fields = properties.fields;
157
+ this.indexBy = properties.indexBy || DEFAULT_INDEX_BY;
158
+ this.indexByArr = Array.isArray(this.indexBy)
159
+ ? this.indexBy
160
+ : [this.indexBy];
252
161
  this.indexByResolver =
253
- typeof this._indexBy === "string"
254
- ? (obj) => obj[this._indexBy]
255
- : (obj) => extractFieldValue(obj, this._indexBy);
162
+ typeof this.indexBy === "string"
163
+ ? (obj) => obj[this.indexBy]
164
+ : (obj) => types.extractFieldValue(obj, this.indexBy);
256
165
  this._valueEncoding = BORSH_ENCODING(this.type);
257
- this._resultsCollectQueue = new Cache({ max: 10000 }); // TODO choose limit better
166
+ if (this._isProgramValues) {
167
+ this._resolverProgramCache = new Map();
168
+ }
169
+ this._resolverCache = new Cache({ max: 1000 }); // TODO choose limit better (adaptive)
170
+ this.engine = properties.engine || new HashmapIndexEngine();
171
+ await this.engine.init({
172
+ indexBy: this.indexBy,
173
+ nested: {
174
+ match: (obj) => obj instanceof this.dbType,
175
+ query: async (obj, query) => obj.index.search(query)
176
+ },
177
+ maxBatchSize: MAX_BATCH_SIZE
178
+ });
179
+ await this.engine.start?.();
258
180
  await this._query.open({
259
181
  topic: sha256Base64Sync(concat([this._log.log.id, fromString("/document")])),
260
182
  responseHandler: async (query, ctx) => {
@@ -263,68 +185,130 @@ let DocumentIndex = class DocumentIndex extends Program {
263
185
  return;
264
186
  }
265
187
  if (properties.canSearch &&
266
- (query instanceof SearchRequest ||
267
- query instanceof CollectNextRequest) &&
188
+ (query instanceof types.SearchRequest ||
189
+ query instanceof types.CollectNextRequest) &&
268
190
  !(await properties.canSearch(query, ctx.from))) {
269
- return new NoAccess();
191
+ return new types.NoAccess();
270
192
  }
271
- if (query instanceof CloseIteratorRequest) {
193
+ if (query instanceof types.CloseIteratorRequest) {
272
194
  this.processCloseIteratorRequest(query, ctx.from);
273
195
  }
274
196
  else {
275
- const results = await this.processFetchRequest(query, ctx.from, {
197
+ const results = await this.processQuery(query, ctx.from, {
276
198
  canRead: properties.canRead
277
199
  });
278
- return new Results({
200
+ return new types.Results({
279
201
  // Even if results might have length 0, respond, because then we now at least there are no matching results
280
- results: results.results.map((r) => {
281
- if (r.value.last instanceof PutOperation === false) {
282
- throw new Error("Unexpected value type on local results: " +
283
- r.value.last?.constructor.name ||
284
- typeof r.value.last);
285
- }
286
- return new ResultWithSource({
287
- source: r.value.last.data,
288
- context: r.context
289
- });
290
- }),
291
- kept: BigInt(results.kept)
202
+ results: results.results,
203
+ kept: results.kept
292
204
  });
293
205
  }
294
206
  },
295
- responseType: AbstractSearchResult,
296
- queryType: AbstractSearchRequest
207
+ responseType: types.AbstractSearchResult,
208
+ queryType: types.AbstractSearchRequest
297
209
  });
298
210
  }
211
+ async close(from) {
212
+ const closed = await super.close(from);
213
+ if (closed) {
214
+ await this.engine.stop?.();
215
+ }
216
+ return closed;
217
+ }
218
+ async drop(from) {
219
+ const closed = await super.drop(from);
220
+ if (closed) {
221
+ await this.engine.stop?.();
222
+ }
223
+ return closed;
224
+ }
299
225
  async get(key, options) {
300
- return (await this.getDetailed(key, options))?.[0]?.results[0]?.value;
226
+ return (await this.getDetailed(key instanceof types.IdKey ? key : types.toId(key), options))?.[0]?.results[0]?.value;
227
+ }
228
+ async put(value, entry, id) {
229
+ const idString = id.primitive;
230
+ if (this._isProgramValues) {
231
+ this._resolverProgramCache.set(idString, value);
232
+ }
233
+ else {
234
+ this._resolverCache.add(idString, value);
235
+ }
236
+ const context = new types.Context({
237
+ created: (await this.engine.get(id))?.context.created ||
238
+ entry.meta.clock.timestamp.wallTime,
239
+ modified: entry.meta.clock.timestamp.wallTime,
240
+ head: entry.hash,
241
+ gid: entry.gid
242
+ });
243
+ const valueToIndex = await this.fields(value, context);
244
+ this.engine.put({
245
+ id,
246
+ indexed: valueToIndex,
247
+ context,
248
+ size: entry.payload.data.byteLength
249
+ /* reference:
250
+ valueToIndex === value || value instanceof Program
251
+ ? { value }
252
+ : undefined */
253
+ });
254
+ }
255
+ del(key) {
256
+ const keyObject = types.toId(key);
257
+ if (this._isProgramValues) {
258
+ this._resolverProgramCache.delete(key);
259
+ }
260
+ else {
261
+ this._resolverCache.del(key);
262
+ }
263
+ return this.engine.del(keyObject);
301
264
  }
302
265
  async getDetailed(key, options) {
303
266
  let results;
304
267
  if (key instanceof Uint8Array) {
305
- results = await this.queryDetailed(new SearchRequest({
306
- query: [new ByteMatchQuery({ key: this._indexByArr, value: key })]
307
- }), options);
308
- }
309
- else {
310
- const stringValue = asString(key);
311
- results = await this.queryDetailed(new SearchRequest({
268
+ results = await this.queryDetailed(new types.SearchRequest({
312
269
  query: [
313
- new StringMatch({
314
- key: this._indexByArr,
315
- value: stringValue
316
- })
270
+ new types.ByteMatchQuery({ key: this.indexByArr, value: key })
317
271
  ]
318
272
  }), options);
319
273
  }
274
+ else {
275
+ const indexableKey = types.toIdeable(key);
276
+ if (typeof indexableKey === "number" ||
277
+ typeof indexableKey === "bigint") {
278
+ results = await this.queryDetailed(new types.SearchRequest({
279
+ query: [
280
+ new types.IntegerCompare({
281
+ key: this.indexByArr,
282
+ compare: types.Compare.Equal,
283
+ value: indexableKey
284
+ })
285
+ ]
286
+ }), options);
287
+ }
288
+ else {
289
+ results = await this.queryDetailed(new types.SearchRequest({
290
+ query: [
291
+ new types.StringMatch({
292
+ key: this.indexByArr,
293
+ value: indexableKey
294
+ })
295
+ ]
296
+ }), options);
297
+ }
298
+ }
320
299
  return results;
321
300
  }
322
- get size() {
323
- return this._index.size;
301
+ getSize() {
302
+ return this.engine.getSize();
324
303
  }
325
- async getDocumentWithLastOperation(value) {
326
- if (value.reference) {
327
- return value.reference;
304
+ async resolveDocument(value) {
305
+ const cached = this._resolverCache.get(value.id.primitive) ||
306
+ this._resolverProgramCache?.get(value.id.primitive);
307
+ if (cached != null) {
308
+ return { value: cached };
309
+ }
310
+ if (value.indexed instanceof this.type) {
311
+ return { value: value.indexed };
328
312
  }
329
313
  const head = await await this._log.log.get(value.context.head);
330
314
  if (!head) {
@@ -334,230 +318,47 @@ let DocumentIndex = class DocumentIndex extends Program {
334
318
  const payloadValue = await head.getPayloadValue();
335
319
  if (payloadValue instanceof PutOperation) {
336
320
  return {
337
- value: payloadValue.getValue(this.valueEncoding),
338
- last: payloadValue
321
+ value: this.valueEncoding.decoder(payloadValue.data)
322
+ /* size: payloadValue.data.byteLength */
339
323
  };
340
324
  }
341
325
  throw new Error("Unexpected value type when getting document: " +
342
326
  payloadValue?.constructor?.name || typeof payloadValue);
343
327
  }
344
- getDocument(value) {
345
- return this.getDocumentWithLastOperation(value).then((r) => r?.value);
346
- }
347
- async _queryDocuments(filter) {
348
- // Whether we return the full operation data or just the db value
349
- const results = [];
350
- for (const value of this._index.values()) {
351
- if (await filter(value)) {
352
- const topDoc = await this.getDocumentWithLastOperation(value);
353
- topDoc &&
354
- results.push({
355
- context: value.context,
356
- value: topDoc
357
- });
358
- }
359
- }
360
- return results;
361
- }
362
- async processFetchRequest(query, from, options) {
328
+ async processQuery(query, from, options) {
363
329
  // We do special case for querying the id as we can do it faster than iterating
364
- if (query instanceof SearchRequest) {
365
- // Special case querying ids
366
- if (query.query.length === 1 &&
367
- (query.query[0] instanceof ByteMatchQuery ||
368
- query.query[0] instanceof StringMatch) &&
369
- stringArraysEquals(query.query[0].key, this._indexByArr)) {
370
- const firstQuery = query.query[0];
371
- if (firstQuery instanceof ByteMatchQuery) {
372
- const doc = this._index.get(asString(firstQuery.value));
373
- const topDoc = doc && (await this.getDocumentWithLastOperation(doc));
374
- return topDoc
375
- ? {
376
- results: [
377
- {
378
- value: topDoc,
379
- context: doc.context
380
- }
381
- ],
382
- kept: 0
383
- }
384
- : { results: [], kept: 0 };
385
- }
386
- else if (firstQuery instanceof StringMatch &&
387
- firstQuery.method === StringMatchMethod.exact &&
388
- firstQuery.caseInsensitive === false) {
389
- const doc = this._index.get(firstQuery.value);
390
- const topDoc = doc && (await this.getDocumentWithLastOperation(doc));
391
- return topDoc
392
- ? {
393
- results: [
394
- {
395
- value: topDoc,
396
- context: doc.context
397
- }
398
- ],
399
- kept: 0
400
- }
401
- : { results: [], kept: 0 };
402
- }
403
- }
404
- // Handle query normally
405
- let results = await this._queryDocuments(async (doc) => {
406
- for (const f of query.query) {
407
- if (!(await this.handleQueryObject(f, doc))) {
408
- return false;
409
- }
410
- }
411
- return true;
412
- });
413
- if (options?.canRead) {
414
- const keepFilter = await Promise.all(results.map((x) => options?.canRead(x.value.value, from)));
415
- results = results.filter((x, i) => keepFilter[i]);
416
- }
417
- // Sort
418
- await resolvedSort(results, this._toIndex, query.sort);
419
- const batch = getBatchFromResults(results, query.fetch);
420
- if (results.length > 0) {
421
- this._resultsCollectQueue.add(query.idString, {
422
- arr: results,
423
- from
424
- }); // cache resulst not returned
425
- }
426
- return { results: batch, kept: results.length }; // Only return 1 result since we are doing distributed sort, TODO buffer more initially
427
- }
428
- else if (query instanceof CollectNextRequest) {
429
- const results = this._resultsCollectQueue.get(query.idString);
430
- if (!results) {
431
- return {
432
- results: [],
433
- kept: 0
434
- };
435
- }
436
- const batch = getBatchFromResults(results.arr, query.amount);
437
- if (results.arr.length === 0) {
438
- this._resultsCollectQueue.del(query.idString); // TODO add tests for proper cleanup/timeouts
439
- }
440
- return { results: batch, kept: results.arr.length };
441
- }
442
- throw new Error("Unsupported");
443
- }
444
- async processCloseIteratorRequest(query, publicKey) {
445
- const entry = this._resultsCollectQueue.get(query.idString);
446
- if (entry?.from.equals(publicKey)) {
447
- this._resultsCollectQueue.del(query.idString);
448
- }
449
- else if (entry) {
450
- logger.warn("Received a close iterator request for a iterator that does not belong to the requesting peer");
451
- }
452
- }
453
- async handleFieldQuery(f, obj, startIndex) {
454
- // this clause is needed if we have a field that is of type [][] (we will recursively go through each subarray)
455
- if (Array.isArray(obj)) {
456
- for (const element of obj) {
457
- if (await this.handleFieldQuery(f, element, startIndex)) {
458
- return true;
459
- }
460
- }
461
- return false;
462
- }
463
- // Resolve the field from the key path. If we reach an array or nested Document store,
464
- // then do a recursive call or a search to look into them
465
- for (let i = startIndex; i < f.key.length; i++) {
466
- obj = obj[f.key[i]];
467
- if (Array.isArray(obj)) {
468
- for (const element of obj) {
469
- if (await this.handleFieldQuery(f, element, i + 1)) {
470
- return true;
471
- }
472
- }
473
- return false;
474
- }
475
- if (obj instanceof this.dbType) {
476
- const queryCloned = f.clone();
477
- queryCloned.key.splice(0, i + 1); // remove key path until the document store
478
- const results = await obj.index.search(new SearchRequest({ query: [queryCloned] }));
479
- return results.length > 0 ? true : false; // TODO return INNER HITS?
480
- }
330
+ let indexedResult = undefined;
331
+ if (query instanceof types.SearchRequest) {
332
+ indexedResult = await this.engine.query(query, from);
481
333
  }
482
- // When we reach here, the field value (obj) is comparable
483
- if (f instanceof StringMatch) {
484
- let compare = f.value;
485
- if (f.caseInsensitive) {
486
- compare = compare.toLowerCase();
487
- }
488
- if (this.handleStringMatch(f, compare, obj)) {
489
- return true;
490
- }
491
- return false;
334
+ else if (query instanceof types.CollectNextRequest) {
335
+ indexedResult = await this.engine.next(query, from);
492
336
  }
493
- else if (f instanceof ByteMatchQuery) {
494
- if (obj instanceof Uint8Array === false) {
495
- if (stringArraysEquals(f.key, this._indexByArr)) {
496
- return f.valueString === obj;
497
- }
498
- return false;
499
- }
500
- return equals(obj, f.value);
501
- }
502
- else if (f instanceof IntegerCompare) {
503
- const value = obj;
504
- if (typeof value !== "bigint" && typeof value !== "number") {
505
- return false;
506
- }
507
- return compare(value, f.compare, f.value.value);
508
- }
509
- else if (f instanceof MissingField) {
510
- return obj == null; // null or undefined
511
- }
512
- else if (f instanceof BoolQuery) {
513
- return obj === f.value; // true/false
337
+ else {
338
+ throw new Error("Unsupported");
514
339
  }
515
- logger.warn("Unsupported query type: " + f.constructor.name);
516
- return false;
517
- }
518
- async handleQueryObject(f, doc) {
519
- if (f instanceof StateFieldQuery) {
520
- return this.handleFieldQuery(f, doc.value, 0);
521
- }
522
- else if (f instanceof LogicalQuery) {
523
- if (f instanceof And) {
524
- for (const and of f.and) {
525
- if (!(await this.handleQueryObject(and, doc))) {
526
- return false;
527
- }
528
- }
529
- return true;
530
- }
531
- if (f instanceof Or) {
532
- for (const or of f.or) {
533
- if (await this.handleQueryObject(or, doc)) {
534
- return true;
535
- }
536
- }
537
- return false;
538
- }
539
- return false;
340
+ const filteredResults = [];
341
+ for (const result of indexedResult.results) {
342
+ const value = await this.resolveDocument(result);
343
+ if (!value ||
344
+ (options?.canRead && !(await options.canRead(value.value, from)))) {
345
+ continue;
346
+ }
347
+ filteredResults.push(new types.ResultWithSource({
348
+ context: result.context,
349
+ value: value.value,
350
+ source: serialize(value.value),
351
+ indexed: result.indexed
352
+ }));
540
353
  }
541
- logger.info("Unsupported query type: " + f.constructor.name);
542
- return false;
354
+ const results = new types.Results({
355
+ results: filteredResults,
356
+ kept: BigInt(indexedResult.kept)
357
+ });
358
+ return results;
543
359
  }
544
- handleStringMatch(f, compare, fv) {
545
- if (typeof fv !== "string") {
546
- return false;
547
- }
548
- if (f.caseInsensitive) {
549
- fv = fv.toLowerCase();
550
- }
551
- if (f.method === StringMatchMethod.exact) {
552
- return fv === compare;
553
- }
554
- if (f.method === StringMatchMethod.prefix) {
555
- return fv.startsWith(compare);
556
- }
557
- if (f.method === StringMatchMethod.contains) {
558
- return fv.includes(compare);
559
- }
560
- throw new Error("Unsupported");
360
+ async processCloseIteratorRequest(query, publicKey) {
361
+ return this.engine.close(query, publicKey);
561
362
  }
562
363
  /**
563
364
  * Query and retrieve results with most details
@@ -591,25 +392,11 @@ let DocumentIndex = class DocumentIndex extends Program {
591
392
  }
592
393
  const allResults = [];
593
394
  if (local) {
594
- const results = await this.processFetchRequest(queryRequest, this.node.identity.publicKey);
395
+ const results = await this.processQuery(queryRequest, this.node.identity.publicKey);
595
396
  if (results.results.length > 0) {
596
- const resultsObject = new Results({
597
- results: results.results.map((r) => {
598
- if (r.value.last instanceof PutOperation === false) {
599
- throw new Error("Unexpected value type on local results: " +
600
- r.value.last?.constructor.name || typeof r.value.last);
601
- }
602
- return new ResultWithSource({
603
- context: r.context,
604
- value: r.value.value,
605
- source: r.value.last.data
606
- });
607
- }),
608
- kept: BigInt(results.kept)
609
- });
610
397
  options?.onResponse &&
611
- options.onResponse(resultsObject, this.node.identity.publicKey);
612
- allResults.push(resultsObject);
398
+ (await options.onResponse(results, this.node.identity.publicKey));
399
+ allResults.push(results);
613
400
  }
614
401
  }
615
402
  if (remote) {
@@ -624,7 +411,7 @@ let DocumentIndex = class DocumentIndex extends Program {
624
411
  }
625
412
  };
626
413
  try {
627
- if (queryRequest instanceof CloseIteratorRequest) {
414
+ if (queryRequest instanceof types.CloseIteratorRequest) {
628
415
  // don't wait for responses
629
416
  await this._query.request(queryRequest, { mode: remote.mode });
630
417
  }
@@ -718,16 +505,16 @@ let DocumentIndex = class DocumentIndex extends Program {
718
505
  queryRequest.fetch = n;
719
506
  await this.queryDetailed(queryRequest, {
720
507
  ...options,
721
- onResponse: (response, from) => {
508
+ onResponse: async (response, from) => {
722
509
  if (!from) {
723
510
  logger.error("Missing response from");
724
511
  return;
725
512
  }
726
- if (response instanceof NoAccess) {
513
+ if (response instanceof types.NoAccess) {
727
514
  logger.error("Dont have access");
728
515
  return;
729
516
  }
730
- else if (response instanceof Results) {
517
+ else if (response instanceof types.Results) {
731
518
  const results = response;
732
519
  if (results.kept === 0n && results.results.length === 0) {
733
520
  return;
@@ -735,17 +522,23 @@ let DocumentIndex = class DocumentIndex extends Program {
735
522
  if (results.kept > 0n) {
736
523
  done = false; // we have more to do later!
737
524
  }
525
+ const buffer = [];
526
+ for (const result of results.results) {
527
+ const indexKey = types.toIdeable(this.indexByResolver(result.value));
528
+ if (visited.has(indexKey)) {
529
+ continue;
530
+ }
531
+ visited.add(indexKey);
532
+ buffer.push({
533
+ value: result.value,
534
+ context: result.context,
535
+ from: from,
536
+ indexed: result.indexed ||
537
+ (await this.fields(result.value, result.context))
538
+ });
539
+ }
738
540
  peerBufferMap.set(from.hashcode(), {
739
- buffer: results.results
740
- .filter((x) => !visited.has(asString(this.indexByResolver(x.value))))
741
- .map((x) => {
742
- visited.add(asString(this.indexByResolver(x.value)));
743
- return {
744
- from,
745
- value: { value: x.value },
746
- context: x.context
747
- };
748
- }),
541
+ buffer,
749
542
  kept: Number(response.kept)
750
543
  });
751
544
  }
@@ -778,15 +571,15 @@ let DocumentIndex = class DocumentIndex extends Program {
778
571
  }
779
572
  // TODO buffer more than deleted?
780
573
  // TODO batch to multiple 'to's
781
- const collectRequest = new CollectNextRequest({
574
+ const collectRequest = new types.CollectNextRequest({
782
575
  id: queryRequest.id,
783
576
  amount: n - buffer.buffer.length
784
577
  });
785
578
  // Fetch locally?
786
579
  if (peer === this.node.identity.publicKey.hashcode()) {
787
- promises.push(this.processFetchRequest(collectRequest, this.node.identity.publicKey)
788
- .then((results) => {
789
- resultsLeft += results.kept;
580
+ promises.push(this.processQuery(collectRequest, this.node.identity.publicKey)
581
+ .then(async (results) => {
582
+ resultsLeft += Number(results.kept);
790
583
  if (results.results.length === 0) {
791
584
  if (peerBufferMap.get(peer)?.buffer.length === 0) {
792
585
  peerBufferMap.delete(peer); // No more results
@@ -797,17 +590,20 @@ let DocumentIndex = class DocumentIndex extends Program {
797
590
  if (!peerBuffer) {
798
591
  return;
799
592
  }
800
- peerBuffer.kept = results.kept;
801
- peerBuffer.buffer.push(...results.results
802
- .filter((x) => !visited.has(asString(this.indexByResolver(x.value.value))))
803
- .map((x) => {
804
- visited.add(asString(this.indexByResolver(x.value.value)));
805
- return {
806
- value: x.value,
807
- context: x.context,
808
- from: this.node.identity.publicKey
809
- };
810
- }));
593
+ peerBuffer.kept = Number(results.kept);
594
+ for (const result of results.results) {
595
+ if (visited.has(types.toIdeable(this.indexByResolver(result.value)))) {
596
+ continue;
597
+ }
598
+ visited.add(types.toIdeable(this.indexByResolver(result.value)));
599
+ peerBuffer.buffer.push({
600
+ value: result.value,
601
+ context: result.context,
602
+ from: this.node.identity.publicKey,
603
+ indexed: result.indexed ||
604
+ (await this.fields(result.value, result.context))
605
+ });
606
+ }
811
607
  }
812
608
  })
813
609
  .catch((e) => {
@@ -843,16 +639,18 @@ let DocumentIndex = class DocumentIndex extends Program {
843
639
  return;
844
640
  }
845
641
  peerBuffer.kept = Number(response.response.kept);
846
- peerBuffer.buffer.push(...response.response.results
847
- .filter((x) => !visited.has(asString(this.indexByResolver(x.value))))
848
- .map((x) => {
849
- visited.add(asString(this.indexByResolver(x.value)));
850
- return {
851
- value: { value: x.value },
852
- context: x.context,
853
- from: response.from
854
- };
855
- }));
642
+ for (const result of response.response.results) {
643
+ if (visited.has(types.toIdeable(this.indexByResolver(result.value)))) {
644
+ continue;
645
+ }
646
+ visited.add(types.toIdeable(this.indexByResolver(result.value)));
647
+ peerBuffer.buffer.push({
648
+ value: result.value,
649
+ context: result.context,
650
+ from: response.from,
651
+ indexed: this.fields(result.value, result.context)
652
+ });
653
+ }
856
654
  }
857
655
  });
858
656
  })
@@ -883,7 +681,7 @@ let DocumentIndex = class DocumentIndex extends Program {
883
681
  // TODO everything below is not very optimized
884
682
  const fetchedAll = await fetchAtLeast(n);
885
683
  // get n next top entries, shift and pull more results
886
- const results = await resolvedSort(peerBuffers(), this._toIndex, queryRequest.sort);
684
+ const results = await types.resolvedSort(peerBuffers(), queryRequest.sort);
887
685
  const pendingMoreResults = n < results.length;
888
686
  const batch = results.splice(0, n);
889
687
  for (const result of batch) {
@@ -892,17 +690,19 @@ let DocumentIndex = class DocumentIndex extends Program {
892
690
  logger.error("Unexpected empty result buffer");
893
691
  continue;
894
692
  }
895
- const idx = arr.buffer.findIndex((x) => x == result);
693
+ const idx = arr.buffer.findIndex((x) => x.value == result.value);
896
694
  if (idx >= 0) {
897
695
  arr.buffer.splice(idx, 1);
898
696
  }
899
697
  }
900
698
  done = fetchedAll && !pendingMoreResults;
901
- return dedup(batch.map((x) => x.value.value), this.indexByResolver);
699
+ return dedup(batch.map((x) => x.value), this.indexByResolver);
902
700
  };
903
701
  const close = async () => {
904
702
  controller.abort(new AbortError("Iterator closed"));
905
- const closeRequest = new CloseIteratorRequest({ id: queryRequest.id });
703
+ const closeRequest = new types.CloseIteratorRequest({
704
+ id: queryRequest.id
705
+ });
906
706
  const promises = [];
907
707
  for (const [peer, buffer] of peerBufferMap) {
908
708
  if (buffer.kept === 0) {
@@ -939,4 +739,4 @@ DocumentIndex = __decorate([
939
739
  __metadata("design:paramtypes", [Object])
940
740
  ], DocumentIndex);
941
741
  export { DocumentIndex };
942
- //# sourceMappingURL=document-index.js.map
742
+ //# sourceMappingURL=search.js.map