document-dataply 0.0.9-alpha.12 → 0.0.9-alpha.14

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 (2) hide show
  1. package/dist/cjs/index.js +168 -30
  2. package/package.json +3 -3
package/dist/cjs/index.js CHANGED
@@ -6485,21 +6485,68 @@ var require_cjs = __commonJS({
6485
6485
  }
6486
6486
  return (crc ^ -1) >>> 0;
6487
6487
  }
6488
- function getMinMaxValue(array) {
6489
- let i = 0;
6490
- let min = Infinity;
6491
- let max = -Infinity;
6492
- let len = array.length;
6493
- while (i < len) {
6494
- if (array[i] < min) {
6495
- min = array[i];
6496
- }
6497
- if (array[i] > max) {
6498
- max = array[i];
6488
+ function calcThreshold(sortedGaps, n) {
6489
+ const gLen = sortedGaps.length;
6490
+ if (gLen === 0) return 0;
6491
+ const median = sortedGaps[Math.floor(gLen * 0.5)];
6492
+ const q1 = sortedGaps[Math.floor(gLen * 0.25)];
6493
+ const q3 = sortedGaps[Math.floor(gLen * 0.75)];
6494
+ const iqr = q3 - q1;
6495
+ const logN = Math.max(1, Math.log10(n));
6496
+ if (iqr > 0) {
6497
+ const threshold2 = q3 + iqr * 1.5 * logN;
6498
+ const minJump = Math.max(median * 5, 20);
6499
+ return Math.max(threshold2, minJump);
6500
+ }
6501
+ const baseGap = median > 0 ? median : 1;
6502
+ const p90 = sortedGaps[Math.floor(gLen * 0.9)];
6503
+ if (p90 > baseGap) {
6504
+ const threshold2 = baseGap + (p90 - baseGap) * 0.5 * logN;
6505
+ return Math.max(threshold2, baseGap * 5, 20);
6506
+ }
6507
+ let mean = 0;
6508
+ for (let i = 0; i < gLen; i++) mean += sortedGaps[i];
6509
+ mean /= gLen;
6510
+ let variance = 0;
6511
+ for (let i = 0; i < gLen; i++) {
6512
+ const d = sortedGaps[i] - mean;
6513
+ variance += d * d;
6514
+ }
6515
+ const stddev = Math.sqrt(variance / gLen);
6516
+ if (stddev === 0) {
6517
+ return baseGap * 2;
6518
+ }
6519
+ const threshold = mean + stddev * logN;
6520
+ return Math.max(threshold, baseGap * 5, 20);
6521
+ }
6522
+ function clusterNumbers(numbers, gapMultiplier) {
6523
+ const n = numbers.length;
6524
+ if (n === 0) return [];
6525
+ if (n === 1) return [new Float64Array([numbers[0]])];
6526
+ const sorted = (numbers instanceof Float64Array ? numbers.slice() : Float64Array.from(numbers)).sort();
6527
+ const gaps = new Float64Array(n - 1);
6528
+ for (let i = 0, len = n - 1; i < len; i++) {
6529
+ gaps[i] = sorted[i + 1] - sorted[i];
6530
+ }
6531
+ const sortedGaps = gaps.slice().sort();
6532
+ let threshold;
6533
+ if (gapMultiplier !== void 0) {
6534
+ const q3 = sortedGaps[Math.floor((n - 1) * 0.75)];
6535
+ const iqr = q3 - sortedGaps[Math.floor((n - 1) * 0.25)];
6536
+ threshold = q3 + iqr * gapMultiplier;
6537
+ } else {
6538
+ threshold = calcThreshold(sortedGaps, n);
6539
+ }
6540
+ const clusters = [];
6541
+ let clusterStart = 0;
6542
+ for (let i = 0, len = n - 1; i < len; i++) {
6543
+ if (gaps[i] > threshold) {
6544
+ clusters.push(sorted.subarray(clusterStart, i + 1));
6545
+ clusterStart = i + 1;
6499
6546
  }
6500
- i++;
6501
6547
  }
6502
- return [min, max];
6548
+ clusters.push(sorted.subarray(clusterStart));
6549
+ return clusters;
6503
6550
  }
6504
6551
  var Row = class _Row {
6505
6552
  static CONSTANT = {
@@ -9439,14 +9486,30 @@ var require_cjs = __commonJS({
9439
9486
  for (let i = 0, len = pks.length; i < len; i++) {
9440
9487
  pkIndexMap.set(pks[i], i);
9441
9488
  }
9442
- const [minPk, maxPk] = getMinMaxValue(pks);
9443
9489
  const pkRidPairs = new Array(pks.length).fill(null);
9444
9490
  const btx = await this.getBPTreeTransaction(tx);
9445
- const stream = btx.whereStream({ gte: minPk, lte: maxPk });
9446
- for await (const [rid, pk] of stream) {
9447
- const index = pkIndexMap.get(pk);
9448
- if (index !== void 0) {
9449
- pkRidPairs[index] = { pk, rid, index };
9491
+ const clusters = clusterNumbers(pks);
9492
+ for (let i = 0, len = clusters.length; i < len; i++) {
9493
+ const cluster = clusters[i];
9494
+ const minPk = cluster[0];
9495
+ const maxPk = cluster[cluster.length - 1];
9496
+ if (minPk === maxPk) {
9497
+ const keys = await btx.keys({ equal: minPk });
9498
+ if (keys.size > 0) {
9499
+ const rid = keys.values().next().value;
9500
+ const index = pkIndexMap.get(minPk);
9501
+ if (index !== void 0) {
9502
+ pkRidPairs[index] = { pk: minPk, rid, index };
9503
+ }
9504
+ }
9505
+ continue;
9506
+ }
9507
+ const stream = btx.whereStream({ gte: minPk, lte: maxPk });
9508
+ for await (const [rid, pk] of stream) {
9509
+ const index = pkIndexMap.get(pk);
9510
+ if (index !== void 0) {
9511
+ pkRidPairs[index] = { pk, rid, index };
9512
+ }
9450
9513
  }
9451
9514
  }
9452
9515
  return this.fetchRowsByRids(pkRidPairs, tx);
@@ -10934,7 +10997,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10934
10997
  const doc = await this.getDocument(k, tx2);
10935
10998
  if (!doc) continue;
10936
10999
  const flatDoc = this.flattenDocument(doc);
10937
- for (const indexName of backfillTargets) {
11000
+ for (let i = 0, len = backfillTargets.length; i < len; i++) {
11001
+ const indexName = backfillTargets[i];
10938
11002
  if (!(indexName in indexTxMap)) continue;
10939
11003
  const config = this.registeredIndices.get(indexName);
10940
11004
  if (!config) continue;
@@ -10946,8 +11010,8 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
10946
11010
  const ftsConfig = this.getFtsConfig(config);
10947
11011
  const tokens = ftsConfig ? tokenize(v, ftsConfig) : [v];
10948
11012
  const batchInsertData = [];
10949
- for (let i = 0, len = tokens.length; i < len; i++) {
10950
- const token = tokens[i];
11013
+ for (let i2 = 0, len2 = tokens.length; i2 < len2; i2++) {
11014
+ const token = tokens[i2];
10951
11015
  const keyToInsert = this.getTokenKey(k, token);
10952
11016
  const entry = { k, v: token };
10953
11017
  batchInsertData.push([keyToInsert, entry]);
@@ -11148,13 +11212,18 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11148
11212
  if (config.type === "btree") {
11149
11213
  const primaryField = config.fields[0];
11150
11214
  if (!queryFields.has(primaryField)) continue;
11151
- const condition = query[primaryField];
11152
11215
  const treeTx = await tree.createTransaction();
11216
+ const builtCondition = {};
11153
11217
  let score = 0;
11154
11218
  let isConsecutive = true;
11155
11219
  const coveredFields = [];
11156
11220
  const compositeVerifyFields = [];
11157
- for (const field of config.fields) {
11221
+ const startValues = [];
11222
+ const endValues = [];
11223
+ let startOperator = null;
11224
+ let endOperator = null;
11225
+ for (let i = 0, len = config.fields.length; i < len; i++) {
11226
+ const field = config.fields[i];
11158
11227
  if (!queryFields.has(field)) {
11159
11228
  isConsecutive = false;
11160
11229
  continue;
@@ -11169,11 +11238,53 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11169
11238
  if (cond !== void 0) {
11170
11239
  if (typeof cond !== "object" || cond === null) {
11171
11240
  score += 100;
11241
+ startValues.push(cond);
11242
+ endValues.push(cond);
11243
+ startOperator = "primaryGte";
11244
+ endOperator = "primaryLte";
11172
11245
  } else if ("primaryEqual" in cond || "equal" in cond) {
11246
+ const val = cond.primaryEqual?.v ?? cond.equal?.v ?? cond.primaryEqual ?? cond.equal;
11173
11247
  score += 100;
11174
- } else if ("primaryGte" in cond || "primaryLte" in cond || "primaryGt" in cond || "primaryLt" in cond || "gte" in cond || "lte" in cond || "gt" in cond || "lt" in cond) {
11248
+ startValues.push(val);
11249
+ endValues.push(val);
11250
+ startOperator = "primaryGte";
11251
+ endOperator = "primaryLte";
11252
+ } else if ("primaryGte" in cond || "gte" in cond) {
11253
+ const val = cond.primaryGte?.v ?? cond.gte?.v ?? cond.primaryGte ?? cond.gte;
11254
+ score += 50;
11255
+ isConsecutive = false;
11256
+ startValues.push(val);
11257
+ startOperator = "primaryGte";
11258
+ if (endValues.length > 0) {
11259
+ endOperator = "primaryLte";
11260
+ }
11261
+ } else if ("primaryGt" in cond || "gt" in cond) {
11262
+ const val = cond.primaryGt?.v ?? cond.gt?.v ?? cond.primaryGt ?? cond.gt;
11263
+ score += 50;
11264
+ isConsecutive = false;
11265
+ startValues.push(val);
11266
+ startOperator = "primaryGt";
11267
+ if (endValues.length > 0) {
11268
+ endOperator = "primaryLte";
11269
+ }
11270
+ } else if ("primaryLte" in cond || "lte" in cond) {
11271
+ const val = cond.primaryLte?.v ?? cond.lte?.v ?? cond.primaryLte ?? cond.lte;
11272
+ score += 50;
11273
+ isConsecutive = false;
11274
+ endValues.push(val);
11275
+ endOperator = "primaryLte";
11276
+ if (startValues.length > 0) {
11277
+ startOperator = "primaryGte";
11278
+ }
11279
+ } else if ("primaryLt" in cond || "lt" in cond) {
11280
+ const val = cond.primaryLt?.v ?? cond.lt?.v ?? cond.primaryLt ?? cond.lt;
11175
11281
  score += 50;
11176
11282
  isConsecutive = false;
11283
+ endValues.push(val);
11284
+ endOperator = "primaryLt";
11285
+ if (startValues.length > 0) {
11286
+ startOperator = "primaryGte";
11287
+ }
11177
11288
  } else if ("primaryOr" in cond || "or" in cond) {
11178
11289
  score += 20;
11179
11290
  isConsecutive = false;
@@ -11187,9 +11298,28 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11187
11298
  }
11188
11299
  }
11189
11300
  }
11301
+ if (coveredFields.length === 1 && config.fields.length === 1) {
11302
+ Object.assign(builtCondition, query[primaryField]);
11303
+ } else {
11304
+ if (startOperator && startValues.length > 0) {
11305
+ builtCondition[startOperator] = { v: startValues.length === 1 ? startValues[0] : startValues };
11306
+ }
11307
+ if (endOperator && endValues.length > 0) {
11308
+ if (startOperator && startValues.length === endValues.length && startValues.every((val, i) => val === endValues[i])) {
11309
+ delete builtCondition[startOperator];
11310
+ builtCondition["primaryEqual"] = { v: startValues.length === 1 ? startValues[0] : startValues };
11311
+ } else {
11312
+ builtCondition[endOperator] = { v: endValues.length === 1 ? endValues[0] : endValues };
11313
+ }
11314
+ }
11315
+ if (Object.keys(builtCondition).length === 0) {
11316
+ Object.assign(builtCondition, query[primaryField] || {});
11317
+ }
11318
+ }
11190
11319
  let isIndexOrderSupported = false;
11191
11320
  if (orderByField) {
11192
- for (const field of config.fields) {
11321
+ for (let i = 0, len = config.fields.length; i < len; i++) {
11322
+ const field = config.fields[i];
11193
11323
  if (field === orderByField) {
11194
11324
  isIndexOrderSupported = true;
11195
11325
  break;
@@ -11208,7 +11338,7 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11208
11338
  }
11209
11339
  candidates.push({
11210
11340
  tree: treeTx,
11211
- condition,
11341
+ condition: builtCondition,
11212
11342
  field: primaryField,
11213
11343
  indexName,
11214
11344
  isFtsMatch: false,
@@ -11246,11 +11376,19 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
11246
11376
  rollback();
11247
11377
  return null;
11248
11378
  }
11249
- candidates.sort((a, b) => b.score - a.score);
11379
+ candidates.sort((a, b) => {
11380
+ if (b.score !== a.score) return b.score - a.score;
11381
+ const aConfig = this.registeredIndices.get(a.indexName);
11382
+ const bConfig = this.registeredIndices.get(b.indexName);
11383
+ const aFieldCount = aConfig ? Array.isArray(aConfig.fields) ? aConfig.fields.length : 1 : 0;
11384
+ const bFieldCount = bConfig ? Array.isArray(bConfig.fields) ? bConfig.fields.length : 1 : 0;
11385
+ return aFieldCount - bFieldCount;
11386
+ });
11250
11387
  const driver = candidates[0];
11251
- const others = candidates.slice(1);
11388
+ const others = candidates.slice(1).filter((c) => c.field !== driver.field);
11252
11389
  const compositeVerifyConditions = [];
11253
- for (const field of driver.compositeVerifyFields) {
11390
+ for (let i = 0, len = driver.compositeVerifyFields.length; i < len; i++) {
11391
+ const field = driver.compositeVerifyFields[i];
11254
11392
  if (query[field]) {
11255
11393
  compositeVerifyConditions.push({ field, condition: query[field] });
11256
11394
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-dataply",
3
- "version": "0.0.9-alpha.12",
3
+ "version": "0.0.9-alpha.14",
4
4
  "description": "Simple and powerful JSON document database supporting complex queries and flexible indexing policies.",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",
@@ -42,7 +42,7 @@
42
42
  "dataply"
43
43
  ],
44
44
  "dependencies": {
45
- "dataply": "^0.0.24-alpha.11"
45
+ "dataply": "^0.0.24-alpha.12"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/jest": "^30.0.0",
@@ -51,4 +51,4 @@
51
51
  "ts-jest": "^29.4.6",
52
52
  "typescript": "^5.9.3"
53
53
  }
54
- }
54
+ }