@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.
- package/README.md +2 -2
- package/dist/benchmark/index.d.ts +2 -0
- package/dist/benchmark/index.d.ts.map +1 -0
- package/dist/benchmark/index.js +126 -0
- package/dist/benchmark/index.js.map +1 -0
- package/dist/benchmark/replication.d.ts +2 -0
- package/dist/benchmark/replication.d.ts.map +1 -0
- package/dist/benchmark/replication.js +174 -0
- package/dist/benchmark/replication.js.map +1 -0
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/program.d.ts +90 -0
- package/dist/src/program.d.ts.map +1 -0
- package/{lib/esm/document-store.js → dist/src/program.js} +141 -109
- package/dist/src/program.js.map +1 -0
- package/dist/src/search.d.ts +118 -0
- package/dist/src/search.d.ts.map +1 -0
- package/{lib/esm/document-index.js → dist/src/search.js} +246 -446
- package/dist/src/search.js.map +1 -0
- package/package.json +69 -43
- package/src/constants.ts +1 -0
- package/src/index.ts +4 -3
- package/src/{document-store.ts → program.ts} +216 -183
- package/src/search.ts +997 -0
- package/LICENSE +0 -202
- package/lib/esm/document-index.d.ts +0 -147
- package/lib/esm/document-index.js.map +0 -1
- package/lib/esm/document-store.d.ts +0 -72
- package/lib/esm/document-store.js.map +0 -1
- package/lib/esm/index.d.ts +0 -3
- package/lib/esm/index.js +0 -4
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/query.d.ts +0 -191
- package/lib/esm/query.js +0 -615
- package/lib/esm/query.js.map +0 -1
- package/lib/esm/utils.d.ts +0 -3
- package/lib/esm/utils.js +0 -12
- package/lib/esm/utils.js.map +0 -1
- package/src/document-index.ts +0 -1268
- package/src/query.ts +0 -525
- 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 {
|
|
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
|
|
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 {
|
|
20
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
100
|
-
this.key = props.key;
|
|
101
|
-
}
|
|
75
|
+
this.key = props.key;
|
|
102
76
|
}
|
|
103
77
|
};
|
|
104
78
|
__decorate([
|
|
105
|
-
field({ type:
|
|
106
|
-
__metadata("design:type",
|
|
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 =
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
// Resolve doc value by index key
|
|
131
|
+
indexBy;
|
|
132
|
+
indexByArr;
|
|
220
133
|
indexByResolver;
|
|
221
|
-
//
|
|
222
|
-
|
|
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.
|
|
248
|
-
this.
|
|
249
|
-
this.
|
|
250
|
-
? this.
|
|
251
|
-
: [this.
|
|
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.
|
|
254
|
-
? (obj) => obj[this.
|
|
255
|
-
: (obj) => extractFieldValue(obj, this.
|
|
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.
|
|
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.
|
|
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
|
|
281
|
-
|
|
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
|
|
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
|
-
|
|
323
|
-
return this.
|
|
301
|
+
getSize() {
|
|
302
|
+
return this.engine.getSize();
|
|
324
303
|
}
|
|
325
|
-
async
|
|
326
|
-
|
|
327
|
-
|
|
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:
|
|
338
|
-
|
|
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
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
483
|
-
|
|
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
|
|
494
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
|
|
542
|
-
|
|
354
|
+
const results = new types.Results({
|
|
355
|
+
results: filteredResults,
|
|
356
|
+
kept: BigInt(indexedResult.kept)
|
|
357
|
+
});
|
|
358
|
+
return results;
|
|
543
359
|
}
|
|
544
|
-
|
|
545
|
-
|
|
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.
|
|
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(
|
|
612
|
-
allResults.push(
|
|
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
|
|
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.
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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(),
|
|
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
|
|
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({
|
|
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=
|
|
742
|
+
//# sourceMappingURL=search.js.map
|