@peerbit/document 13.1.4 → 13.1.6
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/src/program.d.ts +1 -0
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +50 -1
- package/dist/src/program.js.map +1 -1
- package/dist/src/resumable-iterator.d.ts +5 -0
- package/dist/src/resumable-iterator.d.ts.map +1 -1
- package/dist/src/resumable-iterator.js +58 -20
- package/dist/src/resumable-iterator.js.map +1 -1
- package/dist/src/search.d.ts +7 -1
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +120 -23
- package/dist/src/search.js.map +1 -1
- package/package.json +17 -16
- package/src/program.ts +54 -2
- package/src/resumable-iterator.ts +68 -20
- package/src/search.ts +169 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peerbit/document",
|
|
3
|
-
"version": "13.1.
|
|
3
|
+
"version": "13.1.6",
|
|
4
4
|
"description": "Document store implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -61,18 +61,18 @@
|
|
|
61
61
|
"uint8arrays": "^5.1.0",
|
|
62
62
|
"@peerbit/cache": "3.1.0",
|
|
63
63
|
"@peerbit/crypto": "3.1.2",
|
|
64
|
-
"@peerbit/document-interface": "3.2.
|
|
65
|
-
"@peerbit/indexer-
|
|
66
|
-
"@peerbit/indexer-
|
|
67
|
-
"@peerbit/indexer-simple": "1.2.
|
|
68
|
-
"@peerbit/log": "6.2.
|
|
69
|
-
"@peerbit/indexer-sqlite3": "3.0.
|
|
70
|
-
"@peerbit/
|
|
64
|
+
"@peerbit/document-interface": "3.2.48",
|
|
65
|
+
"@peerbit/indexer-interface": "3.0.6",
|
|
66
|
+
"@peerbit/indexer-cache": "0.2.9",
|
|
67
|
+
"@peerbit/indexer-simple": "1.2.10",
|
|
68
|
+
"@peerbit/log": "6.2.5",
|
|
69
|
+
"@peerbit/indexer-sqlite3": "3.0.9",
|
|
70
|
+
"@peerbit/logger": "2.0.1",
|
|
71
71
|
"@peerbit/pubsub": "5.3.0",
|
|
72
|
-
"@peerbit/
|
|
73
|
-
"@peerbit/
|
|
74
|
-
"@peerbit/
|
|
75
|
-
"@peerbit/
|
|
72
|
+
"@peerbit/rpc": "6.1.3",
|
|
73
|
+
"@peerbit/program": "6.0.35",
|
|
74
|
+
"@peerbit/shared-log": "13.2.6",
|
|
75
|
+
"@peerbit/stream-interface": "6.0.11"
|
|
76
76
|
},
|
|
77
77
|
"optionalDependencies": {
|
|
78
78
|
"@peerbit/document-rust": "0.1.1"
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"@types/pidusage": "^2.0.5",
|
|
84
84
|
"pidusage": "^4.0.1",
|
|
85
85
|
"uuid": "^10.0.0",
|
|
86
|
-
"@peerbit/native-backbone": "0.1.3",
|
|
87
|
-
"@peerbit/test-utils": "3.1.4",
|
|
88
86
|
"@peerbit/log-rust": "1.1.2",
|
|
89
|
-
"peerbit": "
|
|
90
|
-
"@peerbit/
|
|
87
|
+
"@peerbit/native-backbone": "0.1.4",
|
|
88
|
+
"@peerbit/test-utils": "3.1.6",
|
|
89
|
+
"@peerbit/time": "3.0.0",
|
|
90
|
+
"peerbit": "5.3.6"
|
|
91
91
|
},
|
|
92
92
|
"repository": {
|
|
93
93
|
"type": "git",
|
|
@@ -102,6 +102,7 @@
|
|
|
102
102
|
"clean": "aegir clean",
|
|
103
103
|
"build": "aegir build --no-bundle",
|
|
104
104
|
"test": "aegir test --target node",
|
|
105
|
+
"test:document-rust-core": "npm run build && PEERBIT_SHARED_LOG_RUST_CORE=1 aegir test -t node --grep '^(?!.*(uses the validated local append path|uses the independent native prepared batch path|uses native shared-log planning for replicated target-none puts|uses commit-only local puts when coordinate persistence is deferred)).*(native conformance guard |operations basic |operations get |operations index |operations search |iterate sort |count approximate |query distribution |returnIndexed |caching |custom index |prefetch )' -- --forbid-pending",
|
|
105
106
|
"lint": "aegir lint",
|
|
106
107
|
"test:cov": "aegir test -t node --cov"
|
|
107
108
|
}
|
package/src/program.ts
CHANGED
|
@@ -112,6 +112,16 @@ export class NativeDocumentModeError extends Error {
|
|
|
112
112
|
|
|
113
113
|
type MaybePromise<T> = Promise<T> | T;
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* True when an error signals that an entry's payload bytes were never
|
|
117
|
+
* materialized on the JS side (a hollow entry backed by a native block store).
|
|
118
|
+
* `DecryptedThing.getValue` throws `Error("Missing data")` in that case. Used to
|
|
119
|
+
* decide whether the auto-mode append/delete read-back should recover the
|
|
120
|
+
* operation from the storage-bytes / block-store path instead.
|
|
121
|
+
*/
|
|
122
|
+
const isMissingPayloadDataError = (error: unknown): boolean =>
|
|
123
|
+
error instanceof Error && error.message === "Missing data";
|
|
124
|
+
|
|
115
125
|
const isPromiseLike = <T>(value: MaybePromise<T>): value is Promise<T> =>
|
|
116
126
|
!!value && typeof (value as Promise<T>).then === "function";
|
|
117
127
|
|
|
@@ -2837,17 +2847,44 @@ export class Documents<
|
|
|
2837
2847
|
return;
|
|
2838
2848
|
}
|
|
2839
2849
|
ensureInitialized?.();
|
|
2840
|
-
|
|
2850
|
+
try {
|
|
2851
|
+
return await entry.getPayloadValue();
|
|
2852
|
+
} catch (error) {
|
|
2853
|
+
// Auto (non-native document) mode with a native block store: the entry
|
|
2854
|
+
// materialized in the entry index can be a hollow shell whose in-memory
|
|
2855
|
+
// payload bytes were never loaded (the native store keeps the block
|
|
2856
|
+
// bytes at the storage layer, not on the JS entry). `getPayloadValue`
|
|
2857
|
+
// then throws "Missing data". The block itself is present, so recover
|
|
2858
|
+
// the operation via the storage-bytes / block-store read path. This is
|
|
2859
|
+
// a no-op for the pure-JS backend, where `getPayloadValue` succeeds.
|
|
2860
|
+
if (!isMissingPayloadDataError(error)) {
|
|
2861
|
+
throw error;
|
|
2862
|
+
}
|
|
2863
|
+
const operation = await this.getPlainEntryOperationFromStorage(entry);
|
|
2864
|
+
if (operation) {
|
|
2865
|
+
return operation;
|
|
2866
|
+
}
|
|
2867
|
+
throw error;
|
|
2868
|
+
}
|
|
2841
2869
|
}
|
|
2842
2870
|
|
|
2843
2871
|
private async getPlainEntryOperationFromStorage(
|
|
2844
2872
|
entry: Entry<Operation>,
|
|
2845
2873
|
): Promise<Operation | undefined> {
|
|
2846
|
-
let storageBytes: Uint8Array;
|
|
2874
|
+
let storageBytes: Uint8Array | undefined;
|
|
2847
2875
|
try {
|
|
2848
2876
|
storageBytes =
|
|
2849
2877
|
Entry.getPreparedStorageBytes(entry) ?? entry.getStorageBytes();
|
|
2850
2878
|
} catch {
|
|
2879
|
+
// The entry object is hollow (its payload bytes never materialized on
|
|
2880
|
+
// the JS side), so it cannot re-serialize itself. Fall through to the
|
|
2881
|
+
// block-store fallback below.
|
|
2882
|
+
storageBytes = undefined;
|
|
2883
|
+
}
|
|
2884
|
+
// Fall back to the raw block held by the block store, keyed by the entry
|
|
2885
|
+
// hash, whenever the entry could not (or did not) yield its own bytes.
|
|
2886
|
+
storageBytes ??= await this.getEntryStorageBytesFromBlocks(entry);
|
|
2887
|
+
if (!storageBytes) {
|
|
2851
2888
|
return;
|
|
2852
2889
|
}
|
|
2853
2890
|
try {
|
|
@@ -2869,6 +2906,21 @@ export class Documents<
|
|
|
2869
2906
|
}
|
|
2870
2907
|
}
|
|
2871
2908
|
|
|
2909
|
+
private async getEntryStorageBytesFromBlocks(
|
|
2910
|
+
entry: Entry<Operation>,
|
|
2911
|
+
): Promise<Uint8Array | undefined> {
|
|
2912
|
+
const hash = entry.hash;
|
|
2913
|
+
if (!hash) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
try {
|
|
2917
|
+
const bytes = await this.log.log.blocks.get(hash);
|
|
2918
|
+
return bytes ?? undefined;
|
|
2919
|
+
} catch {
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2872
2924
|
private getNativeDocumentFieldExtractionPlan(
|
|
2873
2925
|
path: string | readonly string[],
|
|
2874
2926
|
): SimpleDocumentFieldExtractionPlan | undefined {
|
|
@@ -11,6 +11,11 @@ import { logger as loggerFn } from "@peerbit/logger";
|
|
|
11
11
|
const iteratorLogger = loggerFn("peerbit:document:index:iterate");
|
|
12
12
|
|
|
13
13
|
export class ResumableIterators<T extends Record<string, any>> {
|
|
14
|
+
private readonly pendingMarks = new Map<
|
|
15
|
+
string,
|
|
16
|
+
Map<string, indexerTypes.IdKey>
|
|
17
|
+
>();
|
|
18
|
+
|
|
14
19
|
constructor(
|
|
15
20
|
readonly index: indexerTypes.Index<T>,
|
|
16
21
|
readonly queues = new Cache<{
|
|
@@ -22,6 +27,36 @@ export class ResumableIterators<T extends Record<string, any>> {
|
|
|
22
27
|
// TODO choose upper limit better
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
private markKey(id: indexerTypes.IdKey) {
|
|
31
|
+
return `${id.key instanceof Uint8Array ? "bytes" : typeof id.key}:${String(id.primitive)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private retainMarks(id: string, ids: Iterable<indexerTypes.IdKey>) {
|
|
35
|
+
let retained = this.pendingMarks.get(id);
|
|
36
|
+
for (const mark of ids) {
|
|
37
|
+
if (!retained) {
|
|
38
|
+
retained = new Map();
|
|
39
|
+
this.pendingMarks.set(id, retained);
|
|
40
|
+
}
|
|
41
|
+
retained.set(this.markKey(mark), mark);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private async applyPendingMarks(
|
|
46
|
+
id: string,
|
|
47
|
+
iterator: indexerTypes.IndexIterator<T, undefined>,
|
|
48
|
+
) {
|
|
49
|
+
while (true) {
|
|
50
|
+
const retained = this.pendingMarks.get(id);
|
|
51
|
+
if (!retained?.size) {
|
|
52
|
+
this.pendingMarks.delete(id);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.pendingMarks.delete(id);
|
|
56
|
+
await iterator.markYielded?.(retained.values());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
25
60
|
async iterateAndFetch(
|
|
26
61
|
request: SearchRequest | SearchRequestIndexed | IterationRequest,
|
|
27
62
|
options?: { keepAlive?: boolean },
|
|
@@ -31,29 +66,32 @@ export class ResumableIterators<T extends Record<string, any>> {
|
|
|
31
66
|
fetch: request.fetch,
|
|
32
67
|
keepAlive: Boolean(options?.keepAlive),
|
|
33
68
|
});
|
|
34
|
-
const iterator = this.index.iterate(request);
|
|
35
|
-
const firstResult = await iterator.next(request.fetch);
|
|
36
69
|
const keepAlive = options?.keepAlive === true;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
70
|
+
const iterator = this.index.iterate(request);
|
|
71
|
+
this.queues.add(request.idString, { iterator, request, keepAlive });
|
|
72
|
+
try {
|
|
73
|
+
await this.applyPendingMarks(request.idString, iterator);
|
|
74
|
+
const firstResult = await iterator.next(request.fetch);
|
|
75
|
+
if (!keepAlive && iterator.done() === true) {
|
|
76
|
+
this.queues.del(request.idString);
|
|
77
|
+
this.pendingMarks.delete(request.idString);
|
|
78
|
+
}
|
|
79
|
+
iteratorLogger("iterate:queued", {
|
|
80
|
+
id: request.idString,
|
|
41
81
|
keepAlive,
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
done: iterator.done() === true,
|
|
83
|
+
batch: firstResult.length,
|
|
84
|
+
});
|
|
85
|
+
/* console.debug(
|
|
86
|
+
"[ResumableIterators] iterateAndFetch",
|
|
87
|
+
request.idString,
|
|
88
|
+
{ keepAlive },
|
|
89
|
+
); */
|
|
90
|
+
return firstResult;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
this.clear(request.idString);
|
|
93
|
+
throw error;
|
|
44
94
|
}
|
|
45
|
-
iteratorLogger("iterate:queued", {
|
|
46
|
-
id: request.idString,
|
|
47
|
-
keepAlive,
|
|
48
|
-
done: iterator.done() === true,
|
|
49
|
-
batch: firstResult.length,
|
|
50
|
-
});
|
|
51
|
-
/* console.debug(
|
|
52
|
-
"[ResumableIterators] iterateAndFetch",
|
|
53
|
-
request.idString,
|
|
54
|
-
{ keepAlive },
|
|
55
|
-
); */
|
|
56
|
-
return firstResult;
|
|
57
95
|
}
|
|
58
96
|
|
|
59
97
|
async next(
|
|
@@ -138,6 +176,7 @@ export class ResumableIterators<T extends Record<string, any>> {
|
|
|
138
176
|
}
|
|
139
177
|
}
|
|
140
178
|
this.queues.del(id);
|
|
179
|
+
this.pendingMarks.delete(id);
|
|
141
180
|
}
|
|
142
181
|
|
|
143
182
|
async clearAll() {
|
|
@@ -155,6 +194,7 @@ export class ResumableIterators<T extends Record<string, any>> {
|
|
|
155
194
|
),
|
|
156
195
|
);
|
|
157
196
|
this.queues.clear();
|
|
197
|
+
this.pendingMarks.clear();
|
|
158
198
|
}
|
|
159
199
|
|
|
160
200
|
has(id: string) {
|
|
@@ -179,4 +219,12 @@ export class ResumableIterators<T extends Record<string, any>> {
|
|
|
179
219
|
});
|
|
180
220
|
return pending;
|
|
181
221
|
}
|
|
222
|
+
|
|
223
|
+
async markYielded(id: string, ids: Iterable<indexerTypes.IdKey>) {
|
|
224
|
+
this.retainMarks(id, ids);
|
|
225
|
+
const iterator = this.queues.get(id);
|
|
226
|
+
if (iterator) {
|
|
227
|
+
await this.applyPendingMarks(id, iterator.iterator);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
182
230
|
}
|
package/src/search.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import { CachedIndex, type QueryCacheOptions } from "@peerbit/indexer-cache";
|
|
25
25
|
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
26
26
|
import { HashmapIndex } from "@peerbit/indexer-simple";
|
|
27
|
-
import { BORSH_ENCODING, type Encoding, Entry } from "@peerbit/log";
|
|
27
|
+
import { BORSH_ENCODING, type Encoding, type Entry } from "@peerbit/log";
|
|
28
28
|
import { logger as loggerFn } from "@peerbit/logger";
|
|
29
29
|
import { ClosedError, Program } from "@peerbit/program";
|
|
30
30
|
import {
|
|
@@ -1179,13 +1179,98 @@ export class DocumentIndex<
|
|
|
1179
1179
|
}
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
+
/**
|
|
1183
|
+
* Resolve documents without bypassing the resolver/program caches. Cache and
|
|
1184
|
+
* identity-index hits are collected first; only the remaining heads cross the
|
|
1185
|
+
* log read boundary, and those reads stay aligned in one batch.
|
|
1186
|
+
*/
|
|
1187
|
+
private async resolveDocumentsWithBatchedHeads(
|
|
1188
|
+
values: Array<{
|
|
1189
|
+
id?: indexerTypes.IdPrimitive;
|
|
1190
|
+
indexed: I;
|
|
1191
|
+
head: string;
|
|
1192
|
+
}>,
|
|
1193
|
+
): Promise<{
|
|
1194
|
+
resolved: Array<{ value: T } | undefined>;
|
|
1195
|
+
heads: Array<Entry<Operation> | undefined>;
|
|
1196
|
+
}> {
|
|
1197
|
+
const resolved: Array<{ value: T } | undefined> = new Array(values.length);
|
|
1198
|
+
const heads: Array<Entry<Operation> | undefined> = new Array(values.length);
|
|
1199
|
+
const unresolvedPositions: number[] = [];
|
|
1200
|
+
|
|
1201
|
+
for (let i = 0; i < values.length; i++) {
|
|
1202
|
+
const value = values[i]!;
|
|
1203
|
+
const cached = await this.resolveDocument({
|
|
1204
|
+
...value,
|
|
1205
|
+
headEntry: null,
|
|
1206
|
+
});
|
|
1207
|
+
if (cached) {
|
|
1208
|
+
resolved[i] = cached;
|
|
1209
|
+
} else {
|
|
1210
|
+
unresolvedPositions.push(i);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (unresolvedPositions.length === 0) {
|
|
1215
|
+
return { resolved, heads };
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const unresolvedHeads = await this._log.log.getMany(
|
|
1219
|
+
unresolvedPositions.map((position) => values[position]!.head),
|
|
1220
|
+
);
|
|
1221
|
+
for (let i = 0; i < unresolvedPositions.length; i++) {
|
|
1222
|
+
const position = unresolvedPositions[i]!;
|
|
1223
|
+
const head = unresolvedHeads[i];
|
|
1224
|
+
heads[position] = head;
|
|
1225
|
+
resolved[position] = await this.resolveDocument({
|
|
1226
|
+
...values[position]!,
|
|
1227
|
+
headEntry: head ?? null,
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
return { resolved, heads };
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1182
1234
|
private async wrapPushResults(
|
|
1183
1235
|
matches: Array<WithContext<T> | WithContext<I>>,
|
|
1184
1236
|
resolve: boolean,
|
|
1185
1237
|
): Promise<types.Result[]> {
|
|
1186
1238
|
if (!matches.length) return [];
|
|
1239
|
+
const headsByMatch: Array<Entry<Operation> | undefined> = [];
|
|
1240
|
+
const resolvedByMatch: Array<{ value: T } | undefined> = [];
|
|
1241
|
+
if (!resolve) {
|
|
1242
|
+
headsByMatch.push(
|
|
1243
|
+
...(await this._log.log.getMany(
|
|
1244
|
+
matches.map((match) => match.__context.head),
|
|
1245
|
+
)),
|
|
1246
|
+
);
|
|
1247
|
+
} else {
|
|
1248
|
+
const indexedPositions: number[] = [];
|
|
1249
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1250
|
+
if (!(matches[i] instanceof this.documentType)) {
|
|
1251
|
+
indexedPositions.push(i);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
const indexedBatch = indexedPositions.length
|
|
1255
|
+
? await this.resolveDocumentsWithBatchedHeads(
|
|
1256
|
+
indexedPositions.map((position) => {
|
|
1257
|
+
const indexed = matches[position] as WithContext<I>;
|
|
1258
|
+
return {
|
|
1259
|
+
indexed,
|
|
1260
|
+
head: indexed.__context.head,
|
|
1261
|
+
};
|
|
1262
|
+
}),
|
|
1263
|
+
)
|
|
1264
|
+
: { resolved: [], heads: [] };
|
|
1265
|
+
for (let i = 0; i < indexedPositions.length; i++) {
|
|
1266
|
+
const position = indexedPositions[i]!;
|
|
1267
|
+
resolvedByMatch[position] = indexedBatch.resolved[i];
|
|
1268
|
+
headsByMatch[position] = indexedBatch.heads[i];
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1187
1271
|
const results: types.Result[] = [];
|
|
1188
|
-
for (
|
|
1272
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1273
|
+
const match = matches[i]!;
|
|
1189
1274
|
if (resolve) {
|
|
1190
1275
|
if (match instanceof this.documentType) {
|
|
1191
1276
|
const doc = match as WithContext<T>;
|
|
@@ -1203,10 +1288,7 @@ export class DocumentIndex<
|
|
|
1203
1288
|
}
|
|
1204
1289
|
|
|
1205
1290
|
const indexed = match as WithContext<I>;
|
|
1206
|
-
const resolved =
|
|
1207
|
-
indexed,
|
|
1208
|
-
head: indexed.__context.head,
|
|
1209
|
-
});
|
|
1291
|
+
const resolved = resolvedByMatch[i];
|
|
1210
1292
|
|
|
1211
1293
|
if (resolved) {
|
|
1212
1294
|
results.push(
|
|
@@ -1220,7 +1302,7 @@ export class DocumentIndex<
|
|
|
1220
1302
|
continue;
|
|
1221
1303
|
}
|
|
1222
1304
|
|
|
1223
|
-
const head =
|
|
1305
|
+
const head = headsByMatch[i];
|
|
1224
1306
|
results.push(
|
|
1225
1307
|
new types.ResultIndexedValue({
|
|
1226
1308
|
context: indexed.__context,
|
|
@@ -1231,7 +1313,7 @@ export class DocumentIndex<
|
|
|
1231
1313
|
);
|
|
1232
1314
|
} else {
|
|
1233
1315
|
const indexed = match as WithContext<I>;
|
|
1234
|
-
const head =
|
|
1316
|
+
const head = headsByMatch[i];
|
|
1235
1317
|
results.push(
|
|
1236
1318
|
new types.ResultIndexedValue({
|
|
1237
1319
|
context: indexed.__context,
|
|
@@ -1253,17 +1335,28 @@ export class DocumentIndex<
|
|
|
1253
1335
|
return [];
|
|
1254
1336
|
}
|
|
1255
1337
|
const drained = queueEntries.splice(0);
|
|
1338
|
+
const resolvedBatch = resolve
|
|
1339
|
+
? await this.resolveDocumentsWithBatchedHeads(
|
|
1340
|
+
drained.map((entry) => ({
|
|
1341
|
+
indexed: entry.value,
|
|
1342
|
+
head: entry.value.__context.head,
|
|
1343
|
+
})),
|
|
1344
|
+
)
|
|
1345
|
+
: undefined;
|
|
1346
|
+
const heads = resolvedBatch
|
|
1347
|
+
? resolvedBatch.heads
|
|
1348
|
+
: await this._log.log.getMany(
|
|
1349
|
+
drained.map((entry) => entry.value.__context.head),
|
|
1350
|
+
);
|
|
1256
1351
|
const results: types.Result[] = [];
|
|
1257
|
-
for (
|
|
1352
|
+
for (let i = 0; i < drained.length; i++) {
|
|
1353
|
+
const entry = drained[i]!;
|
|
1258
1354
|
const indexedUnwrapped = Object.assign(
|
|
1259
1355
|
Object.create(this.indexedType.prototype),
|
|
1260
1356
|
entry.value,
|
|
1261
1357
|
);
|
|
1262
1358
|
if (resolve) {
|
|
1263
|
-
const value =
|
|
1264
|
-
indexed: entry.value,
|
|
1265
|
-
head: entry.value.__context.head,
|
|
1266
|
-
});
|
|
1359
|
+
const value = resolvedBatch!.resolved[i];
|
|
1267
1360
|
if (value) {
|
|
1268
1361
|
results.push(
|
|
1269
1362
|
new types.ResultValue({
|
|
@@ -1276,7 +1369,7 @@ export class DocumentIndex<
|
|
|
1276
1369
|
continue;
|
|
1277
1370
|
}
|
|
1278
1371
|
|
|
1279
|
-
const head =
|
|
1372
|
+
const head = heads[i];
|
|
1280
1373
|
results.push(
|
|
1281
1374
|
new types.ResultIndexedValue({
|
|
1282
1375
|
context: entry.value.__context,
|
|
@@ -1286,7 +1379,7 @@ export class DocumentIndex<
|
|
|
1286
1379
|
}),
|
|
1287
1380
|
);
|
|
1288
1381
|
} else {
|
|
1289
|
-
const head =
|
|
1382
|
+
const head = heads[i];
|
|
1290
1383
|
results.push(
|
|
1291
1384
|
new types.ResultIndexedValue({
|
|
1292
1385
|
context: entry.value.__context,
|
|
@@ -1336,6 +1429,7 @@ export class DocumentIndex<
|
|
|
1336
1429
|
let pendingAdded = added;
|
|
1337
1430
|
do {
|
|
1338
1431
|
const batches: types.Result[] = [];
|
|
1432
|
+
let claimedIds: indexerTypes.IdKey[] = [];
|
|
1339
1433
|
const queued = await this.drainQueuedResults(
|
|
1340
1434
|
queue.queue,
|
|
1341
1435
|
resolveFlag,
|
|
@@ -1357,6 +1451,21 @@ export class DocumentIndex<
|
|
|
1357
1451
|
const wrapped = await this.wrapPushResults(matches, resolveFlag);
|
|
1358
1452
|
if (wrapped.length) {
|
|
1359
1453
|
batches.push(...wrapped);
|
|
1454
|
+
claimedIds = wrapped
|
|
1455
|
+
.map((result) =>
|
|
1456
|
+
result instanceof types.ResultValue && result.indexed
|
|
1457
|
+
? indexerTypes.toId(
|
|
1458
|
+
this.indexByResolver(result.indexed),
|
|
1459
|
+
)
|
|
1460
|
+
: result instanceof types.ResultIndexedValue
|
|
1461
|
+
? indexerTypes.toId(
|
|
1462
|
+
this.indexByResolver(result.value),
|
|
1463
|
+
)
|
|
1464
|
+
: undefined,
|
|
1465
|
+
)
|
|
1466
|
+
.filter(
|
|
1467
|
+
(id): id is indexerTypes.IdKey => id !== undefined,
|
|
1468
|
+
);
|
|
1360
1469
|
}
|
|
1361
1470
|
}
|
|
1362
1471
|
if (batches.length) {
|
|
@@ -1374,6 +1483,12 @@ export class DocumentIndex<
|
|
|
1374
1483
|
redundancy: 1,
|
|
1375
1484
|
}),
|
|
1376
1485
|
});
|
|
1486
|
+
if (claimedIds.length) {
|
|
1487
|
+
await this._resumableIterators.markYielded(
|
|
1488
|
+
_iteratorId,
|
|
1489
|
+
claimedIds,
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1377
1492
|
}
|
|
1378
1493
|
pendingAdded = queue.pendingAdded ?? [];
|
|
1379
1494
|
queue.pendingAdded = undefined;
|
|
@@ -3452,6 +3567,7 @@ export class DocumentIndex<
|
|
|
3452
3567
|
id?: indexerTypes.IdPrimitive;
|
|
3453
3568
|
indexed: I;
|
|
3454
3569
|
head: string;
|
|
3570
|
+
headEntry?: Entry<Operation> | null;
|
|
3455
3571
|
}): Promise<{ value: T } | undefined> {
|
|
3456
3572
|
const id =
|
|
3457
3573
|
value.id ??
|
|
@@ -3473,7 +3589,10 @@ export class DocumentIndex<
|
|
|
3473
3589
|
return { value: obj as T };
|
|
3474
3590
|
}
|
|
3475
3591
|
|
|
3476
|
-
const head =
|
|
3592
|
+
const head =
|
|
3593
|
+
value.headEntry === undefined
|
|
3594
|
+
? await this._log.log.get(value.head)
|
|
3595
|
+
: (value.headEntry ?? undefined);
|
|
3477
3596
|
if (!head) {
|
|
3478
3597
|
return undefined; // we could end up here if we recently pruned the document and other peers never persisted the entry
|
|
3479
3598
|
// TODO update changes in index before removing entries from log entry storage
|
|
@@ -3611,7 +3730,10 @@ export class DocumentIndex<
|
|
|
3611
3730
|
this._resultQueue.set(query.idString, prevQueued);
|
|
3612
3731
|
}
|
|
3613
3732
|
|
|
3614
|
-
const
|
|
3733
|
+
const candidates: Array<{
|
|
3734
|
+
result: indexerTypes.IndexedResult<WithContext<I>>;
|
|
3735
|
+
indexed: I;
|
|
3736
|
+
}> = [];
|
|
3615
3737
|
const resolveDocumentsFlag = resolvesDocuments(fromQuery);
|
|
3616
3738
|
const replicateIndexFlag = replicatesIndex(fromQuery);
|
|
3617
3739
|
for (const result of toIterate) {
|
|
@@ -3633,11 +3755,29 @@ export class DocumentIndex<
|
|
|
3633
3755
|
) {
|
|
3634
3756
|
continue;
|
|
3635
3757
|
}
|
|
3758
|
+
candidates.push({ result, indexed: indexedUnwrapped });
|
|
3759
|
+
}
|
|
3760
|
+
|
|
3761
|
+
const resolvedBatch = resolveDocumentsFlag
|
|
3762
|
+
? await this.resolveDocumentsWithBatchedHeads(
|
|
3763
|
+
candidates.map(({ result }) => ({
|
|
3764
|
+
indexed: result.value,
|
|
3765
|
+
head: result.value.__context.head,
|
|
3766
|
+
})),
|
|
3767
|
+
)
|
|
3768
|
+
: undefined;
|
|
3769
|
+
const heads = resolvedBatch
|
|
3770
|
+
? resolvedBatch.heads
|
|
3771
|
+
: candidates.length > 0
|
|
3772
|
+
? await this._log.log.getMany(
|
|
3773
|
+
candidates.map(({ result }) => result.value.__context.head),
|
|
3774
|
+
)
|
|
3775
|
+
: [];
|
|
3776
|
+
const filteredResults: types.Result[] = [];
|
|
3777
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
3778
|
+
const { result, indexed: indexedUnwrapped } = candidates[i]!;
|
|
3636
3779
|
if (resolveDocumentsFlag) {
|
|
3637
|
-
const value =
|
|
3638
|
-
indexed: result.value,
|
|
3639
|
-
head: result.value.__context.head,
|
|
3640
|
-
});
|
|
3780
|
+
const value = resolvedBatch!.resolved[i];
|
|
3641
3781
|
|
|
3642
3782
|
if (!value) {
|
|
3643
3783
|
continue;
|
|
@@ -3653,7 +3793,7 @@ export class DocumentIndex<
|
|
|
3653
3793
|
);
|
|
3654
3794
|
} else {
|
|
3655
3795
|
const context = result.value.__context;
|
|
3656
|
-
const head =
|
|
3796
|
+
const head = heads[i];
|
|
3657
3797
|
if (replicateIndexFlag) {
|
|
3658
3798
|
if (!head) {
|
|
3659
3799
|
continue;
|
|
@@ -6122,9 +6262,10 @@ export class DocumentIndex<
|
|
|
6122
6262
|
continue;
|
|
6123
6263
|
}
|
|
6124
6264
|
}
|
|
6125
|
-
const
|
|
6265
|
+
const indexId = indexerTypes.toId(
|
|
6126
6266
|
this.indexByResolver(indexedCandidate),
|
|
6127
|
-
)
|
|
6267
|
+
);
|
|
6268
|
+
const id = indexId.primitive;
|
|
6128
6269
|
const existingIndexed = indexedPlaceholders?.get(id);
|
|
6129
6270
|
if (existingIndexed) {
|
|
6130
6271
|
if (resolve) {
|
|
@@ -6165,6 +6306,10 @@ export class DocumentIndex<
|
|
|
6165
6306
|
if (!resolve) {
|
|
6166
6307
|
ensureIndexedPlaceholders().set(id, placeholder);
|
|
6167
6308
|
}
|
|
6309
|
+
await this._resumableIterators.markYielded(
|
|
6310
|
+
queryRequestCoerced.idString,
|
|
6311
|
+
[indexId],
|
|
6312
|
+
);
|
|
6168
6313
|
hasRelevantChange = true;
|
|
6169
6314
|
}
|
|
6170
6315
|
}
|