@tanstack/db 0.5.32 → 0.6.0
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/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +24 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +12 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/effect.cjs +602 -0
- package/dist/cjs/query/effect.cjs.map +1 -0
- package/dist/cjs/query/effect.d.cts +94 -0
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +219 -0
- package/dist/cjs/query/live/utils.cjs.map +1 -0
- package/dist/cjs/query/live/utils.d.cts +110 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +12 -3
- package/dist/esm/index.js +27 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/effect.d.ts +94 -0
- package/dist/esm/query/effect.js +602 -0
- package/dist/esm/query/effect.js.map +1 -0
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +492 -65
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
- package/dist/esm/query/live/collection-subscriber.js +31 -98
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +110 -0
- package/dist/esm/query/live/utils.js +219 -0
- package/dist/esm/query/live/utils.js.map +1 -0
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +2 -2
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +57 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/effect.ts +1119 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +778 -104
- package/src/query/live/collection-subscriber.ts +40 -156
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +417 -0
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { DEFAULT_COMPARE_OPTIONS } from "../utils.js";
|
|
2
|
-
import {
|
|
2
|
+
import { hasVirtualPropPath } from "../virtual-props.js";
|
|
3
|
+
import { isDevModeEnabled, checkCollectionSizeForIndex } from "./index-registry.js";
|
|
3
4
|
function shouldAutoIndex(collection) {
|
|
4
|
-
|
|
5
|
-
return false;
|
|
6
|
-
}
|
|
7
|
-
return true;
|
|
5
|
+
return collection.config.autoIndex === `eager`;
|
|
8
6
|
}
|
|
9
7
|
function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, compareFn) {
|
|
8
|
+
if (hasVirtualPropPath(fieldPath)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
10
11
|
if (!shouldAutoIndex(collection)) {
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
@@ -20,6 +21,13 @@ function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, c
|
|
|
20
21
|
if (existingIndex) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
24
|
+
if (isDevModeEnabled()) {
|
|
25
|
+
checkCollectionSizeForIndex(
|
|
26
|
+
collection.id || `unknown`,
|
|
27
|
+
collection.size,
|
|
28
|
+
fieldPath
|
|
29
|
+
);
|
|
30
|
+
}
|
|
23
31
|
try {
|
|
24
32
|
collection.createIndex(
|
|
25
33
|
(row) => {
|
|
@@ -31,7 +39,6 @@ function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, c
|
|
|
31
39
|
},
|
|
32
40
|
{
|
|
33
41
|
name: `auto:${fieldPath.join(`.`)}`,
|
|
34
|
-
indexType: BTreeIndex,
|
|
35
42
|
options: compareFn ? { compareFn, compareOptions: compareOpts } : {}
|
|
36
43
|
}
|
|
37
44
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-index.js","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { DEFAULT_COMPARE_OPTIONS } from '../utils'\nimport {
|
|
1
|
+
{"version":3,"file":"auto-index.js","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { DEFAULT_COMPARE_OPTIONS } from '../utils'\nimport { hasVirtualPropPath } from '../virtual-props'\nimport { checkCollectionSizeForIndex, isDevModeEnabled } from './index-registry'\nimport type { CompareOptions } from '../query/builder/types'\nimport type { BasicExpression } from '../query/ir'\nimport type { CollectionImpl } from '../collection/index.js'\n\nexport interface AutoIndexConfig {\n autoIndex?: `off` | `eager`\n}\n\nfunction shouldAutoIndex(collection: CollectionImpl<any, any, any, any, any>) {\n // Only proceed if auto-indexing is enabled\n // Note: autoIndex: 'eager' without defaultIndexType is caught at construction time\n // in CollectionImpl, so we don't need to check for it here.\n return collection.config.autoIndex === `eager`\n}\n\nexport function ensureIndexForField<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n fieldName: string,\n fieldPath: Array<string>,\n collection: CollectionImpl<T, TKey, any, any, any>,\n compareOptions?: CompareOptions,\n compareFn?: (a: any, b: any) => number,\n) {\n if (hasVirtualPropPath(fieldPath)) {\n return\n }\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n const compareOpts = compareOptions ?? {\n ...DEFAULT_COMPARE_OPTIONS,\n ...collection.compareOptions,\n }\n\n // Check if we already have an index for this field\n const existingIndex = Array.from(collection.indexes.values()).find(\n (index) =>\n index.matchesField(fieldPath) && index.matchesCompareOptions(compareOpts),\n )\n\n if (existingIndex) {\n return // Index already exists\n }\n\n // Dev mode: check if collection size warrants an index suggestion\n if (isDevModeEnabled()) {\n checkCollectionSizeForIndex(\n collection.id || `unknown`,\n collection.size,\n fieldPath,\n )\n }\n\n // Create a new index for this field using the collection's createIndex method\n // The collection will use its defaultIndexType\n try {\n collection.createIndex(\n (row) => {\n // Navigate through the field path\n let current: any = row\n for (const part of fieldPath) {\n current = current[part]\n }\n return current\n },\n {\n name: `auto:${fieldPath.join(`.`)}`,\n options: compareFn ? { compareFn, compareOptions: compareOpts } : {},\n },\n )\n } catch (error) {\n console.warn(\n `${collection.id ? `[${collection.id}] ` : ``}Failed to create auto-index for field path \"${fieldPath.join(`.`)}\":`,\n error,\n )\n }\n}\n\n/**\n * Analyzes a where expression and creates indexes for all simple operations on single fields\n */\nexport function ensureIndexForExpression<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n expression: BasicExpression,\n collection: CollectionImpl<T, TKey, any, any, any>,\n): void {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n // Extract all indexable expressions and create indexes for them\n const indexableExpressions = extractIndexableExpressions(expression)\n\n for (const { fieldName, fieldPath } of indexableExpressions) {\n ensureIndexForField(fieldName, fieldPath, collection)\n }\n}\n\n/**\n * Extracts all indexable expressions from a where expression\n */\nfunction extractIndexableExpressions(\n expression: BasicExpression,\n): Array<{ fieldName: string; fieldPath: Array<string> }> {\n const results: Array<{ fieldName: string; fieldPath: Array<string> }> = []\n\n function extractFromExpression(expr: BasicExpression): void {\n if (expr.type !== `func`) {\n return\n }\n\n const func = expr as any\n\n // Handle 'and' expressions by recursively processing all arguments\n if (func.name === `and`) {\n for (const arg of func.args) {\n extractFromExpression(arg)\n }\n return\n }\n\n // Check if this is a supported operation\n const supportedOperations = [`eq`, `gt`, `gte`, `lt`, `lte`, `in`]\n if (!supportedOperations.includes(func.name)) {\n return\n }\n\n // Check if the first argument is a property reference\n if (func.args.length < 1 || func.args[0].type !== `ref`) {\n return\n }\n\n const fieldRef = func.args[0]\n const fieldPath = fieldRef.path\n\n // Skip if the path is empty\n if (fieldPath.length === 0) {\n return\n }\n\n // For nested paths, use the full path joined with underscores as the field name\n // For simple paths, use the first (and only) element\n const fieldName = fieldPath.join(`_`)\n results.push({ fieldName, fieldPath })\n }\n\n extractFromExpression(expression)\n return results\n}\n"],"names":[],"mappings":";;;AAWA,SAAS,gBAAgB,YAAqD;AAI5E,SAAO,WAAW,OAAO,cAAc;AACzC;AAEO,SAAS,oBAId,WACA,WACA,YACA,gBACA,WACA;AACA,MAAI,mBAAmB,SAAS,GAAG;AACjC;AAAA,EACF;AACA,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAEA,QAAM,cAAc,kBAAkB;AAAA,IACpC,GAAG;AAAA,IACH,GAAG,WAAW;AAAA,EAAA;AAIhB,QAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,OAAA,CAAQ,EAAE;AAAA,IAC5D,CAAC,UACC,MAAM,aAAa,SAAS,KAAK,MAAM,sBAAsB,WAAW;AAAA,EAAA;AAG5E,MAAI,eAAe;AACjB;AAAA,EACF;AAGA,MAAI,oBAAoB;AACtB;AAAA,MACE,WAAW,MAAM;AAAA,MACjB,WAAW;AAAA,MACX;AAAA,IAAA;AAAA,EAEJ;AAIA,MAAI;AACF,eAAW;AAAA,MACT,CAAC,QAAQ;AAEP,YAAI,UAAe;AACnB,mBAAW,QAAQ,WAAW;AAC5B,oBAAU,QAAQ,IAAI;AAAA,QACxB;AACA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,MAAM,QAAQ,UAAU,KAAK,GAAG,CAAC;AAAA,QACjC,SAAS,YAAY,EAAE,WAAW,gBAAgB,YAAA,IAAgB,CAAA;AAAA,MAAC;AAAA,IACrE;AAAA,EAEJ,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,GAAG,WAAW,KAAK,IAAI,WAAW,EAAE,OAAO,EAAE,+CAA+C,UAAU,KAAK,GAAG,CAAC;AAAA,MAC/G;AAAA,IAAA;AAAA,EAEJ;AACF;AAKO,SAAS,yBAId,YACA,YACM;AACN,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAGA,QAAM,uBAAuB,4BAA4B,UAAU;AAEnE,aAAW,EAAE,WAAW,UAAA,KAAe,sBAAsB;AAC3D,wBAAoB,WAAW,WAAW,UAAU;AAAA,EACtD;AACF;AAKA,SAAS,4BACP,YACwD;AACxD,QAAM,UAAkE,CAAA;AAExE,WAAS,sBAAsB,MAA6B;AAC1D,QAAI,KAAK,SAAS,QAAQ;AACxB;AAAA,IACF;AAEA,UAAM,OAAO;AAGb,QAAI,KAAK,SAAS,OAAO;AACvB,iBAAW,OAAO,KAAK,MAAM;AAC3B,8BAAsB,GAAG;AAAA,MAC3B;AACA;AAAA,IACF;AAGA,UAAM,sBAAsB,CAAC,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AACjE,QAAI,CAAC,oBAAoB,SAAS,KAAK,IAAI,GAAG;AAC5C;AAAA,IACF;AAGA,QAAI,KAAK,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,EAAE,SAAS,OAAO;AACvD;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,KAAK,CAAC;AAC5B,UAAM,YAAY,SAAS;AAG3B,QAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,IACF;AAIA,UAAM,YAAY,UAAU,KAAK,GAAG;AACpC,YAAQ,KAAK,EAAE,WAAW,UAAA,CAAW;AAAA,EACvC;AAEA,wBAAsB,UAAU;AAChC,SAAO;AACT;"}
|
|
@@ -19,7 +19,7 @@ export interface IndexStats {
|
|
|
19
19
|
readonly averageLookupTime: number;
|
|
20
20
|
readonly lastUpdated: Date;
|
|
21
21
|
}
|
|
22
|
-
export interface IndexInterface<TKey extends string | number
|
|
22
|
+
export interface IndexInterface<TKey extends string | number = string | number> {
|
|
23
23
|
add: (key: TKey, item: any) => void;
|
|
24
24
|
remove: (key: TKey, item: any) => void;
|
|
25
25
|
update: (key: TKey, oldItem: any, newItem: any) => void;
|
|
@@ -48,7 +48,7 @@ export interface IndexInterface<TKey extends string | number | undefined = strin
|
|
|
48
48
|
/**
|
|
49
49
|
* Base abstract class that all index types extend
|
|
50
50
|
*/
|
|
51
|
-
export declare abstract class BaseIndex<TKey extends string | number
|
|
51
|
+
export declare abstract class BaseIndex<TKey extends string | number = string | number> implements IndexInterface<TKey> {
|
|
52
52
|
readonly id: number;
|
|
53
53
|
readonly name?: string;
|
|
54
54
|
readonly expression: BasicExpression;
|
|
@@ -98,7 +98,3 @@ export declare abstract class BaseIndex<TKey extends string | number | undefined
|
|
|
98
98
|
* Type for index constructor
|
|
99
99
|
*/
|
|
100
100
|
export type IndexConstructor<TKey extends string | number = string | number> = new (id: number, expression: BasicExpression, name?: string, options?: any) => BaseIndex<TKey>;
|
|
101
|
-
/**
|
|
102
|
-
* Index resolver can be either a class constructor or async loader
|
|
103
|
-
*/
|
|
104
|
-
export type IndexResolver<TKey extends string | number = string | number> = IndexConstructor<TKey> | (() => Promise<IndexConstructor<TKey>>);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { compileSingleRowExpression } from "../query/compiler/evaluators.js";
|
|
2
|
-
import { comparisonFunctions } from "../query/builder/functions.js";
|
|
3
2
|
import { DEFAULT_COMPARE_OPTIONS, deepEquals } from "../utils.js";
|
|
4
|
-
const IndexOperation = comparisonFunctions;
|
|
5
3
|
class BaseIndex {
|
|
6
4
|
constructor(id, expression, name, options) {
|
|
7
5
|
this.lookupCount = 0;
|
|
@@ -66,7 +64,6 @@ class BaseIndex {
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
export {
|
|
69
|
-
BaseIndex
|
|
70
|
-
IndexOperation
|
|
67
|
+
BaseIndex
|
|
71
68
|
};
|
|
72
69
|
//# sourceMappingURL=base-index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from '../query/compiler/evaluators.js'\nimport { comparisonFunctions } from '../query/builder/functions.js'\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from '../utils.js'\nimport type { RangeQueryOptions } from './btree-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression, OrderByDirection } from '../query/ir.js'\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number
|
|
1
|
+
{"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from '../query/compiler/evaluators.js'\nimport { comparisonFunctions } from '../query/builder/functions.js'\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from '../utils.js'\nimport type { RangeQueryOptions } from './btree-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression, OrderByDirection } from '../query/ir.js'\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number = string | number,\n> {\n add: (key: TKey, item: any) => void\n remove: (key: TKey, item: any) => void\n update: (key: TKey, oldItem: any, newItem: any) => void\n\n build: (entries: Iterable<[TKey, any]>) => void\n clear: () => void\n\n lookup: (operation: IndexOperation, value: any) => Set<TKey>\n\n equalityLookup: (value: any) => Set<TKey>\n inArrayLookup: (values: Array<any>) => Set<TKey>\n\n rangeQuery: (options: RangeQueryOptions) => Set<TKey>\n rangeQueryReversed: (options: RangeQueryOptions) => Set<TKey>\n\n take: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeFromStart: (n: number, filterFn?: (key: TKey) => boolean) => Array<TKey>\n takeReversed: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeReversedFromEnd: (\n n: number,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n\n get keyCount(): number\n get orderedEntriesArray(): Array<[any, Set<TKey>]>\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n\n get indexedKeysSet(): Set<TKey>\n get valueMapData(): Map<any, Set<TKey>>\n\n supports: (operation: IndexOperation) => boolean\n\n matchesField: (fieldPath: Array<string>) => boolean\n matchesCompareOptions: (compareOptions: CompareOptions) => boolean\n matchesDirection: (direction: OrderByDirection) => boolean\n\n getStats: () => IndexStats\n}\n\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<\n TKey extends string | number = string | number,\n> implements IndexInterface<TKey> {\n public readonly id: number\n public readonly name?: string\n public readonly expression: BasicExpression\n public abstract readonly supportedOperations: Set<IndexOperation>\n\n protected lookupCount = 0\n protected totalLookupTime = 0\n protected lastUpdated = new Date()\n protected compareOptions: CompareOptions\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) {\n this.id = id\n this.expression = expression\n this.compareOptions = DEFAULT_COMPARE_OPTIONS\n this.name = name\n this.initialize(options)\n }\n\n // Abstract methods that each index type must implement\n abstract add(key: TKey, item: any): void\n abstract remove(key: TKey, item: any): void\n abstract update(key: TKey, oldItem: any, newItem: any): void\n abstract build(entries: Iterable<[TKey, any]>): void\n abstract clear(): void\n abstract lookup(operation: IndexOperation, value: any): Set<TKey>\n abstract take(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeFromStart(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversed(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversedFromEnd(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract get keyCount(): number\n abstract equalityLookup(value: any): Set<TKey>\n abstract inArrayLookup(values: Array<any>): Set<TKey>\n abstract rangeQuery(options: RangeQueryOptions): Set<TKey>\n abstract rangeQueryReversed(options: RangeQueryOptions): Set<TKey>\n abstract get orderedEntriesArray(): Array<[any, Set<TKey>]>\n abstract get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n abstract get indexedKeysSet(): Set<TKey>\n abstract get valueMapData(): Map<any, Set<TKey>>\n\n // Common methods\n supports(operation: IndexOperation): boolean {\n return this.supportedOperations.has(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return (\n this.expression.type === `ref` &&\n this.expression.path.length === fieldPath.length &&\n this.expression.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Checks if the compare options match the index's compare options.\n * The direction is ignored because the index can be reversed if the direction is different.\n */\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n const thisCompareOptionsWithoutDirection = {\n ...this.compareOptions,\n direction: undefined,\n }\n const compareOptionsWithoutDirection = {\n ...compareOptions,\n direction: undefined,\n }\n\n return deepEquals(\n thisCompareOptionsWithoutDirection,\n compareOptionsWithoutDirection,\n )\n }\n\n /**\n * Checks if the index matches the provided direction.\n */\n matchesDirection(direction: OrderByDirection): boolean {\n return this.compareOptions.direction === direction\n }\n\n getStats(): IndexStats {\n return {\n entryCount: this.keyCount,\n lookupCount: this.lookupCount,\n averageLookupTime:\n this.lookupCount > 0 ? this.totalLookupTime / this.lookupCount : 0,\n lastUpdated: this.lastUpdated,\n }\n }\n\n protected abstract initialize(options?: any): void\n\n protected evaluateIndexExpression(item: any): any {\n const evaluator = compileSingleRowExpression(this.expression)\n return evaluator(item as Record<string, unknown>)\n }\n\n protected trackLookup(startTime: number): void {\n const duration = performance.now() - startTime\n this.lookupCount++\n this.totalLookupTime += duration\n }\n\n protected updateTimestamp(): void {\n this.lastUpdated = new Date()\n }\n}\n\n/**\n * Type for index constructor\n */\nexport type IndexConstructor<TKey extends string | number = string | number> =\n new (\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) => BaseIndex<TKey>\n"],"names":[],"mappings":";;AAgFO,MAAe,UAEY;AAAA,EAWhC,YACE,IACA,YACA,MACA,SACA;AAVF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAS1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAsCA,SAAS,WAAoC;AAC3C,WAAO,KAAK,oBAAoB,IAAI,SAAS;AAAA,EAC/C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WACE,KAAK,WAAW,SAAS,SACzB,KAAK,WAAW,KAAK,WAAW,UAAU,UAC1C,KAAK,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAEjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAyC;AAC7D,UAAM,qCAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,WAAW;AAAA,IAAA;AAEb,UAAM,iCAAiC;AAAA,MACrC,GAAG;AAAA,MACH,WAAW;AAAA,IAAA;AAGb,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA,EAEA,WAAuB;AACrB,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBACE,KAAK,cAAc,IAAI,KAAK,kBAAkB,KAAK,cAAc;AAAA,MACnE,aAAa,KAAK;AAAA,IAAA;AAAA,EAEtB;AAAA,EAIU,wBAAwB,MAAgB;AAChD,UAAM,YAAY,2BAA2B,KAAK,UAAU;AAC5D,WAAO,UAAU,IAA+B;AAAA,EAClD;AAAA,EAEU,YAAY,WAAyB;AAC7C,UAAM,WAAW,YAAY,IAAA,IAAQ;AACrC,SAAK;AACL,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEU,kBAAwB;AAChC,SAAK,kCAAkB,KAAA;AAAA,EACzB;AACF;"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { BaseIndex, IndexOperation } from './base-index.js';
|
|
2
|
+
import { CompareOptions } from '../query/builder/types.js';
|
|
3
|
+
import { BasicExpression } from '../query/ir.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for range queries
|
|
6
|
+
*/
|
|
7
|
+
export interface RangeQueryOptions {
|
|
8
|
+
from?: any;
|
|
9
|
+
to?: any;
|
|
10
|
+
fromInclusive?: boolean;
|
|
11
|
+
toInclusive?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Options for Basic index
|
|
15
|
+
*/
|
|
16
|
+
export interface BasicIndexOptions {
|
|
17
|
+
compareFn?: (a: any, b: any) => number;
|
|
18
|
+
compareOptions?: CompareOptions;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Basic index using Map + sorted Array.
|
|
22
|
+
*
|
|
23
|
+
* - Map for O(1) equality lookups
|
|
24
|
+
* - Sorted Array for O(log n) range queries via binary search
|
|
25
|
+
* - O(n) updates to maintain sort order
|
|
26
|
+
*
|
|
27
|
+
* Simpler and smaller than BTreeIndex, good for read-heavy workloads.
|
|
28
|
+
* Use BTreeIndex for write-heavy workloads with large collections.
|
|
29
|
+
*/
|
|
30
|
+
export declare class BasicIndex<TKey extends string | number = string | number> extends BaseIndex<TKey> {
|
|
31
|
+
readonly supportedOperations: Set<"eq" | "gt" | "gte" | "lt" | "lte" | "in" | "like" | "ilike">;
|
|
32
|
+
private valueMap;
|
|
33
|
+
private sortedValues;
|
|
34
|
+
private indexedKeys;
|
|
35
|
+
private compareFn;
|
|
36
|
+
constructor(id: number, expression: BasicExpression, name?: string, options?: any);
|
|
37
|
+
protected initialize(_options?: BasicIndexOptions): void;
|
|
38
|
+
/**
|
|
39
|
+
* Adds a value to the index
|
|
40
|
+
*/
|
|
41
|
+
add(key: TKey, item: any): void;
|
|
42
|
+
/**
|
|
43
|
+
* Removes a value from the index
|
|
44
|
+
*/
|
|
45
|
+
remove(key: TKey, item: any): void;
|
|
46
|
+
/**
|
|
47
|
+
* Updates a value in the index
|
|
48
|
+
*/
|
|
49
|
+
update(key: TKey, oldItem: any, newItem: any): void;
|
|
50
|
+
/**
|
|
51
|
+
* Builds the index from a collection of entries
|
|
52
|
+
*/
|
|
53
|
+
build(entries: Iterable<[TKey, any]>): void;
|
|
54
|
+
/**
|
|
55
|
+
* Clears all data from the index
|
|
56
|
+
*/
|
|
57
|
+
clear(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Performs a lookup operation
|
|
60
|
+
*/
|
|
61
|
+
lookup(operation: IndexOperation, value: any): Set<TKey>;
|
|
62
|
+
/**
|
|
63
|
+
* Gets the number of indexed keys
|
|
64
|
+
*/
|
|
65
|
+
get keyCount(): number;
|
|
66
|
+
/**
|
|
67
|
+
* Performs an equality lookup - O(1)
|
|
68
|
+
*/
|
|
69
|
+
equalityLookup(value: any): Set<TKey>;
|
|
70
|
+
/**
|
|
71
|
+
* Performs a range query using binary search - O(log n + m)
|
|
72
|
+
*/
|
|
73
|
+
rangeQuery(options?: RangeQueryOptions): Set<TKey>;
|
|
74
|
+
/**
|
|
75
|
+
* Performs a reversed range query
|
|
76
|
+
*/
|
|
77
|
+
rangeQueryReversed(options?: RangeQueryOptions): Set<TKey>;
|
|
78
|
+
/**
|
|
79
|
+
* Returns the next n items in sorted order
|
|
80
|
+
*/
|
|
81
|
+
take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
82
|
+
/**
|
|
83
|
+
* Returns the next n items in reverse sorted order
|
|
84
|
+
*/
|
|
85
|
+
takeReversed(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
86
|
+
/**
|
|
87
|
+
* Returns the first n items in sorted order (from the start)
|
|
88
|
+
*/
|
|
89
|
+
takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
90
|
+
/**
|
|
91
|
+
* Returns the first n items in reverse sorted order (from the end)
|
|
92
|
+
*/
|
|
93
|
+
takeReversedFromEnd(n: number, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
94
|
+
/**
|
|
95
|
+
* Performs an IN array lookup - O(k) where k is values.length
|
|
96
|
+
*/
|
|
97
|
+
inArrayLookup(values: Array<any>): Set<TKey>;
|
|
98
|
+
get indexedKeysSet(): Set<TKey>;
|
|
99
|
+
get orderedEntriesArray(): Array<[any, Set<TKey>]>;
|
|
100
|
+
get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>;
|
|
101
|
+
get valueMapData(): Map<any, Set<TKey>>;
|
|
102
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { defaultComparator, normalizeValue } from "../utils/comparison.js";
|
|
2
|
+
import { findInsertPositionInArray, deleteInSortedArray } from "../utils/array-utils.js";
|
|
3
|
+
import { BaseIndex } from "./base-index.js";
|
|
4
|
+
class BasicIndex extends BaseIndex {
|
|
5
|
+
constructor(id, expression, name, options) {
|
|
6
|
+
super(id, expression, name, options);
|
|
7
|
+
this.supportedOperations = /* @__PURE__ */ new Set([
|
|
8
|
+
`eq`,
|
|
9
|
+
`gt`,
|
|
10
|
+
`gte`,
|
|
11
|
+
`lt`,
|
|
12
|
+
`lte`,
|
|
13
|
+
`in`
|
|
14
|
+
]);
|
|
15
|
+
this.valueMap = /* @__PURE__ */ new Map();
|
|
16
|
+
this.sortedValues = [];
|
|
17
|
+
this.indexedKeys = /* @__PURE__ */ new Set();
|
|
18
|
+
this.compareFn = defaultComparator;
|
|
19
|
+
this.compareFn = options?.compareFn ?? defaultComparator;
|
|
20
|
+
if (options?.compareOptions) {
|
|
21
|
+
this.compareOptions = options.compareOptions;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
initialize(_options) {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Adds a value to the index
|
|
28
|
+
*/
|
|
29
|
+
add(key, item) {
|
|
30
|
+
let indexedValue;
|
|
31
|
+
try {
|
|
32
|
+
indexedValue = this.evaluateIndexExpression(item);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Failed to evaluate index expression for key ${key}: ${error}`,
|
|
36
|
+
{ cause: error }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const normalizedValue = normalizeValue(indexedValue);
|
|
40
|
+
if (this.valueMap.has(normalizedValue)) {
|
|
41
|
+
this.valueMap.get(normalizedValue).add(key);
|
|
42
|
+
} else {
|
|
43
|
+
this.valueMap.set(normalizedValue, /* @__PURE__ */ new Set([key]));
|
|
44
|
+
const insertIdx = findInsertPositionInArray(
|
|
45
|
+
this.sortedValues,
|
|
46
|
+
normalizedValue,
|
|
47
|
+
this.compareFn
|
|
48
|
+
);
|
|
49
|
+
this.sortedValues.splice(insertIdx, 0, normalizedValue);
|
|
50
|
+
}
|
|
51
|
+
this.indexedKeys.add(key);
|
|
52
|
+
this.updateTimestamp();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Removes a value from the index
|
|
56
|
+
*/
|
|
57
|
+
remove(key, item) {
|
|
58
|
+
let indexedValue;
|
|
59
|
+
try {
|
|
60
|
+
indexedValue = this.evaluateIndexExpression(item);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.warn(
|
|
63
|
+
`Failed to evaluate index expression for key ${key} during removal:`,
|
|
64
|
+
error
|
|
65
|
+
);
|
|
66
|
+
this.indexedKeys.delete(key);
|
|
67
|
+
this.updateTimestamp();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const normalizedValue = normalizeValue(indexedValue);
|
|
71
|
+
if (this.valueMap.has(normalizedValue)) {
|
|
72
|
+
const keySet = this.valueMap.get(normalizedValue);
|
|
73
|
+
keySet.delete(key);
|
|
74
|
+
if (keySet.size === 0) {
|
|
75
|
+
this.valueMap.delete(normalizedValue);
|
|
76
|
+
deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.indexedKeys.delete(key);
|
|
80
|
+
this.updateTimestamp();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Updates a value in the index
|
|
84
|
+
*/
|
|
85
|
+
update(key, oldItem, newItem) {
|
|
86
|
+
this.remove(key, oldItem);
|
|
87
|
+
this.add(key, newItem);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds the index from a collection of entries
|
|
91
|
+
*/
|
|
92
|
+
build(entries) {
|
|
93
|
+
this.clear();
|
|
94
|
+
const entriesArray = [];
|
|
95
|
+
for (const [key, item] of entries) {
|
|
96
|
+
let indexedValue;
|
|
97
|
+
try {
|
|
98
|
+
indexedValue = this.evaluateIndexExpression(item);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Failed to evaluate index expression for key ${key}: ${error}`,
|
|
102
|
+
{ cause: error }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
entriesArray.push({ key, value: normalizeValue(indexedValue) });
|
|
106
|
+
this.indexedKeys.add(key);
|
|
107
|
+
}
|
|
108
|
+
for (const { key, value } of entriesArray) {
|
|
109
|
+
if (this.valueMap.has(value)) {
|
|
110
|
+
this.valueMap.get(value).add(key);
|
|
111
|
+
} else {
|
|
112
|
+
this.valueMap.set(value, /* @__PURE__ */ new Set([key]));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn);
|
|
116
|
+
this.updateTimestamp();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Clears all data from the index
|
|
120
|
+
*/
|
|
121
|
+
clear() {
|
|
122
|
+
this.valueMap.clear();
|
|
123
|
+
this.sortedValues = [];
|
|
124
|
+
this.indexedKeys.clear();
|
|
125
|
+
this.updateTimestamp();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Performs a lookup operation
|
|
129
|
+
*/
|
|
130
|
+
lookup(operation, value) {
|
|
131
|
+
const startTime = performance.now();
|
|
132
|
+
let result;
|
|
133
|
+
switch (operation) {
|
|
134
|
+
case `eq`:
|
|
135
|
+
result = this.equalityLookup(value);
|
|
136
|
+
break;
|
|
137
|
+
case `gt`:
|
|
138
|
+
result = this.rangeQuery({ from: value, fromInclusive: false });
|
|
139
|
+
break;
|
|
140
|
+
case `gte`:
|
|
141
|
+
result = this.rangeQuery({ from: value, fromInclusive: true });
|
|
142
|
+
break;
|
|
143
|
+
case `lt`:
|
|
144
|
+
result = this.rangeQuery({ to: value, toInclusive: false });
|
|
145
|
+
break;
|
|
146
|
+
case `lte`:
|
|
147
|
+
result = this.rangeQuery({ to: value, toInclusive: true });
|
|
148
|
+
break;
|
|
149
|
+
case `in`:
|
|
150
|
+
result = this.inArrayLookup(value);
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
throw new Error(`Operation ${operation} not supported by BasicIndex`);
|
|
154
|
+
}
|
|
155
|
+
this.trackLookup(startTime);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Gets the number of indexed keys
|
|
160
|
+
*/
|
|
161
|
+
get keyCount() {
|
|
162
|
+
return this.indexedKeys.size;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Performs an equality lookup - O(1)
|
|
166
|
+
*/
|
|
167
|
+
equalityLookup(value) {
|
|
168
|
+
const normalizedValue = normalizeValue(value);
|
|
169
|
+
return this.valueMap.get(normalizedValue) ?? /* @__PURE__ */ new Set();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Performs a range query using binary search - O(log n + m)
|
|
173
|
+
*/
|
|
174
|
+
rangeQuery(options = {}) {
|
|
175
|
+
const { from, to, fromInclusive = true, toInclusive = true } = options;
|
|
176
|
+
const result = /* @__PURE__ */ new Set();
|
|
177
|
+
if (this.sortedValues.length === 0) {
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
const normalizedFrom = normalizeValue(from);
|
|
181
|
+
const normalizedTo = normalizeValue(to);
|
|
182
|
+
let startIdx = 0;
|
|
183
|
+
if (normalizedFrom !== void 0) {
|
|
184
|
+
startIdx = findInsertPositionInArray(
|
|
185
|
+
this.sortedValues,
|
|
186
|
+
normalizedFrom,
|
|
187
|
+
this.compareFn
|
|
188
|
+
);
|
|
189
|
+
if (!fromInclusive && startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0) {
|
|
190
|
+
startIdx++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
let endIdx = this.sortedValues.length;
|
|
194
|
+
if (normalizedTo !== void 0) {
|
|
195
|
+
endIdx = findInsertPositionInArray(
|
|
196
|
+
this.sortedValues,
|
|
197
|
+
normalizedTo,
|
|
198
|
+
this.compareFn
|
|
199
|
+
);
|
|
200
|
+
if (toInclusive && endIdx < this.sortedValues.length && this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0) {
|
|
201
|
+
endIdx++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
205
|
+
const keys = this.valueMap.get(this.sortedValues[i]);
|
|
206
|
+
if (keys) {
|
|
207
|
+
keys.forEach((key) => result.add(key));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Performs a reversed range query
|
|
214
|
+
*/
|
|
215
|
+
rangeQueryReversed(options = {}) {
|
|
216
|
+
const { from, to, fromInclusive = true, toInclusive = true } = options;
|
|
217
|
+
const swappedFrom = to ?? (this.sortedValues.length > 0 ? this.sortedValues[this.sortedValues.length - 1] : void 0);
|
|
218
|
+
const swappedTo = from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : void 0);
|
|
219
|
+
return this.rangeQuery({
|
|
220
|
+
from: swappedFrom,
|
|
221
|
+
to: swappedTo,
|
|
222
|
+
fromInclusive: toInclusive,
|
|
223
|
+
toInclusive: fromInclusive
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Returns the next n items in sorted order
|
|
228
|
+
*/
|
|
229
|
+
take(n, from, filterFn) {
|
|
230
|
+
const result = [];
|
|
231
|
+
let startIdx = 0;
|
|
232
|
+
if (from !== void 0) {
|
|
233
|
+
const normalizedFrom = normalizeValue(from);
|
|
234
|
+
startIdx = findInsertPositionInArray(
|
|
235
|
+
this.sortedValues,
|
|
236
|
+
normalizedFrom,
|
|
237
|
+
this.compareFn
|
|
238
|
+
);
|
|
239
|
+
while (startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0) {
|
|
240
|
+
startIdx++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (let i = startIdx; i < this.sortedValues.length && result.length < n; i++) {
|
|
244
|
+
const keys = this.valueMap.get(this.sortedValues[i]);
|
|
245
|
+
if (keys) {
|
|
246
|
+
for (const key of keys) {
|
|
247
|
+
if (result.length >= n) break;
|
|
248
|
+
if (!filterFn || filterFn(key)) {
|
|
249
|
+
result.push(key);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Returns the next n items in reverse sorted order
|
|
258
|
+
*/
|
|
259
|
+
takeReversed(n, from, filterFn) {
|
|
260
|
+
const result = [];
|
|
261
|
+
let startIdx = this.sortedValues.length - 1;
|
|
262
|
+
if (from !== void 0) {
|
|
263
|
+
const normalizedFrom = normalizeValue(from);
|
|
264
|
+
startIdx = findInsertPositionInArray(
|
|
265
|
+
this.sortedValues,
|
|
266
|
+
normalizedFrom,
|
|
267
|
+
this.compareFn
|
|
268
|
+
) - 1;
|
|
269
|
+
while (startIdx >= 0 && this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0) {
|
|
270
|
+
startIdx--;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (let i = startIdx; i >= 0 && result.length < n; i--) {
|
|
274
|
+
const keys = this.valueMap.get(this.sortedValues[i]);
|
|
275
|
+
if (keys) {
|
|
276
|
+
for (const key of keys) {
|
|
277
|
+
if (result.length >= n) break;
|
|
278
|
+
if (!filterFn || filterFn(key)) {
|
|
279
|
+
result.push(key);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Returns the first n items in sorted order (from the start)
|
|
288
|
+
*/
|
|
289
|
+
takeFromStart(n, filterFn) {
|
|
290
|
+
const result = [];
|
|
291
|
+
for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {
|
|
292
|
+
const keys = this.valueMap.get(this.sortedValues[i]);
|
|
293
|
+
if (keys) {
|
|
294
|
+
for (const key of keys) {
|
|
295
|
+
if (result.length >= n) break;
|
|
296
|
+
if (!filterFn || filterFn(key)) {
|
|
297
|
+
result.push(key);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Returns the first n items in reverse sorted order (from the end)
|
|
306
|
+
*/
|
|
307
|
+
takeReversedFromEnd(n, filterFn) {
|
|
308
|
+
const result = [];
|
|
309
|
+
for (let i = this.sortedValues.length - 1; i >= 0 && result.length < n; i--) {
|
|
310
|
+
const keys = this.valueMap.get(this.sortedValues[i]);
|
|
311
|
+
if (keys) {
|
|
312
|
+
for (const key of keys) {
|
|
313
|
+
if (result.length >= n) break;
|
|
314
|
+
if (!filterFn || filterFn(key)) {
|
|
315
|
+
result.push(key);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Performs an IN array lookup - O(k) where k is values.length
|
|
324
|
+
*/
|
|
325
|
+
inArrayLookup(values) {
|
|
326
|
+
const result = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const value of values) {
|
|
328
|
+
const normalizedValue = normalizeValue(value);
|
|
329
|
+
const keys = this.valueMap.get(normalizedValue);
|
|
330
|
+
if (keys) {
|
|
331
|
+
keys.forEach((key) => result.add(key));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
// Getter methods for testing/compatibility
|
|
337
|
+
get indexedKeysSet() {
|
|
338
|
+
return this.indexedKeys;
|
|
339
|
+
}
|
|
340
|
+
get orderedEntriesArray() {
|
|
341
|
+
return this.sortedValues.map((value) => [
|
|
342
|
+
value,
|
|
343
|
+
this.valueMap.get(value) ?? /* @__PURE__ */ new Set()
|
|
344
|
+
]);
|
|
345
|
+
}
|
|
346
|
+
get orderedEntriesArrayReversed() {
|
|
347
|
+
const result = [];
|
|
348
|
+
for (let i = this.sortedValues.length - 1; i >= 0; i--) {
|
|
349
|
+
const value = this.sortedValues[i];
|
|
350
|
+
result.push([value, this.valueMap.get(value) ?? /* @__PURE__ */ new Set()]);
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
get valueMapData() {
|
|
355
|
+
return this.valueMap;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
export {
|
|
359
|
+
BasicIndex
|
|
360
|
+
};
|
|
361
|
+
//# sourceMappingURL=basic-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basic-index.js","sources":["../../../src/indexes/basic-index.ts"],"sourcesContent":["import { defaultComparator, normalizeValue } from '../utils/comparison.js'\nimport {\n deleteInSortedArray,\n findInsertPositionInArray,\n} from '../utils/array-utils.js'\nimport { BaseIndex } from './base-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression } from '../query/ir.js'\nimport type { IndexOperation } from './base-index.js'\n\n/**\n * Options for range queries\n */\nexport interface RangeQueryOptions {\n from?: any\n to?: any\n fromInclusive?: boolean\n toInclusive?: boolean\n}\n\n/**\n * Options for Basic index\n */\nexport interface BasicIndexOptions {\n compareFn?: (a: any, b: any) => number\n compareOptions?: CompareOptions\n}\n\n/**\n * Basic index using Map + sorted Array.\n *\n * - Map for O(1) equality lookups\n * - Sorted Array for O(log n) range queries via binary search\n * - O(n) updates to maintain sort order\n *\n * Simpler and smaller than BTreeIndex, good for read-heavy workloads.\n * Use BTreeIndex for write-heavy workloads with large collections.\n */\nexport class BasicIndex<\n TKey extends string | number = string | number,\n> extends BaseIndex<TKey> {\n public readonly supportedOperations = new Set<IndexOperation>([\n `eq`,\n `gt`,\n `gte`,\n `lt`,\n `lte`,\n `in`,\n ])\n\n // Map for O(1) equality lookups: indexedValue -> Set of PKs\n private valueMap = new Map<any, Set<TKey>>()\n // Sorted array of unique indexed values for range queries\n private sortedValues: Array<any> = []\n // Set of all indexed PKs\n private indexedKeys = new Set<TKey>()\n // Comparator function\n private compareFn: (a: any, b: any) => number = defaultComparator\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) {\n super(id, expression, name, options)\n this.compareFn = options?.compareFn ?? defaultComparator\n if (options?.compareOptions) {\n this.compareOptions = options!.compareOptions\n }\n }\n\n protected initialize(_options?: BasicIndexOptions): void {}\n\n /**\n * Adds a value to the index\n */\n add(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`,\n { cause: error },\n )\n }\n\n const normalizedValue = normalizeValue(indexedValue)\n\n if (this.valueMap.has(normalizedValue)) {\n // Value already exists, just add the key to the set\n this.valueMap.get(normalizedValue)!.add(key)\n } else {\n // New value - add to map and insert into sorted array\n this.valueMap.set(normalizedValue, new Set([key]))\n\n // Insert into sorted position\n const insertIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedValue,\n this.compareFn,\n )\n this.sortedValues.splice(insertIdx, 0, normalizedValue)\n }\n\n this.indexedKeys.add(key)\n this.updateTimestamp()\n }\n\n /**\n * Removes a value from the index\n */\n remove(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n console.warn(\n `Failed to evaluate index expression for key ${key} during removal:`,\n error,\n )\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n return\n }\n\n const normalizedValue = normalizeValue(indexedValue)\n\n if (this.valueMap.has(normalizedValue)) {\n const keySet = this.valueMap.get(normalizedValue)!\n keySet.delete(key)\n\n if (keySet.size === 0) {\n // No more keys for this value, remove from map and sorted array\n this.valueMap.delete(normalizedValue)\n deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn)\n }\n }\n\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n }\n\n /**\n * Updates a value in the index\n */\n update(key: TKey, oldItem: any, newItem: any): void {\n this.remove(key, oldItem)\n this.add(key, newItem)\n }\n\n /**\n * Builds the index from a collection of entries\n */\n build(entries: Iterable<[TKey, any]>): void {\n this.clear()\n\n // Collect all entries first\n const entriesArray: Array<{ key: TKey; value: any }> = []\n for (const [key, item] of entries) {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`,\n { cause: error },\n )\n }\n entriesArray.push({ key, value: normalizeValue(indexedValue) })\n this.indexedKeys.add(key)\n }\n\n // Group by value\n for (const { key, value } of entriesArray) {\n if (this.valueMap.has(value)) {\n this.valueMap.get(value)!.add(key)\n } else {\n this.valueMap.set(value, new Set([key]))\n }\n }\n\n // Build sorted array from unique values\n this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn)\n\n this.updateTimestamp()\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.valueMap.clear()\n this.sortedValues = []\n this.indexedKeys.clear()\n this.updateTimestamp()\n }\n\n /**\n * Performs a lookup operation\n */\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const startTime = performance.now()\n\n let result: Set<TKey>\n\n switch (operation) {\n case `eq`:\n result = this.equalityLookup(value)\n break\n case `gt`:\n result = this.rangeQuery({ from: value, fromInclusive: false })\n break\n case `gte`:\n result = this.rangeQuery({ from: value, fromInclusive: true })\n break\n case `lt`:\n result = this.rangeQuery({ to: value, toInclusive: false })\n break\n case `lte`:\n result = this.rangeQuery({ to: value, toInclusive: true })\n break\n case `in`:\n result = this.inArrayLookup(value)\n break\n default:\n throw new Error(`Operation ${operation} not supported by BasicIndex`)\n }\n\n this.trackLookup(startTime)\n return result\n }\n\n /**\n * Gets the number of indexed keys\n */\n get keyCount(): number {\n return this.indexedKeys.size\n }\n\n /**\n * Performs an equality lookup - O(1)\n */\n equalityLookup(value: any): Set<TKey> {\n const normalizedValue = normalizeValue(value)\n return this.valueMap.get(normalizedValue) ?? new Set()\n }\n\n /**\n * Performs a range query using binary search - O(log n + m)\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n if (this.sortedValues.length === 0) {\n return result\n }\n\n const normalizedFrom = normalizeValue(from)\n const normalizedTo = normalizeValue(to)\n\n // Find start index\n let startIdx = 0\n if (normalizedFrom !== undefined) {\n startIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n )\n // If not inclusive and we found exact match, skip it\n if (\n !fromInclusive &&\n startIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0\n ) {\n startIdx++\n }\n }\n\n // Find end index\n let endIdx = this.sortedValues.length\n if (normalizedTo !== undefined) {\n endIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedTo,\n this.compareFn,\n )\n // If inclusive and we found the value, include it\n if (\n toInclusive &&\n endIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0\n ) {\n endIdx++\n }\n }\n\n // Collect all keys in range\n for (let i = startIdx; i < endIdx; i++) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n /**\n * Performs a reversed range query\n */\n rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n\n // Swap from/to and fromInclusive/toInclusive to handle reversed ranges\n // If to is undefined, we want to start from the end (max value)\n // If from is undefined, we want to end at the beginning (min value)\n const swappedFrom =\n to ??\n (this.sortedValues.length > 0\n ? this.sortedValues[this.sortedValues.length - 1]\n : undefined)\n const swappedTo =\n from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : undefined)\n\n return this.rangeQuery({\n from: swappedFrom,\n to: swappedTo,\n fromInclusive: toInclusive,\n toInclusive: fromInclusive,\n })\n }\n\n /**\n * Returns the next n items in sorted order\n */\n take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {\n const result: Array<TKey> = []\n\n let startIdx = 0\n if (from !== undefined) {\n const normalizedFrom = normalizeValue(from)\n startIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n )\n // Skip past the 'from' value (exclusive)\n while (\n startIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0\n ) {\n startIdx++\n }\n }\n\n for (\n let i = startIdx;\n i < this.sortedValues.length && result.length < n;\n i++\n ) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Returns the next n items in reverse sorted order\n */\n takeReversed(\n n: number,\n from?: any,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey> {\n const result: Array<TKey> = []\n\n let startIdx = this.sortedValues.length - 1\n if (from !== undefined) {\n const normalizedFrom = normalizeValue(from)\n startIdx =\n findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n ) - 1\n // Skip past the 'from' value (exclusive)\n while (\n startIdx >= 0 &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0\n ) {\n startIdx--\n }\n }\n\n for (let i = startIdx; i >= 0 && result.length < n; i--) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Returns the first n items in sorted order (from the start)\n */\n takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey> {\n const result: Array<TKey> = []\n for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n return result\n }\n\n /**\n * Returns the first n items in reverse sorted order (from the end)\n */\n takeReversedFromEnd(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey> {\n const result: Array<TKey> = []\n for (\n let i = this.sortedValues.length - 1;\n i >= 0 && result.length < n;\n i--\n ) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n return result\n }\n\n /**\n * Performs an IN array lookup - O(k) where k is values.length\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const normalizedValue = normalizeValue(value)\n const keys = this.valueMap.get(normalizedValue)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n // Getter methods for testing/compatibility\n get indexedKeysSet(): Set<TKey> {\n return this.indexedKeys\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.sortedValues.map((value) => [\n value,\n this.valueMap.get(value) ?? new Set(),\n ])\n }\n\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {\n const result: Array<[any, Set<TKey>]> = []\n for (let i = this.sortedValues.length - 1; i >= 0; i--) {\n const value = this.sortedValues[i]\n result.push([value, this.valueMap.get(value) ?? new Set()])\n }\n return result\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":[],"mappings":"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"}
|