@based/db 0.0.66 → 0.0.67

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 (121) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/darwin_aarch64/include/selva/colvec.h +71 -0
  3. package/dist/lib/darwin_aarch64/include/selva/db.h +3 -0
  4. package/dist/lib/darwin_aarch64/include/selva/fields.h +1 -1
  5. package/dist/lib/darwin_aarch64/include/selva/types.h +7 -0
  6. package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
  7. package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
  8. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  9. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  10. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  11. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  12. package/dist/lib/darwin_aarch64/libnode-v24.node +0 -0
  13. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  14. package/dist/lib/darwin_aarch64/libxxhash.dylib +0 -0
  15. package/dist/lib/linux_aarch64/include/selva/colvec.h +71 -0
  16. package/dist/lib/linux_aarch64/include/selva/db.h +3 -0
  17. package/dist/lib/linux_aarch64/include/selva/fields.h +1 -1
  18. package/dist/lib/linux_aarch64/include/selva/types.h +7 -0
  19. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  20. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  21. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  22. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  23. package/dist/lib/linux_aarch64/libnode-v24.node +0 -0
  24. package/dist/lib/linux_aarch64/libselva.so +0 -0
  25. package/dist/lib/linux_x86_64/include/selva/colvec.h +71 -0
  26. package/dist/lib/linux_x86_64/include/selva/db.h +3 -0
  27. package/dist/lib/linux_x86_64/include/selva/fields.h +1 -1
  28. package/dist/lib/linux_x86_64/include/selva/types.h +7 -0
  29. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  30. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  31. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  32. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  33. package/dist/lib/linux_x86_64/libnode-v24.node +0 -0
  34. package/dist/lib/linux_x86_64/libselva.so +0 -0
  35. package/dist/src/client/flushModify.d.ts +2 -1
  36. package/dist/src/client/flushModify.js +12 -4
  37. package/dist/src/client/modify/create.js +11 -0
  38. package/dist/src/client/modify/delete.js +3 -0
  39. package/dist/src/client/modify/modify.js +2 -2
  40. package/dist/src/client/modify/setCursor.d.ts +2 -1
  41. package/dist/src/client/query/BasedDbQuery.d.ts +7 -2
  42. package/dist/src/client/query/BasedDbQuery.js +111 -14
  43. package/dist/src/client/query/aggregates/aggregation.js +24 -11
  44. package/dist/src/client/query/aggregates/types.d.ts +21 -2
  45. package/dist/src/client/query/aggregates/types.js +34 -1
  46. package/dist/src/client/query/display.js +8 -2
  47. package/dist/src/client/query/filter/createVariableFilterBuffer.d.ts +2 -3
  48. package/dist/src/client/query/filter/createVariableFilterBuffer.js +20 -7
  49. package/dist/src/client/query/filter/filter.js +13 -3
  50. package/dist/src/client/query/filter/primitiveFilter.d.ts +1 -2
  51. package/dist/src/client/query/include/props.js +18 -2
  52. package/dist/src/client/query/include/toBuffer.js +11 -3
  53. package/dist/src/client/query/include/walk.js +5 -1
  54. package/dist/src/client/query/queryDef.js +4 -1
  55. package/dist/src/client/query/read/read.js +50 -23
  56. package/dist/src/client/query/registerQuery.js +1 -0
  57. package/dist/src/client/query/search/index.d.ts +1 -1
  58. package/dist/src/client/query/search/index.js +21 -7
  59. package/dist/src/client/query/sort.d.ts +1 -1
  60. package/dist/src/client/query/toByteCode/default.d.ts +1 -1
  61. package/dist/src/client/query/toByteCode/default.js +0 -2
  62. package/dist/src/client/query/toByteCode/toBuffer.js +0 -7
  63. package/dist/src/client/query/types.d.ts +16 -5
  64. package/dist/src/client/query/validation.js +25 -2
  65. package/dist/src/client/xxHash64.d.ts +1 -1
  66. package/dist/src/index.d.ts +0 -1
  67. package/dist/src/index.js +0 -1
  68. package/dist/src/native.d.ts +2 -2
  69. package/dist/src/native.js +7 -8
  70. package/dist/src/server/IoWorker.d.ts +8 -0
  71. package/dist/src/server/IoWorker.js +39 -0
  72. package/dist/src/server/QueryWorker.d.ts +8 -0
  73. package/dist/src/server/QueryWorker.js +26 -0
  74. package/dist/src/server/blocks.d.ts +24 -0
  75. package/dist/src/server/blocks.js +112 -0
  76. package/dist/src/server/dbHash.d.ts +1 -1
  77. package/dist/src/server/index.d.ts +9 -16
  78. package/dist/src/server/index.js +37 -15
  79. package/dist/src/server/migrate/index.d.ts +5 -0
  80. package/dist/src/server/migrate/index.js +10 -7
  81. package/dist/src/server/save.d.ts +8 -6
  82. package/dist/src/server/save.js +34 -78
  83. package/dist/src/server/schema.js +6 -5
  84. package/dist/src/server/start.js +57 -60
  85. package/dist/src/server/tree.d.ts +24 -13
  86. package/dist/src/server/tree.js +95 -66
  87. package/dist/src/server/workers/DbWorker.d.ts +17 -0
  88. package/dist/src/server/{DbWorker.js → workers/DbWorker.js} +15 -17
  89. package/dist/src/server/workers/io_worker.js +39 -0
  90. package/dist/src/server/workers/io_worker_types.d.ts +12 -0
  91. package/dist/src/server/workers/io_worker_types.js +2 -0
  92. package/dist/src/server/workers/query_worker.d.ts +1 -0
  93. package/dist/src/server/workers/query_worker.js +4 -0
  94. package/dist/src/server/workers/worker.d.ts +1 -0
  95. package/dist/src/server/workers/worker.js +41 -0
  96. package/dist/src/shared/Emitter.d.ts +1 -0
  97. package/dist/src/types.d.ts +1 -1
  98. package/dist/src/types.js +1 -1
  99. package/package.json +3 -3
  100. package/dist/lib/darwin_aarch64/include/selva/history.h +0 -64
  101. package/dist/lib/linux_aarch64/include/selva/history.h +0 -64
  102. package/dist/lib/linux_x86_64/include/selva/history.h +0 -64
  103. package/dist/src/client/query/serialize.d.ts +0 -4
  104. package/dist/src/client/query/serialize.js +0 -26
  105. package/dist/src/server/DbWorker.d.ts +0 -13
  106. package/dist/src/server/csmt/draw-dot.d.ts +0 -4
  107. package/dist/src/server/csmt/draw-dot.js +0 -38
  108. package/dist/src/server/csmt/index.d.ts +0 -4
  109. package/dist/src/server/csmt/index.js +0 -5
  110. package/dist/src/server/csmt/match.d.ts +0 -7
  111. package/dist/src/server/csmt/match.js +0 -10
  112. package/dist/src/server/csmt/memebership-proof.d.ts +0 -7
  113. package/dist/src/server/csmt/memebership-proof.js +0 -122
  114. package/dist/src/server/csmt/tree-utils.d.ts +0 -6
  115. package/dist/src/server/csmt/tree-utils.js +0 -33
  116. package/dist/src/server/csmt/tree.d.ts +0 -3
  117. package/dist/src/server/csmt/tree.js +0 -270
  118. package/dist/src/server/csmt/types.d.ts +0 -46
  119. package/dist/src/server/csmt/types.js +0 -2
  120. package/dist/src/server/worker.js +0 -33
  121. /package/dist/src/server/{worker.d.ts → workers/io_worker.d.ts} +0 -0
@@ -11,7 +11,7 @@ import { langCodesMap } from '@based/schema';
11
11
  import { convertFilter } from './filter/convertFilter.js';
12
12
  import { validateLocale, validateRange } from './validation.js';
13
13
  import { DEF_RANGE_PROP_LIMIT } from './thresholds.js';
14
- import { wait } from '@saulx/utils';
14
+ import { AggregateType } from './aggregates/types.js';
15
15
  import { displayTarget } from './display.js';
16
16
  import picocolors from 'picocolors';
17
17
  export class QueryBranch {
@@ -152,7 +152,7 @@ export class QueryBranch {
152
152
  }
153
153
  else {
154
154
  const p = field.split('.');
155
- addAggregate(2 /* AggregateType.COUNT */, this.def, p);
155
+ addAggregate(AggregateType.COUNT, this.def, p);
156
156
  }
157
157
  // @ts-ignore
158
158
  return this;
@@ -168,7 +168,7 @@ export class QueryBranch {
168
168
  });
169
169
  }
170
170
  else {
171
- addAggregate(1 /* AggregateType.SUM */, this.def, fields);
171
+ addAggregate(AggregateType.SUM, this.def, fields);
172
172
  }
173
173
  // @ts-ignore
174
174
  return this;
@@ -184,7 +184,87 @@ export class QueryBranch {
184
184
  });
185
185
  }
186
186
  else {
187
- addAggregate(3 /* AggregateType.CARDINALITY */, this.def, fields);
187
+ addAggregate(AggregateType.CARDINALITY, this.def, fields);
188
+ }
189
+ // @ts-ignore
190
+ return this;
191
+ }
192
+ stddev(...fields) {
193
+ if (fields.length === 0) {
194
+ throw new Error('Empty standard deviation function called');
195
+ }
196
+ if (this.queryCommands) {
197
+ this.queryCommands.push({
198
+ method: 'stddev',
199
+ args: fields,
200
+ });
201
+ }
202
+ else {
203
+ addAggregate(AggregateType.STDDEV, this.def, fields);
204
+ }
205
+ // @ts-ignore
206
+ return this;
207
+ }
208
+ var(...fields) {
209
+ if (fields.length === 0) {
210
+ throw new Error('Empty variance function called');
211
+ }
212
+ if (this.queryCommands) {
213
+ this.queryCommands.push({
214
+ method: 'var',
215
+ args: fields,
216
+ });
217
+ }
218
+ else {
219
+ addAggregate(AggregateType.VARIANCE, this.def, fields);
220
+ }
221
+ // @ts-ignore
222
+ return this;
223
+ }
224
+ avg(...fields) {
225
+ if (fields.length === 0) {
226
+ throw new Error('Empty average function called');
227
+ }
228
+ if (this.queryCommands) {
229
+ this.queryCommands.push({
230
+ method: 'avg',
231
+ args: fields,
232
+ });
233
+ }
234
+ else {
235
+ addAggregate(AggregateType.AVERAGE, this.def, fields);
236
+ }
237
+ // @ts-ignore
238
+ return this;
239
+ }
240
+ max(...fields) {
241
+ if (fields.length === 0) {
242
+ throw new Error('Empty maximum function called');
243
+ }
244
+ if (this.queryCommands) {
245
+ this.queryCommands.push({
246
+ method: 'max',
247
+ args: fields,
248
+ });
249
+ }
250
+ else {
251
+ addAggregate(AggregateType.MAX, this.def, fields);
252
+ }
253
+ // @ts-ignore
254
+ return this;
255
+ }
256
+ min(...fields) {
257
+ if (fields.length === 0) {
258
+ throw new Error('Empty minimum function called');
259
+ }
260
+ if (this.queryCommands) {
261
+ this.queryCommands.push({
262
+ method: 'min',
263
+ args: fields,
264
+ });
265
+ }
266
+ else {
267
+ addAggregate(AggregateType.MIN, this.def, fields);
188
268
  }
189
269
  // @ts-ignore
190
270
  return this;
@@ -354,13 +434,19 @@ export class BasedDbQuery extends QueryBranch {
354
434
  const res = await this.db.hooks.getQueryBuf(buf);
355
435
  if (res.byteLength === 1) {
356
436
  if (res[0] === 0) {
357
- this.reset();
358
- this.db.emit('info', 'query get schema mismatch - awaiting new schema (max 15s)');
359
- const ok = await Promise.race([wait(15e3), this.db.once('schema')]);
360
- if (!ok) {
361
- reject(new Error('schema mismath'));
437
+ if (this.def && this.def.schemaChecksum === this.db.schema?.hash) {
438
+ // my schema did not change since last time, wait for the schema to change
439
+ this.reset();
440
+ this.db.emit('info', 'query get schema mismatch - awaiting new schema');
441
+ await this.db.once('schema');
442
+ return this.#getInternal(resolve, reject);
443
+ }
444
+ else {
445
+ // its changed so lets send again
446
+ this.db.emit('info', 'query get schema mismatch - got the same already');
447
+ this.reset();
448
+ return this.#getInternal(resolve, reject);
362
449
  }
363
- return this.#getInternal(resolve, reject);
364
450
  }
365
451
  else {
366
452
  reject(new Error('unexpected error'));
@@ -382,16 +468,28 @@ export class BasedDbQuery extends QueryBranch {
382
468
  register() {
383
469
  registerQuery(this);
384
470
  }
385
- locale(locale) {
471
+ locale(locale, fallBack) {
386
472
  if (this.queryCommands) {
387
- this.queryCommands.push({
473
+ this.queryCommands.unshift({
388
474
  method: 'locale',
389
475
  args: [locale],
390
476
  });
391
477
  }
392
478
  else {
479
+ if (fallBack === undefined) {
480
+ // Uses fallback from schema if available
481
+ const localeDescriptor = this.def.schema.locales[locale];
482
+ fallBack =
483
+ typeof localeDescriptor === 'object'
484
+ ? localeDescriptor.fallback || false
485
+ : false;
486
+ }
393
487
  validateLocale(this.def, locale);
394
- this.def.lang = langCodesMap.get(locale) ?? 0;
488
+ const fallBackCode = fallBack === false ? [] : [langCodesMap.get(fallBack)];
489
+ this.def.lang = {
490
+ lang: langCodesMap.get(locale) ?? 0,
491
+ fallback: fallBackCode,
492
+ };
395
493
  }
396
494
  return this;
397
495
  }
@@ -401,7 +499,6 @@ export class BasedDbQuery extends QueryBranch {
401
499
  onData(res);
402
500
  }
403
501
  catch (err) {
404
- // const t = displayTarget(this.def)
405
502
  const def = this.def;
406
503
  let name = picocolors.red(`QueryError[${displayTarget(def)}]\n`);
407
504
  name += ` Error executing onData handler in subscription\n`;
@@ -1,7 +1,9 @@
1
1
  import { writeUint16 } from '@saulx/utils';
2
2
  import { QueryDefType } from '../types.js';
3
+ import { AggregateType } from './types.js';
3
4
  import { UINT32 } from '@based/schema/def';
4
5
  import { aggregationFieldDoesNotExist } from '../validation.js';
6
+ import { aggregateTypeMap } from '../aggregates/types.js';
5
7
  export const aggregateToBuffer = (aggregates) => {
6
8
  const aggBuffer = new Uint8Array(aggregates.size);
7
9
  let i = 0;
@@ -21,7 +23,9 @@ export const aggregateToBuffer = (aggregates) => {
21
23
  aggBuffer[i] = 0 /* GroupBy.NONE */;
22
24
  i += 1;
23
25
  }
24
- writeUint16(aggBuffer, aggregates.totalResultsPos, i);
26
+ writeUint16(aggBuffer, aggregates.totalResultsSize, i);
27
+ i += 2;
28
+ writeUint16(aggBuffer, aggregates.totalAccumulatorSize, i);
25
29
  i += 2;
26
30
  for (const [prop, aggregatesArray] of aggregates.aggregates.entries()) {
27
31
  aggBuffer[i] = prop;
@@ -39,6 +43,8 @@ export const aggregateToBuffer = (aggregates) => {
39
43
  i += 2;
40
44
  writeUint16(aggBuffer, agg.resultPos, i);
41
45
  i += 2;
46
+ writeUint16(aggBuffer, agg.accumulatorPos, i);
47
+ i += 2;
42
48
  size += i - startI;
43
49
  }
44
50
  writeUint16(aggBuffer, size, sizeIndex);
@@ -48,13 +54,13 @@ export const aggregateToBuffer = (aggregates) => {
48
54
  const ensureAggregate = (def) => {
49
55
  if (!def.aggregate) {
50
56
  def.aggregate = {
51
- size: 3,
57
+ size: 5,
52
58
  aggregates: new Map(),
53
- totalResultsPos: 0,
59
+ totalResultsSize: 0,
60
+ totalAccumulatorSize: 0,
54
61
  };
55
62
  }
56
63
  };
57
- // Group by is great for normal stuff as well (do later)
58
64
  export const groupBy = (def, field) => {
59
65
  const fieldDef = def.schema.props[field];
60
66
  if (!fieldDef) {
@@ -74,7 +80,7 @@ export const addAggregate = (type, def, fields) => {
74
80
  addAggregate(type, def, field);
75
81
  }
76
82
  else {
77
- const fieldDef = type === 2 /* AggregateType.COUNT */
83
+ const fieldDef = type === AggregateType.COUNT
78
84
  ? {
79
85
  prop: 255,
80
86
  path: [field],
@@ -98,13 +104,20 @@ export const addAggregate = (type, def, fields) => {
98
104
  aggregateField.push({
99
105
  propDef: fieldDef,
100
106
  type,
101
- resultPos: def.aggregate.totalResultsPos,
107
+ resultPos: def.aggregate.totalResultsSize,
108
+ accumulatorPos: def.aggregate.totalAccumulatorSize,
102
109
  });
103
- // IF FLOAT // NUMBER ETC USE 8!
104
- // do this better
105
- def.aggregate.totalResultsPos += 4;
110
+ const specificSizes = aggregateTypeMap.get(type);
111
+ if (specificSizes) {
112
+ def.aggregate.totalResultsSize += specificSizes.resultsSize;
113
+ def.aggregate.totalAccumulatorSize += specificSizes.accumulatorSize;
114
+ }
115
+ else {
116
+ def.aggregate.totalResultsSize += 8;
117
+ def.aggregate.totalAccumulatorSize += 8;
118
+ }
106
119
  // needs to add an extra field WRITE TO
107
- def.aggregate.size += 6;
120
+ def.aggregate.size += 8;
108
121
  }
109
122
  }
110
123
  };
@@ -128,7 +141,7 @@ export const isRootCountOnly = (def, filterSize) => {
128
141
  if (aggs.length !== 1) {
129
142
  return false;
130
143
  }
131
- if (aggs[0].type !== 2 /* AggregateType.COUNT */) {
144
+ if (aggs[0].type !== AggregateType.COUNT) {
132
145
  return false;
133
146
  }
134
147
  if (def.filter && def.filter.size > 0) {
@@ -1,8 +1,27 @@
1
- export declare const enum AggregateType {
1
+ export declare enum AggregateType {
2
2
  SUM = 1,
3
3
  COUNT = 2,
4
- CARDINALITY = 3
4
+ CARDINALITY = 3,
5
+ STDDEV = 4,
6
+ AVERAGE = 5,
7
+ VARIANCE = 6,
8
+ MAX = 7,
9
+ MIN = 8
5
10
  }
11
+ export declare const enum AccumulatorSize {
12
+ SUM = 8,
13
+ COUNT = 4,
14
+ CARDINALITY = 254,// TODO: accordinly to sparse or dense modes
15
+ STDDEV = 24,// count (u64) + sum (f64) + sum_sq (f64) = 8 + 8 + 8 = 24
16
+ AVERAGE = 16,// count (u64) + sum (f64) = 16
17
+ VARIANCE = 24,// count (u64) + sum (f64) + sum_sq (f64) = 8 + 8 + 8 = 24
18
+ MAX = 8,
19
+ MIN = 8
20
+ }
21
+ export declare const aggregateTypeMap: Map<AggregateType, {
22
+ resultsSize: number;
23
+ accumulatorSize: number;
24
+ }>;
6
25
  export declare const enum GroupBy {
7
26
  NONE = 0,
8
27
  HAS_GROUP = 255
@@ -1,2 +1,35 @@
1
- export {};
1
+ export var AggregateType;
2
+ (function (AggregateType) {
3
+ AggregateType[AggregateType["SUM"] = 1] = "SUM";
4
+ AggregateType[AggregateType["COUNT"] = 2] = "COUNT";
5
+ AggregateType[AggregateType["CARDINALITY"] = 3] = "CARDINALITY";
6
+ AggregateType[AggregateType["STDDEV"] = 4] = "STDDEV";
7
+ AggregateType[AggregateType["AVERAGE"] = 5] = "AVERAGE";
8
+ AggregateType[AggregateType["VARIANCE"] = 6] = "VARIANCE";
9
+ AggregateType[AggregateType["MAX"] = 7] = "MAX";
10
+ AggregateType[AggregateType["MIN"] = 8] = "MIN";
11
+ })(AggregateType || (AggregateType = {}));
12
+ export const aggregateTypeMap = new Map([
13
+ [
14
+ AggregateType.CARDINALITY,
15
+ { resultsSize: 4, accumulatorSize: 254 /* AccumulatorSize.CARDINALITY */ },
16
+ ],
17
+ [
18
+ AggregateType.COUNT,
19
+ { resultsSize: 4, accumulatorSize: 4 /* AccumulatorSize.COUNT */ },
20
+ ],
21
+ [
22
+ AggregateType.STDDEV,
23
+ { resultsSize: 8, accumulatorSize: 24 /* AccumulatorSize.STDDEV */ },
24
+ ],
25
+ [
26
+ AggregateType.AVERAGE,
27
+ { resultsSize: 8, accumulatorSize: 16 /* AccumulatorSize.AVERAGE */ },
28
+ ],
29
+ [
30
+ AggregateType.VARIANCE,
31
+ { resultsSize: 8, accumulatorSize: 24 /* AccumulatorSize.VARIANCE */ },
32
+ ],
33
+ // Othe types like MAX, MIN, SUM fall in the else case in aggregation.ts 8/8
34
+ ]);
2
35
  //# sourceMappingURL=types.js.map
@@ -1,6 +1,7 @@
1
1
  import picocolors from 'picocolors';
2
2
  import { BINARY, CARDINALITY, NUMBER, REFERENCE, REFERENCES, STRING, TEXT, TIMESTAMP, } from '@based/schema/def';
3
3
  import { ENCODER } from '@saulx/utils';
4
+ import { AggregateType } from './aggregates/types.js';
4
5
  const decimals = (v) => ~~(v * 100) / 100;
5
6
  const sizeCalc = (size) => {
6
7
  if (size > 1e6) {
@@ -158,7 +159,10 @@ const inspectObject = (object, q, path, level, isLast, isFirst, isObject, depth)
158
159
  if (typeof v === 'number') {
159
160
  if (q.aggregate) {
160
161
  str += printNumber(v);
161
- str += picocolors.italic(picocolors.dim(` ${key.indexOf('count') >= 0 ? ' count' : ' sum'}`));
162
+ // TBD: replace comptime const enum and reverse map it
163
+ const [[__, akv], _] = q.aggregate.aggregates;
164
+ const aggType = akv[0].type;
165
+ str += picocolors.italic(picocolors.dim(` ${AggregateType[aggType].toLowerCase()}`));
162
166
  str += ',\n';
163
167
  }
164
168
  else {
@@ -219,7 +223,9 @@ const inspectObject = (object, q, path, level, isLast, isFirst, isObject, depth)
219
223
  if (typeof v === 'number') {
220
224
  if (q.aggregate) {
221
225
  str += printNumber(v);
222
- str += picocolors.italic(picocolors.dim(` ${key.indexOf('count') >= 0 ? ' count' : ' sum'}`));
226
+ const [[__, akv], _] = q.aggregate.aggregates;
227
+ const aggType = akv[0].type;
228
+ str += picocolors.italic(picocolors.dim(` ${AggregateType[aggType].toLowerCase()}`));
223
229
  }
224
230
  else {
225
231
  str += printNumber(v);
@@ -1,5 +1,4 @@
1
1
  import { PropDef, PropDefEdge } from '@based/schema/def';
2
2
  import { FilterCtx } from './types.js';
3
- import { LangCode } from '@based/schema';
4
- import { FilterCondition } from '../types.js';
5
- export declare const createVariableFilterBuffer: (value: any, prop: PropDef | PropDefEdge, ctx: FilterCtx, lang: LangCode) => FilterCondition;
3
+ import { FilterCondition, QueryDef } from '../types.js';
4
+ export declare const createVariableFilterBuffer: (value: any, prop: PropDef | PropDefEdge, ctx: FilterCtx, lang: QueryDef["lang"]) => FilterCondition;
@@ -33,9 +33,15 @@ const parseValue = (value, prop, ctx, lang) => {
33
33
  val = ENCODER.encode(val.normalize('NFKD'));
34
34
  }
35
35
  if (prop.typeIndex === TEXT) {
36
- const tmp = new Uint8Array(val.byteLength + 1);
36
+ // 1 + size
37
+ const fallbacksSize = lang.lang === 0 ? 0 : lang.fallback.length;
38
+ const tmp = new Uint8Array(val.byteLength + 2 + fallbacksSize);
37
39
  tmp.set(val);
38
- tmp[tmp.byteLength - 1] = lang;
40
+ tmp[tmp.byteLength - 1] = fallbacksSize;
41
+ tmp[tmp.byteLength - 2] = lang.lang;
42
+ for (let i = 0; i < fallbacksSize; i++) {
43
+ tmp[tmp.byteLength - 2 - fallbacksSize + i] = lang.fallback[i];
44
+ }
39
45
  val = tmp;
40
46
  }
41
47
  }
@@ -90,9 +96,10 @@ export const createVariableFilterBuffer = (value, prop, ctx, lang) => {
90
96
  prop.typeIndex !== ALIAS &&
91
97
  prop.typeIndex !== VECTOR) {
92
98
  if (prop.typeIndex === TEXT) {
93
- const crc = crc32(val.slice(0, -1));
94
- const len = val.byteLength - 1;
95
- const v = new Uint8Array(9);
99
+ const fbLen = 2 + val[val.byteLength - 1];
100
+ const crc = crc32(val.slice(0, -fbLen));
101
+ const len = val.byteLength - fbLen;
102
+ const v = new Uint8Array(8 + fbLen);
96
103
  v[0] = crc;
97
104
  v[1] = crc >>> 8;
98
105
  v[2] = crc >>> 16;
@@ -101,11 +108,17 @@ export const createVariableFilterBuffer = (value, prop, ctx, lang) => {
101
108
  v[5] = len >>> 8;
102
109
  v[6] = len >>> 16;
103
110
  v[7] = len >>> 24;
104
- v[8] = val[val.length - 1];
111
+ for (let i = 0; i < fbLen; i++) {
112
+ v[v.byteLength - (i + 1)] = val[val.byteLength - (i + 1)];
113
+ }
105
114
  parsedCondition = writeVarFilter(mode, v, ctx, prop, 0, 0);
106
115
  }
107
116
  else {
108
- parsedCondition = createFixedFilterBuffer(prop, 8, { operation: EQUAL_CRC32, type: ctx.type, opts: ctx.opts }, val, false);
117
+ parsedCondition = createFixedFilterBuffer(prop, 8, {
118
+ operation: EQUAL_CRC32,
119
+ type: ctx.type,
120
+ opts: ctx.opts,
121
+ }, val, false);
109
122
  }
110
123
  }
111
124
  else {
@@ -22,7 +22,11 @@ const referencesFilter = (db, filter, schema, conditions, def) => {
22
22
  if (edgeDef) {
23
23
  conditions.edges ??= new Map();
24
24
  size +=
25
- 3 + primitiveFilter(def, edgeDef, filter, conditions, def.lang);
25
+ 3 +
26
+ primitiveFilter(def, edgeDef, filter, conditions, {
27
+ lang: def.lang.lang,
28
+ fallback: [],
29
+ });
26
30
  }
27
31
  }
28
32
  }
@@ -71,12 +75,18 @@ export const filterRaw = (db, filter, schema, conditions, def) => {
71
75
  filterInvalidLang(def, field);
72
76
  return 0;
73
77
  }
74
- return primitiveFilter(def, fieldDef, filter, conditions, code);
78
+ return primitiveFilter(def, fieldDef, filter, conditions, {
79
+ lang: code,
80
+ fallback: [],
81
+ });
75
82
  }
76
83
  }
77
84
  if (field === 'id') {
78
85
  fieldDef = ID_FIELD_DEF;
79
- return primitiveFilter(def, fieldDef, filter, conditions, def.lang);
86
+ return primitiveFilter(def, fieldDef, filter, conditions, {
87
+ lang: def.lang.lang,
88
+ fallback: [], // only fallbacks for this
89
+ });
80
90
  }
81
91
  return referencesFilter(db, filter, schema, conditions, def);
82
92
  }
@@ -1,5 +1,4 @@
1
1
  import { PropDef, PropDefEdge } from '@based/schema/def';
2
2
  import { QueryDef, QueryDefFilter } from '../types.js';
3
3
  import { Filter } from './types.js';
4
- import { LangCode } from '@based/schema';
5
- export declare const primitiveFilter: (def: QueryDef, prop: PropDef | PropDefEdge, filter: Filter, conditions: QueryDefFilter, lang: LangCode) => number;
4
+ export declare const primitiveFilter: (def: QueryDef, prop: PropDef | PropDefEdge, filter: Filter, conditions: QueryDefFilter, lang: QueryDef["lang"]) => number;
@@ -69,9 +69,25 @@ export const includeProp = (def, prop) => {
69
69
  }
70
70
  if (prop.typeIndex === TEXT) {
71
71
  if (!def.include.langTextFields.has(prop.prop)) {
72
- def.include.langTextFields.set(prop.prop, { def: prop, codes: new Set() });
72
+ def.include.langTextFields.set(prop.prop, {
73
+ def: prop,
74
+ codes: new Set(),
75
+ fallBacks: [],
76
+ });
77
+ }
78
+ const langs = def.include.langTextFields.get(prop.prop);
79
+ if (def.lang.fallback.length > 0) {
80
+ for (const fallback of def.lang.fallback) {
81
+ if (!langs.fallBacks.includes(fallback)) {
82
+ langs.fallBacks.push(fallback);
83
+ }
84
+ }
85
+ }
86
+ const langCode = def.lang.lang ?? 0;
87
+ langs.codes.add(langCode);
88
+ if (langCode === 0 || langs.codes.size > 1) {
89
+ langs.fallBacks = [];
73
90
  }
74
- def.include.langTextFields.get(prop.prop).codes.add(def.lang ?? 0);
75
91
  }
76
92
  else {
77
93
  if (prop.separate) {
@@ -56,21 +56,29 @@ export const includeToBuffer = (db, def) => {
56
56
  }
57
57
  }
58
58
  if (def.include.langTextFields.size) {
59
- for (const [prop, { codes, def: propDef },] of def.include.langTextFields.entries()) {
59
+ for (const [prop, { codes, def: propDef, fallBacks },] of def.include.langTextFields.entries()) {
60
60
  def.include.propsRead[prop] = 0;
61
61
  if (codes.has(0)) {
62
- const b = new Uint8Array(3);
62
+ const b = new Uint8Array(4);
63
63
  b[0] = prop;
64
64
  b[1] = propDef.typeIndex;
65
65
  b[2] = 0;
66
+ b[3] = 0;
66
67
  result.push(b);
67
68
  }
68
69
  else {
69
70
  for (const code of codes) {
70
- const b = new Uint8Array(3);
71
+ const fallBackSize = fallBacks.length;
72
+ const b = new Uint8Array(4 + fallBackSize);
71
73
  b[0] = prop;
72
74
  b[1] = propDef.typeIndex;
73
75
  b[2] = code;
76
+ b[3] = fallBackSize;
77
+ let i = 0;
78
+ for (const fallback of fallBacks) {
79
+ b[i + 4] = fallback;
80
+ i++;
81
+ }
74
82
  result.push(b);
75
83
  }
76
84
  }
@@ -55,7 +55,11 @@ export const walkDefs = (db, def, f) => {
55
55
  return;
56
56
  }
57
57
  if (!def.include.langTextFields.has(t.prop)) {
58
- def.include.langTextFields.set(t.prop, { def: t, codes: new Set() });
58
+ def.include.langTextFields.set(t.prop, {
59
+ def: t,
60
+ codes: new Set(),
61
+ fallBacks: [],
62
+ });
59
63
  }
60
64
  def.include.langTextFields.get(t.prop).codes.add(langCode);
61
65
  return;
@@ -8,7 +8,10 @@ const createEmptySharedDef = (skipValidation) => {
8
8
  skipValidation,
9
9
  filter: { conditions: new Map(), size: 0 },
10
10
  range: { offset: 0, limit: 0 },
11
- lang: langCodesMap.get('none'),
11
+ lang: {
12
+ lang: langCodesMap.get('none'),
13
+ fallback: [],
14
+ },
12
15
  include: {
13
16
  langTextFields: new Map(),
14
17
  stringFields: new Set(),