@peerbit/indexer-interface 0.0.1-cccc078
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/id.d.ts +47 -0
- package/dist/src/id.d.ts.map +1 -0
- package/dist/src/id.js +203 -0
- package/dist/src/id.js.map +1 -0
- package/dist/src/index-engine.d.ts +83 -0
- package/dist/src/index-engine.d.ts.map +1 -0
- package/dist/src/index-engine.js +106 -0
- package/dist/src/index-engine.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/query.d.ts +170 -0
- package/dist/src/query.d.ts.map +1 -0
- package/dist/src/query.js +579 -0
- package/dist/src/query.js.map +1 -0
- package/dist/src/store.d.ts +7 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +2 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/utils.d.ts +9 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +88 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +65 -0
- package/src/id.ts +191 -0
- package/src/index-engine.ts +213 -0
- package/src/index.ts +5 -0
- package/src/query.ts +549 -0
- package/src/store.ts +5 -0
- package/src/utils.ts +115 -0
package/src/query.ts
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deserialize,
|
|
3
|
+
field,
|
|
4
|
+
fixedArray,
|
|
5
|
+
serialize,
|
|
6
|
+
variant,
|
|
7
|
+
vec,
|
|
8
|
+
} from "@dao-xyz/borsh";
|
|
9
|
+
import { randomBytes, sha256Base64Sync, toBase64 } from "@peerbit/crypto";
|
|
10
|
+
import { v4 as uuid } from "uuid";
|
|
11
|
+
import {
|
|
12
|
+
BigUnsignedIntegerValue,
|
|
13
|
+
IntegerValue,
|
|
14
|
+
UnsignedIntegerValue,
|
|
15
|
+
} from "./id.js";
|
|
16
|
+
|
|
17
|
+
export enum Compare {
|
|
18
|
+
Equal = 0,
|
|
19
|
+
Greater = 1,
|
|
20
|
+
GreaterOrEqual = 2,
|
|
21
|
+
Less = 3,
|
|
22
|
+
LessOrEqual = 4,
|
|
23
|
+
}
|
|
24
|
+
export const compare = (
|
|
25
|
+
test: bigint | number,
|
|
26
|
+
compare: Compare,
|
|
27
|
+
value: bigint | number,
|
|
28
|
+
) => {
|
|
29
|
+
switch (compare) {
|
|
30
|
+
case Compare.Equal:
|
|
31
|
+
// eslint-disable-next-line eqeqeq
|
|
32
|
+
return test == value; // == because with want bigint == number at some cases
|
|
33
|
+
case Compare.Greater:
|
|
34
|
+
return test > value;
|
|
35
|
+
case Compare.GreaterOrEqual:
|
|
36
|
+
return test >= value;
|
|
37
|
+
case Compare.Less:
|
|
38
|
+
return test < value;
|
|
39
|
+
case Compare.LessOrEqual:
|
|
40
|
+
return test <= value;
|
|
41
|
+
default:
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.warn("Unexpected compare");
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/// ----- QUERY -----
|
|
49
|
+
|
|
50
|
+
export abstract class Query {
|
|
51
|
+
clone() {
|
|
52
|
+
return deserialize(serialize(this), this.constructor) as this;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export enum SortDirection {
|
|
57
|
+
ASC = 0,
|
|
58
|
+
DESC = 1,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@variant(0)
|
|
62
|
+
export class Sort {
|
|
63
|
+
@field({ type: vec("string") })
|
|
64
|
+
key: string[];
|
|
65
|
+
|
|
66
|
+
@field({ type: "u8" })
|
|
67
|
+
direction: SortDirection;
|
|
68
|
+
|
|
69
|
+
constructor(properties: {
|
|
70
|
+
key: string[] | string;
|
|
71
|
+
direction?: SortDirection | "asc" | "desc";
|
|
72
|
+
}) {
|
|
73
|
+
this.key = Array.isArray(properties.key)
|
|
74
|
+
? properties.key
|
|
75
|
+
: [properties.key];
|
|
76
|
+
if (properties.direction) {
|
|
77
|
+
if (properties.direction === "asc") {
|
|
78
|
+
this.direction = SortDirection.ASC;
|
|
79
|
+
} else if (properties.direction === "desc") {
|
|
80
|
+
this.direction = SortDirection.DESC;
|
|
81
|
+
} else {
|
|
82
|
+
this.direction = properties.direction;
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
this.direction = SortDirection.ASC;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export abstract class AbstractSearchRequest {}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Search with query and collect with sort conditionss
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
const toArray = <T>(arr: T | T[] | undefined) =>
|
|
97
|
+
(arr ? (Array.isArray(arr) ? arr : [arr]) : undefined) || [];
|
|
98
|
+
|
|
99
|
+
@variant(0)
|
|
100
|
+
export class SearchRequest extends AbstractSearchRequest {
|
|
101
|
+
@field({ type: fixedArray("u8", 32) })
|
|
102
|
+
id: Uint8Array; // Session id
|
|
103
|
+
|
|
104
|
+
@field({ type: vec(Query) })
|
|
105
|
+
query: Query[];
|
|
106
|
+
|
|
107
|
+
@field({ type: vec(Sort) })
|
|
108
|
+
sort: Sort[];
|
|
109
|
+
|
|
110
|
+
@field({ type: "u32" })
|
|
111
|
+
fetch: number;
|
|
112
|
+
|
|
113
|
+
constructor(props?: {
|
|
114
|
+
query?:
|
|
115
|
+
| Query[]
|
|
116
|
+
| Query
|
|
117
|
+
| Record<
|
|
118
|
+
string,
|
|
119
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
120
|
+
>;
|
|
121
|
+
sort?: Sort[] | Sort;
|
|
122
|
+
fetch?: number;
|
|
123
|
+
}) {
|
|
124
|
+
super();
|
|
125
|
+
this.id = randomBytes(32);
|
|
126
|
+
this.query = props?.query ? toQuery(props.query) : [];
|
|
127
|
+
this.sort = toArray(props?.sort);
|
|
128
|
+
this.fetch = props?.fetch ?? 10; // default fetch 10 documents
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private _idString: string;
|
|
132
|
+
get idString(): string {
|
|
133
|
+
return this._idString || (this._idString = sha256Base64Sync(this.id));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Collect documents from peers using 'collect' session ids. This is used for distributed sorting internally
|
|
139
|
+
*/
|
|
140
|
+
@variant(1)
|
|
141
|
+
export class CollectNextRequest extends AbstractSearchRequest {
|
|
142
|
+
@field({ type: fixedArray("u8", 32) })
|
|
143
|
+
id: Uint8Array; // collect with id
|
|
144
|
+
|
|
145
|
+
@field({ type: "u32" })
|
|
146
|
+
amount: number; // number of documents to ask for
|
|
147
|
+
|
|
148
|
+
constructor(properties: { id: Uint8Array; amount: number }) {
|
|
149
|
+
super();
|
|
150
|
+
this.id = properties.id;
|
|
151
|
+
this.amount = properties.amount;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private _idString: string;
|
|
155
|
+
get idString(): string {
|
|
156
|
+
return this._idString || (this._idString = sha256Base64Sync(this.id));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@variant(2)
|
|
161
|
+
export class CloseIteratorRequest extends AbstractSearchRequest {
|
|
162
|
+
@field({ type: fixedArray("u8", 32) })
|
|
163
|
+
id: Uint8Array; // collect with id
|
|
164
|
+
|
|
165
|
+
constructor(properties: { id: Uint8Array }) {
|
|
166
|
+
super();
|
|
167
|
+
this.id = properties.id;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private _idString: string;
|
|
171
|
+
get idString(): string {
|
|
172
|
+
return this._idString || (this._idString = sha256Base64Sync(this.id));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@variant(1)
|
|
177
|
+
export abstract class LogicalQuery extends Query {}
|
|
178
|
+
|
|
179
|
+
@variant(0)
|
|
180
|
+
export class And extends LogicalQuery {
|
|
181
|
+
@field({ type: vec(Query) })
|
|
182
|
+
and: Query[];
|
|
183
|
+
|
|
184
|
+
constructor(and: Query[]) {
|
|
185
|
+
super();
|
|
186
|
+
this.and = and;
|
|
187
|
+
|
|
188
|
+
if (this.and.length === 0) {
|
|
189
|
+
throw new Error("And query must have at least one query");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@variant(1)
|
|
195
|
+
export class Or extends LogicalQuery {
|
|
196
|
+
@field({ type: vec(Query) })
|
|
197
|
+
or: Query[];
|
|
198
|
+
|
|
199
|
+
constructor(or: Query[]) {
|
|
200
|
+
super();
|
|
201
|
+
this.or = or;
|
|
202
|
+
|
|
203
|
+
if (this.or.length === 0) {
|
|
204
|
+
throw new Error("Or query must have at least one query");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@variant(2)
|
|
210
|
+
export class Not extends LogicalQuery {
|
|
211
|
+
@field({ type: Query })
|
|
212
|
+
not: Query;
|
|
213
|
+
|
|
214
|
+
constructor(not: Query) {
|
|
215
|
+
super();
|
|
216
|
+
this.not = not;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@variant(2)
|
|
221
|
+
export abstract class StateQuery extends Query {}
|
|
222
|
+
|
|
223
|
+
@variant(1)
|
|
224
|
+
export class StateFieldQuery extends StateQuery {
|
|
225
|
+
@field({ type: vec("string") })
|
|
226
|
+
key: string[];
|
|
227
|
+
|
|
228
|
+
constructor(props: { key: string[] | string }) {
|
|
229
|
+
super();
|
|
230
|
+
this.key = Array.isArray(props.key) ? props.key : [props.key];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@variant(1)
|
|
235
|
+
export class ByteMatchQuery extends StateFieldQuery {
|
|
236
|
+
@field({ type: Uint8Array })
|
|
237
|
+
value: Uint8Array;
|
|
238
|
+
|
|
239
|
+
@field({ type: "u8" })
|
|
240
|
+
// @ts-expect-error: unused
|
|
241
|
+
private _reserved: number; // Replicate MemoryCompare query with this?
|
|
242
|
+
|
|
243
|
+
constructor(props: { key: string[] | string; value: Uint8Array }) {
|
|
244
|
+
super(props);
|
|
245
|
+
this.value = props.value;
|
|
246
|
+
this._reserved = 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_valueString: string;
|
|
250
|
+
/**
|
|
251
|
+
* value `asString`
|
|
252
|
+
*/
|
|
253
|
+
get valueString() {
|
|
254
|
+
return this._valueString ?? (this._valueString = toBase64(this.value));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export enum StringMatchMethod {
|
|
259
|
+
"exact" = 0,
|
|
260
|
+
"prefix" = 1,
|
|
261
|
+
"contains" = 2,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@variant(2)
|
|
265
|
+
export class StringMatch extends StateFieldQuery {
|
|
266
|
+
@field({ type: "string" })
|
|
267
|
+
value: string;
|
|
268
|
+
|
|
269
|
+
@field({ type: "u8" })
|
|
270
|
+
method: StringMatchMethod;
|
|
271
|
+
|
|
272
|
+
@field({ type: "bool" })
|
|
273
|
+
caseInsensitive: boolean;
|
|
274
|
+
|
|
275
|
+
constructor(props: {
|
|
276
|
+
key: string[] | string;
|
|
277
|
+
value: string;
|
|
278
|
+
method?: StringMatchMethod;
|
|
279
|
+
caseInsensitive?: boolean;
|
|
280
|
+
}) {
|
|
281
|
+
super(props);
|
|
282
|
+
this.value = props.value;
|
|
283
|
+
this.method = props.method ?? StringMatchMethod.exact;
|
|
284
|
+
this.caseInsensitive = props.caseInsensitive ?? false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@variant(3)
|
|
289
|
+
export class IntegerCompare extends StateFieldQuery {
|
|
290
|
+
@field({ type: "u8" })
|
|
291
|
+
compare: Compare;
|
|
292
|
+
|
|
293
|
+
@field({ type: IntegerValue })
|
|
294
|
+
value: IntegerValue;
|
|
295
|
+
|
|
296
|
+
constructor(props: {
|
|
297
|
+
key: string[] | string;
|
|
298
|
+
value: bigint | number | IntegerValue;
|
|
299
|
+
compare: "eq" | "gt" | "gte" | "lt" | "lte" | Compare;
|
|
300
|
+
}) {
|
|
301
|
+
super(props);
|
|
302
|
+
if (props.value instanceof IntegerValue) {
|
|
303
|
+
this.value = props.value;
|
|
304
|
+
} else {
|
|
305
|
+
if (typeof props.value === "bigint") {
|
|
306
|
+
this.value = new BigUnsignedIntegerValue(props.value);
|
|
307
|
+
} else {
|
|
308
|
+
this.value = new UnsignedIntegerValue(props.value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (typeof props.compare === "string") {
|
|
313
|
+
if (props.compare === "eq") {
|
|
314
|
+
this.compare = Compare.Equal;
|
|
315
|
+
} else if (props.compare === "gt") {
|
|
316
|
+
this.compare = Compare.Greater;
|
|
317
|
+
} else if (props.compare === "gte") {
|
|
318
|
+
this.compare = Compare.GreaterOrEqual;
|
|
319
|
+
} else if (props.compare === "lt") {
|
|
320
|
+
this.compare = Compare.Less;
|
|
321
|
+
} else if (props.compare === "lte") {
|
|
322
|
+
this.compare = Compare.LessOrEqual;
|
|
323
|
+
} else {
|
|
324
|
+
throw new Error("Invalid compare string");
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
this.compare = props.compare;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@variant(4)
|
|
333
|
+
export class IsNull extends StateFieldQuery {}
|
|
334
|
+
|
|
335
|
+
@variant(5)
|
|
336
|
+
export class BoolQuery extends StateFieldQuery {
|
|
337
|
+
@field({ type: "bool" })
|
|
338
|
+
value: boolean;
|
|
339
|
+
|
|
340
|
+
constructor(props: { key: string[] | string; value: boolean }) {
|
|
341
|
+
super(props);
|
|
342
|
+
this.value = props.value;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@variant(2)
|
|
347
|
+
export class Nested extends Query {
|
|
348
|
+
@field({ type: "string" })
|
|
349
|
+
id: string;
|
|
350
|
+
|
|
351
|
+
@field({ type: "string" })
|
|
352
|
+
path: string;
|
|
353
|
+
|
|
354
|
+
@field({ type: vec(Query) })
|
|
355
|
+
query: Query[];
|
|
356
|
+
|
|
357
|
+
constructor(props: {
|
|
358
|
+
id?: string;
|
|
359
|
+
path: string;
|
|
360
|
+
query:
|
|
361
|
+
| Query[]
|
|
362
|
+
| Query
|
|
363
|
+
| Record<
|
|
364
|
+
string,
|
|
365
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
366
|
+
>;
|
|
367
|
+
}) {
|
|
368
|
+
super();
|
|
369
|
+
this.path = props.path;
|
|
370
|
+
this.id = props.id ?? uuid();
|
|
371
|
+
this.query = toQuery(props.query);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// TODO MemoryCompareQuery can be replaces with ByteMatchQuery? Or Nesteed Queries + ByteMatchQuery?
|
|
376
|
+
/* @variant(0)
|
|
377
|
+
export class MemoryCompare {
|
|
378
|
+
@field({ type: Uint8Array })
|
|
379
|
+
bytes: Uint8Array;
|
|
380
|
+
|
|
381
|
+
@field({ type: "u64" })
|
|
382
|
+
offset: bigint;
|
|
383
|
+
|
|
384
|
+
constructor(opts?: { bytes: Uint8Array; offset: bigint }) {
|
|
385
|
+
if (opts) {
|
|
386
|
+
this.bytes = opts.bytes;
|
|
387
|
+
this.offset = opts.offset;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@variant(4)
|
|
393
|
+
export class MemoryCompareQuery extends Query {
|
|
394
|
+
@field({ type: vec(MemoryCompare) })
|
|
395
|
+
compares: MemoryCompare[];
|
|
396
|
+
|
|
397
|
+
constructor(opts?: { compares: MemoryCompare[] }) {
|
|
398
|
+
super();
|
|
399
|
+
if (opts) {
|
|
400
|
+
this.compares = opts.compares;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} */
|
|
404
|
+
|
|
405
|
+
export abstract class AbstractAggregationRequest {}
|
|
406
|
+
|
|
407
|
+
@variant(0)
|
|
408
|
+
export class SumRequest extends AbstractAggregationRequest {
|
|
409
|
+
@field({ type: fixedArray("u8", 32) })
|
|
410
|
+
id: Uint8Array;
|
|
411
|
+
|
|
412
|
+
@field({ type: vec(Query) })
|
|
413
|
+
query: Query[];
|
|
414
|
+
|
|
415
|
+
@field({ type: vec("string") })
|
|
416
|
+
key: string[];
|
|
417
|
+
|
|
418
|
+
constructor(props: {
|
|
419
|
+
query?:
|
|
420
|
+
| Query[]
|
|
421
|
+
| Query
|
|
422
|
+
| Record<
|
|
423
|
+
string,
|
|
424
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
425
|
+
>;
|
|
426
|
+
key: string[] | string;
|
|
427
|
+
}) {
|
|
428
|
+
super();
|
|
429
|
+
this.id = randomBytes(32);
|
|
430
|
+
this.query = props.query ? toQuery(props.query) : [];
|
|
431
|
+
this.key = Array.isArray(props.key) ? props.key : [props.key];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export abstract class AbstractCountRequest {}
|
|
436
|
+
|
|
437
|
+
@variant(0)
|
|
438
|
+
export class CountRequest extends AbstractCountRequest {
|
|
439
|
+
@field({ type: fixedArray("u8", 32) })
|
|
440
|
+
id: Uint8Array; // Session id
|
|
441
|
+
|
|
442
|
+
@field({ type: vec(Query) })
|
|
443
|
+
query: Query[];
|
|
444
|
+
|
|
445
|
+
constructor(
|
|
446
|
+
props: {
|
|
447
|
+
query:
|
|
448
|
+
| Query[]
|
|
449
|
+
| Query
|
|
450
|
+
| Record<
|
|
451
|
+
string,
|
|
452
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
453
|
+
>;
|
|
454
|
+
} = { query: [] },
|
|
455
|
+
) {
|
|
456
|
+
super();
|
|
457
|
+
this.id = randomBytes(32);
|
|
458
|
+
this.query = toQuery(props.query);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export abstract class AbstractDeleteRequest {}
|
|
463
|
+
|
|
464
|
+
@variant(0)
|
|
465
|
+
export class DeleteRequest extends AbstractDeleteRequest {
|
|
466
|
+
@field({ type: fixedArray("u8", 32) })
|
|
467
|
+
id: Uint8Array; // Session id
|
|
468
|
+
|
|
469
|
+
@field({ type: vec(Query) })
|
|
470
|
+
query: Query[];
|
|
471
|
+
|
|
472
|
+
constructor(props: {
|
|
473
|
+
query:
|
|
474
|
+
| Query[]
|
|
475
|
+
| Query
|
|
476
|
+
| Record<
|
|
477
|
+
string,
|
|
478
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
479
|
+
>;
|
|
480
|
+
}) {
|
|
481
|
+
super();
|
|
482
|
+
this.id = randomBytes(32);
|
|
483
|
+
this.query = toQuery(props.query);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const toQuery = (
|
|
488
|
+
query:
|
|
489
|
+
| Query[]
|
|
490
|
+
| Query
|
|
491
|
+
| Record<
|
|
492
|
+
string,
|
|
493
|
+
string | number | bigint | Uint8Array | boolean | null | undefined
|
|
494
|
+
>,
|
|
495
|
+
) => {
|
|
496
|
+
if (Array.isArray(query)) {
|
|
497
|
+
return query;
|
|
498
|
+
}
|
|
499
|
+
if (query instanceof Query) {
|
|
500
|
+
return [query];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return convertQueryRecordToObject(query);
|
|
504
|
+
};
|
|
505
|
+
const convertQueryRecordToObject = (
|
|
506
|
+
obj: Record<
|
|
507
|
+
string,
|
|
508
|
+
| string
|
|
509
|
+
| number
|
|
510
|
+
| bigint
|
|
511
|
+
| Uint8Array
|
|
512
|
+
| boolean
|
|
513
|
+
| null
|
|
514
|
+
| undefined
|
|
515
|
+
| Record<string, any>
|
|
516
|
+
>,
|
|
517
|
+
queries: Query[] = [],
|
|
518
|
+
path?: string[],
|
|
519
|
+
) => {
|
|
520
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
521
|
+
let mergedKey = path ? [...path, k] : [k];
|
|
522
|
+
if (typeof v === "object" && v instanceof Uint8Array === false) {
|
|
523
|
+
convertQueryRecordToObject(v!, queries, mergedKey);
|
|
524
|
+
} else {
|
|
525
|
+
const matcher = getMatcher(mergedKey, v);
|
|
526
|
+
queries.push(matcher);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return queries;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
export const getMatcher = (
|
|
533
|
+
key: string[],
|
|
534
|
+
value: string | number | bigint | Uint8Array | boolean | null | undefined,
|
|
535
|
+
) => {
|
|
536
|
+
if (typeof value === "string") {
|
|
537
|
+
return new StringMatch({ key, value });
|
|
538
|
+
} else if (typeof value === "bigint" || typeof value === "number") {
|
|
539
|
+
return new IntegerCompare({ key, value, compare: Compare.Equal });
|
|
540
|
+
} else if (typeof value === "boolean") {
|
|
541
|
+
return new BoolQuery({ key, value });
|
|
542
|
+
} else if (value == null) {
|
|
543
|
+
return new IsNull({ key });
|
|
544
|
+
} else if (value instanceof Uint8Array) {
|
|
545
|
+
return new ByteMatchQuery({ key, value });
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
throw new Error("Invalid query value");
|
|
549
|
+
};
|
package/src/store.ts
ADDED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AbstractType,
|
|
3
|
+
type CustomField,
|
|
4
|
+
type SimpleField,
|
|
5
|
+
field,
|
|
6
|
+
getSchema,
|
|
7
|
+
} from "@dao-xyz/borsh";
|
|
8
|
+
import { type Sort, SortDirection } from "./query.js";
|
|
9
|
+
|
|
10
|
+
export const stringArraysEquals = (
|
|
11
|
+
a: string[] | string,
|
|
12
|
+
b: string[] | string,
|
|
13
|
+
) => {
|
|
14
|
+
if (a.length !== b.length) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
for (let i = 0; i < a.length; i++) {
|
|
18
|
+
if (a[i] !== b[i]) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/* export const resolvedSort = async <
|
|
26
|
+
Q extends { indexed: Record<string, any> }
|
|
27
|
+
>(
|
|
28
|
+
arr: Q[],
|
|
29
|
+
sorts: Sort[],
|
|
30
|
+
aliases?: Map<string, string>
|
|
31
|
+
) => {
|
|
32
|
+
arr.sort((a, b) => extractSortCompare(a.indexed, b.indexed, sorts));
|
|
33
|
+
return arr;
|
|
34
|
+
};
|
|
35
|
+
*/
|
|
36
|
+
export const extractSortCompare = (
|
|
37
|
+
a: Record<string, any>,
|
|
38
|
+
b: Record<string, any>,
|
|
39
|
+
sorts: Sort[],
|
|
40
|
+
aliases?: Map<string, string>,
|
|
41
|
+
) => {
|
|
42
|
+
for (const sort of sorts) {
|
|
43
|
+
const av = extractFieldValue(a, sort.key, aliases);
|
|
44
|
+
const bv = extractFieldValue(b, sort.key, aliases);
|
|
45
|
+
const cmp = sortCompare(av, bv);
|
|
46
|
+
if (cmp !== 0) {
|
|
47
|
+
if (sort.direction === SortDirection.ASC) {
|
|
48
|
+
return cmp;
|
|
49
|
+
} else {
|
|
50
|
+
return -cmp;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return 0;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const sortCompare = (av: any, bv: any) => {
|
|
58
|
+
if (typeof av === "string" && typeof bv === "string") {
|
|
59
|
+
return av.localeCompare(bv);
|
|
60
|
+
}
|
|
61
|
+
if (av < bv) {
|
|
62
|
+
return -1;
|
|
63
|
+
} else if (av > bv) {
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
return 0;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const extractFieldValue = <T>(
|
|
70
|
+
doc: any,
|
|
71
|
+
path: string[],
|
|
72
|
+
aliases?: Map<string, string>,
|
|
73
|
+
): T => {
|
|
74
|
+
for (let i = 0; i < path.length; i++) {
|
|
75
|
+
doc = doc[aliases?.get(path[i]) || path[i]];
|
|
76
|
+
}
|
|
77
|
+
return doc;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const INDEXED_ID_META_PROPERY = "_index_id";
|
|
81
|
+
|
|
82
|
+
export function id(properties: SimpleField | CustomField<any>) {
|
|
83
|
+
const innerFn = field(properties);
|
|
84
|
+
return (target: any, name: string) => {
|
|
85
|
+
innerFn(target, name);
|
|
86
|
+
target.constructor[INDEXED_ID_META_PROPERY] = name;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const getIdProperty = (
|
|
91
|
+
clazz: AbstractType<any>,
|
|
92
|
+
): string[] | undefined => {
|
|
93
|
+
// TODO nested id property
|
|
94
|
+
const property = (clazz as any)[INDEXED_ID_META_PROPERY];
|
|
95
|
+
if (!property) {
|
|
96
|
+
// look into children
|
|
97
|
+
|
|
98
|
+
const fields = getSchema(clazz)?.fields;
|
|
99
|
+
if (!fields) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const field of fields) {
|
|
104
|
+
if (typeof field.type === "function") {
|
|
105
|
+
const idFromChild = getIdProperty(field.type);
|
|
106
|
+
if (idFromChild) {
|
|
107
|
+
return [field.key, ...idFromChild];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
return [property as string];
|
|
115
|
+
};
|