@tanstack/db 0.3.2 → 0.4.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/{change-events.cjs → collection/change-events.cjs} +13 -42
- package/dist/cjs/collection/change-events.cjs.map +1 -0
- package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
- package/dist/cjs/collection/changes.cjs +108 -0
- package/dist/cjs/collection/changes.cjs.map +1 -0
- package/dist/cjs/collection/changes.d.cts +53 -0
- package/dist/cjs/{collection-events.cjs → collection/events.cjs} +7 -5
- package/dist/cjs/collection/events.cjs.map +1 -0
- package/dist/cjs/{collection-events.d.cts → collection/events.d.cts} +7 -4
- package/dist/cjs/collection/index.cjs +417 -0
- package/dist/cjs/collection/index.cjs.map +1 -0
- package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +46 -184
- package/dist/cjs/collection/indexes.cjs +124 -0
- package/dist/cjs/collection/indexes.cjs.map +1 -0
- package/dist/cjs/collection/indexes.d.cts +47 -0
- package/dist/cjs/collection/lifecycle.cjs +150 -0
- package/dist/cjs/collection/lifecycle.cjs.map +1 -0
- package/dist/cjs/collection/lifecycle.d.cts +70 -0
- package/dist/cjs/collection/mutations.cjs +315 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -0
- package/dist/cjs/collection/mutations.d.cts +33 -0
- package/dist/cjs/collection/state.cjs +597 -0
- package/dist/cjs/collection/state.cjs.map +1 -0
- package/dist/cjs/collection/state.d.cts +122 -0
- package/dist/cjs/collection/subscription.cjs +160 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -0
- package/dist/cjs/collection/subscription.d.cts +57 -0
- package/dist/cjs/collection/sync.cjs +154 -0
- package/dist/cjs/collection/sync.cjs.map +1 -0
- package/dist/cjs/collection/sync.d.cts +34 -0
- package/dist/cjs/index.cjs +8 -8
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.d.cts +1 -1
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -2
- package/dist/cjs/indexes/btree-index.cjs +2 -2
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/query/builder/index.cjs +2 -2
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +1 -1
- package/dist/cjs/query/compiler/index.cjs +5 -2
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +3 -2
- package/dist/cjs/query/compiler/joins.cjs +22 -24
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +3 -2
- 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/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +1 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
- package/dist/cjs/query/live-query-collection.cjs +2 -2
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +1 -1
- package/dist/cjs/transactions.cjs +3 -3
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +12 -10
- package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
- package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
- package/dist/esm/collection/change-events.js.map +1 -0
- package/dist/esm/collection/changes.d.ts +53 -0
- package/dist/esm/collection/changes.js +108 -0
- package/dist/esm/collection/changes.js.map +1 -0
- package/dist/esm/{collection-events.d.ts → collection/events.d.ts} +7 -4
- package/dist/esm/{collection-events.js → collection/events.js} +7 -5
- package/dist/esm/collection/events.js.map +1 -0
- package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +46 -184
- package/dist/esm/collection/index.js +417 -0
- package/dist/esm/collection/index.js.map +1 -0
- package/dist/esm/collection/indexes.d.ts +47 -0
- package/dist/esm/collection/indexes.js +124 -0
- package/dist/esm/collection/indexes.js.map +1 -0
- package/dist/esm/collection/lifecycle.d.ts +70 -0
- package/dist/esm/collection/lifecycle.js +150 -0
- package/dist/esm/collection/lifecycle.js.map +1 -0
- package/dist/esm/collection/mutations.d.ts +33 -0
- package/dist/esm/collection/mutations.js +315 -0
- package/dist/esm/collection/mutations.js.map +1 -0
- package/dist/esm/collection/state.d.ts +122 -0
- package/dist/esm/collection/state.js +597 -0
- package/dist/esm/collection/state.js.map +1 -0
- package/dist/esm/collection/subscription.d.ts +57 -0
- package/dist/esm/collection/subscription.js +160 -0
- package/dist/esm/collection/subscription.js.map +1 -0
- package/dist/esm/collection/sync.d.ts +34 -0
- package/dist/esm/collection/sync.js +154 -0
- package/dist/esm/collection/sync.js.map +1 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/indexes/auto-index.d.ts +1 -1
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -2
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js +2 -2
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/proxy.js +1 -1
- package/dist/esm/query/builder/index.js +1 -1
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +1 -1
- package/dist/esm/query/compiler/index.d.ts +3 -2
- package/dist/esm/query/compiler/index.js +5 -2
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +3 -2
- package/dist/esm/query/compiler/joins.js +22 -24
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/ir.d.ts +1 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
- package/dist/esm/query/live/collection-config-builder.js +29 -12
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
- package/dist/esm/query/live/collection-subscriber.js +43 -104
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +1 -1
- package/dist/esm/query/live-query-collection.js +1 -1
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/transactions.js +3 -3
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +12 -10
- package/package.json +2 -2
- package/src/{change-events.ts → collection/change-events.ts} +25 -39
- package/src/collection/changes.ts +163 -0
- package/src/{collection-events.ts → collection/events.ts} +8 -6
- package/src/collection/index.ts +808 -0
- package/src/collection/indexes.ts +172 -0
- package/src/collection/lifecycle.ts +221 -0
- package/src/collection/mutations.ts +535 -0
- package/src/collection/state.ts +866 -0
- package/src/collection/subscription.ts +239 -0
- package/src/collection/sync.ts +235 -0
- package/src/index.ts +2 -2
- package/src/indexes/auto-index.ts +1 -1
- package/src/indexes/base-index.ts +3 -3
- package/src/indexes/btree-index.ts +2 -2
- package/src/query/builder/index.ts +1 -1
- package/src/query/builder/types.ts +1 -1
- package/src/query/compiler/index.ts +7 -1
- package/src/query/compiler/joins.ts +28 -41
- package/src/query/compiler/order-by.ts +1 -1
- package/src/query/ir.ts +1 -1
- package/src/query/live/collection-config-builder.ts +48 -22
- package/src/query/live/collection-subscriber.ts +63 -168
- package/src/query/live-query-collection.ts +2 -2
- package/src/transactions.ts +3 -3
- package/src/types.ts +14 -15
- package/dist/cjs/change-events.cjs.map +0 -1
- package/dist/cjs/collection-events.cjs.map +0 -1
- package/dist/cjs/collection.cjs +0 -1625
- package/dist/cjs/collection.cjs.map +0 -1
- package/dist/esm/change-events.js.map +0 -1
- package/dist/esm/collection-events.js.map +0 -1
- package/dist/esm/collection.js +0 -1625
- package/dist/esm/collection.js.map +0 -1
- package/src/collection.ts +0 -2564
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { CollectionConfig } from '../types.cjs';
|
|
3
|
+
import { CollectionImpl } from './index.js';
|
|
4
|
+
import { CollectionStateManager } from './state.cjs';
|
|
5
|
+
import { CollectionLifecycleManager } from './lifecycle.cjs';
|
|
6
|
+
export declare class CollectionSyncManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
|
|
7
|
+
private collection;
|
|
8
|
+
private state;
|
|
9
|
+
private lifecycle;
|
|
10
|
+
private config;
|
|
11
|
+
private id;
|
|
12
|
+
preloadPromise: Promise<void> | null;
|
|
13
|
+
syncCleanupFn: (() => void) | null;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new CollectionSyncManager instance
|
|
16
|
+
*/
|
|
17
|
+
constructor(config: CollectionConfig<TOutput, TKey, TSchema>, id: string);
|
|
18
|
+
setDeps(deps: {
|
|
19
|
+
collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>;
|
|
20
|
+
state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
|
|
21
|
+
lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
|
|
22
|
+
}): void;
|
|
23
|
+
/**
|
|
24
|
+
* Start the sync process for this collection
|
|
25
|
+
* This is called when the collection is first accessed or preloaded
|
|
26
|
+
*/
|
|
27
|
+
startSync(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Preload the collection data by starting sync if not already started
|
|
30
|
+
* Multiple concurrent calls will share the same promise
|
|
31
|
+
*/
|
|
32
|
+
preload(): Promise<void>;
|
|
33
|
+
cleanup(): void;
|
|
34
|
+
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
3
|
+
const index = require("./collection/index.cjs");
|
|
4
4
|
const SortedMap = require("./SortedMap.cjs");
|
|
5
5
|
const transactions = require("./transactions.cjs");
|
|
6
6
|
const proxy = require("./proxy.cjs");
|
|
@@ -11,12 +11,12 @@ const errors = require("./errors.cjs");
|
|
|
11
11
|
const baseIndex = require("./indexes/base-index.cjs");
|
|
12
12
|
const btreeIndex = require("./indexes/btree-index.cjs");
|
|
13
13
|
const lazyIndex = require("./indexes/lazy-index.cjs");
|
|
14
|
-
const index = require("./query/builder/index.cjs");
|
|
14
|
+
const index$1 = require("./query/builder/index.cjs");
|
|
15
15
|
const functions = require("./query/builder/functions.cjs");
|
|
16
|
-
const index$
|
|
16
|
+
const index$2 = require("./query/compiler/index.cjs");
|
|
17
17
|
const liveQueryCollection = require("./query/live-query-collection.cjs");
|
|
18
|
-
exports.CollectionImpl =
|
|
19
|
-
exports.createCollection =
|
|
18
|
+
exports.CollectionImpl = index.CollectionImpl;
|
|
19
|
+
exports.createCollection = index.createCollection;
|
|
20
20
|
exports.SortedMap = SortedMap.SortedMap;
|
|
21
21
|
exports.createTransaction = transactions.createTransaction;
|
|
22
22
|
exports.getActiveTransaction = transactions.getActiveTransaction;
|
|
@@ -108,8 +108,8 @@ exports.IndexOperation = baseIndex.IndexOperation;
|
|
|
108
108
|
exports.BTreeIndex = btreeIndex.BTreeIndex;
|
|
109
109
|
exports.IndexProxy = lazyIndex.IndexProxy;
|
|
110
110
|
exports.LazyIndexWrapper = lazyIndex.LazyIndexWrapper;
|
|
111
|
-
exports.BaseQueryBuilder = index.BaseQueryBuilder;
|
|
112
|
-
exports.Query = index.Query;
|
|
111
|
+
exports.BaseQueryBuilder = index$1.BaseQueryBuilder;
|
|
112
|
+
exports.Query = index$1.Query;
|
|
113
113
|
exports.add = functions.add;
|
|
114
114
|
exports.and = functions.and;
|
|
115
115
|
exports.avg = functions.avg;
|
|
@@ -134,7 +134,7 @@ exports.not = functions.not;
|
|
|
134
134
|
exports.or = functions.or;
|
|
135
135
|
exports.sum = functions.sum;
|
|
136
136
|
exports.upper = functions.upper;
|
|
137
|
-
exports.compileQuery = index$
|
|
137
|
+
exports.compileQuery = index$2.compileQuery;
|
|
138
138
|
exports.createLiveQueryCollection = liveQueryCollection.createLiveQueryCollection;
|
|
139
139
|
exports.liveQueryCollectionOptions = liveQueryCollection.liveQueryCollectionOptions;
|
|
140
140
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from './collection.
|
|
1
|
+
export * from './collection/index.js';
|
|
2
2
|
export * from './SortedMap.cjs';
|
|
3
3
|
export * from './transactions.cjs';
|
|
4
4
|
export * from './types.cjs';
|
|
@@ -12,4 +12,4 @@ export * from './indexes/base-index.js';
|
|
|
12
12
|
export * from './indexes/btree-index.js';
|
|
13
13
|
export * from './indexes/lazy-index.js';
|
|
14
14
|
export { type IndexOptions } from './indexes/index-options.js';
|
|
15
|
-
export type { Collection } from './collection.
|
|
15
|
+
export type { Collection } from './collection/index.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-index.cjs","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { BTreeIndex } from \"./btree-index\"\nimport type { BasicExpression } from \"../query/ir\"\nimport type { CollectionImpl } from \"../collection\"\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 if (collection.config.autoIndex !== `eager`) {\n return false\n }\n\n // Don't auto-index during sync operations\n if (\n collection.status === `loading` ||\n collection.status === `initialCommit`\n ) {\n return false\n }\n\n return true\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 compareFn?: (a: any, b: any) => number\n) {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n // Check if we already have an index for this field\n const existingIndex = Array.from(collection.indexes.values()).find((index) =>\n index.matchesField(fieldPath)\n )\n\n if (existingIndex) {\n return // Index already exists\n }\n\n // Create a new index for this field using the collection's createIndex method\n try {\n collection.createIndex((row) => (row as any)[fieldName], {\n name: `auto_${fieldName}`,\n indexType: BTreeIndex,\n options: compareFn ? { compareFn } : {},\n })\n } catch (error) {\n console.warn(`Failed to create auto-index for field \"${fieldName}\":`, error)\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 (single field)\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 it's not a simple field (e.g., nested properties or array access)\n if (fieldPath.length !== 1) {\n return\n }\n\n const fieldName = fieldPath[0]\n results.push({ fieldName, fieldPath })\n }\n\n extractFromExpression(expression)\n return results\n}\n"],"names":["BTreeIndex"],"mappings":";;;AAQA,SAAS,gBAAgB,YAAqD;AAE5E,MAAI,WAAW,OAAO,cAAc,SAAS;AAC3C,WAAO;AAAA,EACT;AAGA,MACE,WAAW,WAAW,aACtB,WAAW,WAAW,iBACtB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,oBAId,WACA,WACA,YACA,WACA;AACA,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAGA,QAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,OAAA,CAAQ,EAAE;AAAA,IAAK,CAAC,UAClE,MAAM,aAAa,SAAS;AAAA,EAAA;AAG9B,MAAI,eAAe;AACjB;AAAA,EACF;AAGA,MAAI;AACF,eAAW,YAAY,CAAC,QAAS,IAAY,SAAS,GAAG;AAAA,MACvD,MAAM,QAAQ,SAAS;AAAA,MACvB,WAAWA,WAAAA;AAAAA,MACX,SAAS,YAAY,EAAE,cAAc,CAAA;AAAA,IAAC,CACvC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,0CAA0C,SAAS,MAAM,KAAK;AAAA,EAC7E;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;AAEA,UAAM,YAAY,UAAU,CAAC;AAC7B,YAAQ,KAAK,EAAE,WAAW,UAAA,CAAW;AAAA,EACvC;AAEA,wBAAsB,UAAU;AAChC,SAAO;AACT;;;"}
|
|
1
|
+
{"version":3,"file":"auto-index.cjs","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { BTreeIndex } from \"./btree-index\"\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 if (collection.config.autoIndex !== `eager`) {\n return false\n }\n\n // Don't auto-index during sync operations\n if (\n collection.status === `loading` ||\n collection.status === `initialCommit`\n ) {\n return false\n }\n\n return true\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 compareFn?: (a: any, b: any) => number\n) {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n // Check if we already have an index for this field\n const existingIndex = Array.from(collection.indexes.values()).find((index) =>\n index.matchesField(fieldPath)\n )\n\n if (existingIndex) {\n return // Index already exists\n }\n\n // Create a new index for this field using the collection's createIndex method\n try {\n collection.createIndex((row) => (row as any)[fieldName], {\n name: `auto_${fieldName}`,\n indexType: BTreeIndex,\n options: compareFn ? { compareFn } : {},\n })\n } catch (error) {\n console.warn(`Failed to create auto-index for field \"${fieldName}\":`, error)\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 (single field)\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 it's not a simple field (e.g., nested properties or array access)\n if (fieldPath.length !== 1) {\n return\n }\n\n const fieldName = fieldPath[0]\n results.push({ fieldName, fieldPath })\n }\n\n extractFromExpression(expression)\n return results\n}\n"],"names":["BTreeIndex"],"mappings":";;;AAQA,SAAS,gBAAgB,YAAqD;AAE5E,MAAI,WAAW,OAAO,cAAc,SAAS;AAC3C,WAAO;AAAA,EACT;AAGA,MACE,WAAW,WAAW,aACtB,WAAW,WAAW,iBACtB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,oBAId,WACA,WACA,YACA,WACA;AACA,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAGA,QAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,OAAA,CAAQ,EAAE;AAAA,IAAK,CAAC,UAClE,MAAM,aAAa,SAAS;AAAA,EAAA;AAG9B,MAAI,eAAe;AACjB;AAAA,EACF;AAGA,MAAI;AACF,eAAW,YAAY,CAAC,QAAS,IAAY,SAAS,GAAG;AAAA,MACvD,MAAM,QAAQ,SAAS;AAAA,MACvB,WAAWA,WAAAA;AAAAA,MACX,SAAS,YAAY,EAAE,cAAc,CAAA;AAAA,IAAC,CACvC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,0CAA0C,SAAS,MAAM,KAAK;AAAA,EAC7E;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;AAEA,UAAM,YAAY,UAAU,CAAC;AAC7B,YAAQ,KAAK,EAAE,WAAW,UAAA,CAAW;AAAA,EACvC;AAEA,wBAAsB,UAAU;AAChC,SAAO;AACT;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-index.cjs","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from \"../query/compiler/evaluators.js\"\nimport { comparisonFunctions } from \"../query/builder/functions.js\"\nimport type { BasicExpression
|
|
1
|
+
{"version":3,"file":"base-index.cjs","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from \"../query/compiler/evaluators.js\"\nimport { comparisonFunctions } from \"../query/builder/functions.js\"\nimport type { BasicExpression } 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\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<\n TKey extends string | number = string | number,\n> {\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\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.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 get keyCount(): number\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 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 methods for subclasses\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\n/**\n * Index resolver can be either a class constructor or async loader\n */\nexport type IndexResolver<TKey extends string | number = string | number> =\n | IndexConstructor<TKey>\n | (() => Promise<IndexConstructor<TKey>>)\n"],"names":["comparisonFunctions","compileSingleRowExpression"],"mappings":";;;;AAOO,MAAM,iBAAiBA,UAAAA;AAoBvB,MAAe,UAEpB;AAAA,EAUA,YACE,IACA,YACA,MACA,SACA;AATF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAQ1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAiBA,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,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,EAKU,wBAAwB,MAAgB;AAChD,UAAM,YAAYC,WAAAA,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;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { comparisonFunctions } from '../query/builder/functions.js';
|
|
2
|
-
import { BasicExpression
|
|
2
|
+
import { BasicExpression } from '../query/ir.js';
|
|
3
3
|
/**
|
|
4
4
|
* Operations that indexes can support, imported from available comparison functions
|
|
5
5
|
*/
|
|
@@ -35,7 +35,7 @@ export declare abstract class BaseIndex<TKey extends string | number = string |
|
|
|
35
35
|
abstract build(entries: Iterable<[TKey, any]>): void;
|
|
36
36
|
abstract clear(): void;
|
|
37
37
|
abstract lookup(operation: IndexOperation, value: any): Set<TKey>;
|
|
38
|
-
abstract take(n: number,
|
|
38
|
+
abstract take(n: number, from?: TKey, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
39
39
|
abstract get keyCount(): number;
|
|
40
40
|
supports(operation: IndexOperation): boolean;
|
|
41
41
|
matchesField(fieldPath: Array<string>): boolean;
|
|
@@ -169,7 +169,7 @@ class BTreeIndex extends baseIndex.BaseIndex {
|
|
|
169
169
|
* @param from - The item to start from (exclusive). Starts from the smallest item (inclusive) if not provided.
|
|
170
170
|
* @returns The next n items after the provided key. Returns the first n items if no from item is provided.
|
|
171
171
|
*/
|
|
172
|
-
take(n, from) {
|
|
172
|
+
take(n, from, filterFn) {
|
|
173
173
|
const keysInResult = /* @__PURE__ */ new Set();
|
|
174
174
|
const result = [];
|
|
175
175
|
const nextKey = (k) => this.orderedEntries.nextHigherKey(k);
|
|
@@ -180,7 +180,7 @@ class BTreeIndex extends baseIndex.BaseIndex {
|
|
|
180
180
|
const it = keys.values();
|
|
181
181
|
let ks;
|
|
182
182
|
while (result.length < n && (ks = it.next().value)) {
|
|
183
|
-
if (!keysInResult.has(ks)) {
|
|
183
|
+
if (!keysInResult.has(ks) && ((filterFn == null ? void 0 : filterFn(ks)) ?? true)) {
|
|
184
184
|
result.push(ks);
|
|
185
185
|
keysInResult.add(ks);
|
|
186
186
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"btree-index.cjs","sources":["../../../src/indexes/btree-index.ts"],"sourcesContent":["import { BTree } from \"../utils/btree.js\"\nimport { defaultComparator } from \"../utils/comparison.js\"\nimport { BaseIndex } from \"./base-index.js\"\nimport type { BasicExpression } from \"../query/ir.js\"\nimport type { IndexOperation } from \"./base-index.js\"\n\n/**\n * Options for Ordered index\n */\nexport interface BTreeIndexOptions {\n compareFn?: (a: any, b: any) => number\n}\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 * B+Tree index for sorted data with range queries\n * This maintains items in sorted order and provides efficient range operations\n */\nexport class BTreeIndex<\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 // Internal data structures - private to hide implementation details\n // The `orderedEntries` B+ tree is used for efficient range queries\n // The `valueMap` is used for O(1) lookups of PKs by indexed value\n private orderedEntries: BTree<any, undefined> // we don't associate values with the keys of the B+ tree (the keys are indexed values)\n private valueMap = new Map<any, Set<TKey>>() // instead we store a mapping of indexed values to a set of PKs\n private indexedKeys = new Set<TKey>()\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 this.orderedEntries = new BTree(this.compareFn)\n }\n\n protected initialize(_options?: BTreeIndexOptions): 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 )\n }\n\n // Check if this value already exists\n if (this.valueMap.has(indexedValue)) {\n // Add to existing set\n this.valueMap.get(indexedValue)!.add(key)\n } else {\n // Create new set for this value\n const keySet = new Set<TKey>([key])\n this.valueMap.set(indexedValue, keySet)\n this.orderedEntries.set(indexedValue, undefined)\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 return\n }\n\n if (this.valueMap.has(indexedValue)) {\n const keySet = this.valueMap.get(indexedValue)!\n keySet.delete(key)\n\n // If set is now empty, remove the entry entirely\n if (keySet.size === 0) {\n this.valueMap.delete(indexedValue)\n\n // Remove from ordered entries\n this.orderedEntries.delete(indexedValue)\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 for (const [key, item] of entries) {\n this.add(key, item)\n }\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.orderedEntries.clear()\n this.valueMap.clear()\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 BTreeIndex`)\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 // Public methods for backward compatibility (used by tests)\n\n /**\n * Performs an equality lookup\n */\n equalityLookup(value: any): Set<TKey> {\n return new Set(this.valueMap.get(value) ?? [])\n }\n\n /**\n * Performs a range query with options\n * This is more efficient for compound queries like \"WHERE a > 5 AND a < 10\"\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n const fromKey = from ?? this.orderedEntries.minKey()\n const toKey = to ?? this.orderedEntries.maxKey()\n\n this.orderedEntries.forRange(\n fromKey,\n toKey,\n toInclusive,\n (indexedValue, _) => {\n if (!fromInclusive && this.compareFn(indexedValue, from) === 0) {\n // the B+ tree `forRange` method does not support exclusive lower bounds\n // so we need to exclude it manually\n return\n }\n\n const keys = this.valueMap.get(indexedValue)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n )\n\n return result\n }\n\n /**\n * Returns the next n items after the provided item or the first n items if no from item is provided.\n * @param n - The number of items to return\n * @param from - The item to start from (exclusive). Starts from the smallest item (inclusive) if not provided.\n * @returns The next n items after the provided key. Returns the first n items if no from item is provided.\n */\n take(n: number, from?: any): Array<TKey> {\n const keysInResult: Set<TKey> = new Set()\n const result: Array<TKey> = []\n const nextKey = (k?: any) => this.orderedEntries.nextHigherKey(k)\n let key = from\n\n while ((key = nextKey(key)) && result.length < n) {\n const keys = this.valueMap.get(key)\n if (keys) {\n const it = keys.values()\n let ks: TKey | undefined\n while (result.length < n && (ks = it.next().value)) {\n if (!keysInResult.has(ks)) {\n result.push(ks)\n keysInResult.add(ks)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Performs an IN array lookup\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const keys = this.valueMap.get(value)\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.orderedEntries\n .keysArray()\n .map((key) => [key, this.valueMap.get(key) ?? new Set()])\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":["BaseIndex","defaultComparator","BTree"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"btree-index.cjs","sources":["../../../src/indexes/btree-index.ts"],"sourcesContent":["import { BTree } from \"../utils/btree.js\"\nimport { defaultComparator } from \"../utils/comparison.js\"\nimport { BaseIndex } from \"./base-index.js\"\nimport type { BasicExpression } from \"../query/ir.js\"\nimport type { IndexOperation } from \"./base-index.js\"\n\n/**\n * Options for Ordered index\n */\nexport interface BTreeIndexOptions {\n compareFn?: (a: any, b: any) => number\n}\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 * B+Tree index for sorted data with range queries\n * This maintains items in sorted order and provides efficient range operations\n */\nexport class BTreeIndex<\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 // Internal data structures - private to hide implementation details\n // The `orderedEntries` B+ tree is used for efficient range queries\n // The `valueMap` is used for O(1) lookups of PKs by indexed value\n private orderedEntries: BTree<any, undefined> // we don't associate values with the keys of the B+ tree (the keys are indexed values)\n private valueMap = new Map<any, Set<TKey>>() // instead we store a mapping of indexed values to a set of PKs\n private indexedKeys = new Set<TKey>()\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 this.orderedEntries = new BTree(this.compareFn)\n }\n\n protected initialize(_options?: BTreeIndexOptions): 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 )\n }\n\n // Check if this value already exists\n if (this.valueMap.has(indexedValue)) {\n // Add to existing set\n this.valueMap.get(indexedValue)!.add(key)\n } else {\n // Create new set for this value\n const keySet = new Set<TKey>([key])\n this.valueMap.set(indexedValue, keySet)\n this.orderedEntries.set(indexedValue, undefined)\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 return\n }\n\n if (this.valueMap.has(indexedValue)) {\n const keySet = this.valueMap.get(indexedValue)!\n keySet.delete(key)\n\n // If set is now empty, remove the entry entirely\n if (keySet.size === 0) {\n this.valueMap.delete(indexedValue)\n\n // Remove from ordered entries\n this.orderedEntries.delete(indexedValue)\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 for (const [key, item] of entries) {\n this.add(key, item)\n }\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.orderedEntries.clear()\n this.valueMap.clear()\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 BTreeIndex`)\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 // Public methods for backward compatibility (used by tests)\n\n /**\n * Performs an equality lookup\n */\n equalityLookup(value: any): Set<TKey> {\n return new Set(this.valueMap.get(value) ?? [])\n }\n\n /**\n * Performs a range query with options\n * This is more efficient for compound queries like \"WHERE a > 5 AND a < 10\"\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n const fromKey = from ?? this.orderedEntries.minKey()\n const toKey = to ?? this.orderedEntries.maxKey()\n\n this.orderedEntries.forRange(\n fromKey,\n toKey,\n toInclusive,\n (indexedValue, _) => {\n if (!fromInclusive && this.compareFn(indexedValue, from) === 0) {\n // the B+ tree `forRange` method does not support exclusive lower bounds\n // so we need to exclude it manually\n return\n }\n\n const keys = this.valueMap.get(indexedValue)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n )\n\n return result\n }\n\n /**\n * Returns the next n items after the provided item or the first n items if no from item is provided.\n * @param n - The number of items to return\n * @param from - The item to start from (exclusive). Starts from the smallest item (inclusive) if not provided.\n * @returns The next n items after the provided key. Returns the first n items if no from item is provided.\n */\n take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {\n const keysInResult: Set<TKey> = new Set()\n const result: Array<TKey> = []\n const nextKey = (k?: any) => this.orderedEntries.nextHigherKey(k)\n let key = from\n\n while ((key = nextKey(key)) && result.length < n) {\n const keys = this.valueMap.get(key)\n if (keys) {\n const it = keys.values()\n let ks: TKey | undefined\n while (result.length < n && (ks = it.next().value)) {\n if (!keysInResult.has(ks) && (filterFn?.(ks) ?? true)) {\n result.push(ks)\n keysInResult.add(ks)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Performs an IN array lookup\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const keys = this.valueMap.get(value)\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.orderedEntries\n .keysArray()\n .map((key) => [key, this.valueMap.get(key) ?? new Set()])\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":["BaseIndex","defaultComparator","BTree"],"mappings":"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"}
|
|
@@ -70,7 +70,7 @@ export declare class BTreeIndex<TKey extends string | number = string | number>
|
|
|
70
70
|
* @param from - The item to start from (exclusive). Starts from the smallest item (inclusive) if not provided.
|
|
71
71
|
* @returns The next n items after the provided key. Returns the first n items if no from item is provided.
|
|
72
72
|
*/
|
|
73
|
-
take(n: number, from?: any): Array<TKey>;
|
|
73
|
+
take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey>;
|
|
74
74
|
/**
|
|
75
75
|
* Performs an IN array lookup
|
|
76
76
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
3
|
+
const index = require("../../collection/index.cjs");
|
|
4
4
|
const ir = require("../ir.cjs");
|
|
5
5
|
const errors = require("../../errors.cjs");
|
|
6
6
|
const refProxy = require("./ref-proxy.cjs");
|
|
@@ -22,7 +22,7 @@ class BaseQueryBuilder {
|
|
|
22
22
|
const alias = Object.keys(source)[0];
|
|
23
23
|
const sourceValue = source[alias];
|
|
24
24
|
let ref;
|
|
25
|
-
if (sourceValue instanceof
|
|
25
|
+
if (sourceValue instanceof index.CollectionImpl) {
|
|
26
26
|
ref = new ir.CollectionRef(sourceValue, alias);
|
|
27
27
|
} else if (sourceValue instanceof BaseQueryBuilder) {
|
|
28
28
|
const subQuery = sourceValue._getQuery();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/query/builder/index.ts"],"sourcesContent":["import { CollectionImpl } from \"../../collection.js\"\nimport {\n Aggregate as AggregateExpr,\n CollectionRef,\n Func as FuncExpr,\n PropRef,\n QueryRef,\n Value as ValueExpr,\n isExpressionLike,\n} from \"../ir.js\"\nimport {\n InvalidSourceError,\n JoinConditionMustBeEqualityError,\n OnlyOneSourceAllowedError,\n QueryMustHaveFromClauseError,\n SubQueryMustHaveFromClauseError,\n} from \"../../errors.js\"\nimport { createRefProxy, toExpression } from \"./ref-proxy.js\"\nimport type { NamespacedRow } from \"../../types.js\"\nimport type {\n Aggregate,\n BasicExpression,\n JoinClause,\n OrderBy,\n OrderByDirection,\n QueryIR,\n} from \"../ir.js\"\nimport type {\n CompareOptions,\n Context,\n GroupByCallback,\n JoinOnCallback,\n MergeContextForJoinCallback,\n MergeContextWithJoinType,\n OrderByCallback,\n OrderByOptions,\n RefsForContext,\n ResultTypeFromSelect,\n SchemaFromSource,\n SelectObject,\n Source,\n WhereCallback,\n WithResult,\n} from \"./types.js\"\n\nexport class BaseQueryBuilder<TContext extends Context = Context> {\n private readonly query: Partial<QueryIR> = {}\n\n constructor(query: Partial<QueryIR> = {}) {\n this.query = { ...query }\n }\n\n /**\n * Creates a CollectionRef or QueryRef from a source object\n * @param source - An object with a single key-value pair\n * @param context - Context string for error messages (e.g., \"from clause\", \"join clause\")\n * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference\n */\n private _createRefForSource<TSource extends Source>(\n source: TSource,\n context: string\n ): [string, CollectionRef | QueryRef] {\n if (Object.keys(source).length !== 1) {\n throw new OnlyOneSourceAllowedError(context)\n }\n\n const alias = Object.keys(source)[0]!\n const sourceValue = source[alias]\n\n let ref: CollectionRef | QueryRef\n\n if (sourceValue instanceof CollectionImpl) {\n ref = new CollectionRef(sourceValue, alias)\n } else if (sourceValue instanceof BaseQueryBuilder) {\n const subQuery = sourceValue._getQuery()\n if (!(subQuery as Partial<QueryIR>).from) {\n throw new SubQueryMustHaveFromClauseError(context)\n }\n ref = new QueryRef(subQuery, alias)\n } else {\n throw new InvalidSourceError(alias)\n }\n\n return [alias, ref]\n }\n\n /**\n * Specify the source table or subquery for the query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @returns A QueryBuilder with the specified source\n *\n * @example\n * ```ts\n * // Query from a collection\n * query.from({ users: usersCollection })\n *\n * // Query from a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query.from({ activeUsers })\n * ```\n */\n from<TSource extends Source>(\n source: TSource\n ): QueryBuilder<{\n baseSchema: SchemaFromSource<TSource>\n schema: SchemaFromSource<TSource>\n fromSourceName: keyof TSource & string\n hasJoins: false\n }> {\n const [, from] = this._createRefForSource(source, `from clause`)\n\n return new BaseQueryBuilder({\n ...this.query,\n from,\n }) as any\n }\n\n /**\n * Join another table or subquery to the current query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')\n * @returns A QueryBuilder with the joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n *\n * // Inner join with explicit type\n * query\n * .from({ u: usersCollection })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')\n * ```\n *\n * // Join with a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query\n * .from({ activeUsers })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))\n */\n join<\n TSource extends Source,\n TJoinType extends `inner` | `left` | `right` | `full` = `left`,\n >(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >,\n type: TJoinType = `left` as TJoinType\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>\n > {\n const [alias, from] = this._createRefForSource(source, `join clause`)\n\n // Create a temporary context for the callback\n const currentAliases = this._getCurrentAliases()\n const newAliases = [...currentAliases, alias]\n const refProxy = createRefProxy(newAliases) as RefsForContext<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n\n // Get the join condition expression\n const onExpression = onCallback(refProxy)\n\n // Extract left and right from the expression\n // For now, we'll assume it's an eq function with two arguments\n let left: BasicExpression\n let right: BasicExpression\n\n if (\n onExpression.type === `func` &&\n onExpression.name === `eq` &&\n onExpression.args.length === 2\n ) {\n left = onExpression.args[0]!\n right = onExpression.args[1]!\n } else {\n throw new JoinConditionMustBeEqualityError()\n }\n\n const joinClause: JoinClause = {\n from,\n type,\n left,\n right,\n }\n\n const existingJoins = this.query.join || []\n\n return new BaseQueryBuilder({\n ...this.query,\n join: [...existingJoins, joinClause],\n }) as any\n }\n\n /**\n * Perform a LEFT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the left joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .leftJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n leftJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `left`>\n > {\n return this.join(source, onCallback, `left`)\n }\n\n /**\n * Perform a RIGHT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the right joined table available\n *\n * @example\n * ```ts\n * // Right join users with posts\n * query\n * .from({ users: usersCollection })\n * .rightJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n rightJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `right`>\n > {\n return this.join(source, onCallback, `right`)\n }\n\n /**\n * Perform an INNER JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the inner joined table available\n *\n * @example\n * ```ts\n * // Inner join users with posts\n * query\n * .from({ users: usersCollection })\n * .innerJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n innerJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `inner`>\n > {\n return this.join(source, onCallback, `inner`)\n }\n\n /**\n * Perform a FULL JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the full joined table available\n *\n * @example\n * ```ts\n * // Full join users with posts\n * query\n * .from({ users: usersCollection })\n * .fullJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n fullJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `full`>\n > {\n return this.join(source, onCallback, `full`)\n }\n\n /**\n * Filter rows based on a condition\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the where condition applied\n *\n * @example\n * ```ts\n * // Simple condition\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n *\n * // Multiple conditions\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => and(\n * gt(users.age, 18),\n * eq(users.active, true)\n * ))\n *\n * // Multiple where calls are ANDed together\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n * .where(({users}) => eq(users.active, true))\n * ```\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const expression = callback(refProxy)\n\n const existingWhere = this.query.where || []\n\n return new BaseQueryBuilder({\n ...this.query,\n where: [...existingWhere, expression],\n }) as any\n }\n\n /**\n * Filter grouped rows based on aggregate conditions\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the having condition applied\n *\n * @example\n * ```ts\n * // Filter groups by count\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .having(({posts}) => gt(count(posts.id), 5))\n *\n * // Filter by average\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(avg(orders.total), 100))\n *\n * // Multiple having calls are ANDed together\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(count(orders.id), 5))\n * .having(({orders}) => gt(avg(orders.total), 100))\n * ```\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const expression = callback(refProxy)\n\n const existingHaving = this.query.having || []\n\n return new BaseQueryBuilder({\n ...this.query,\n having: [...existingHaving, expression],\n }) as any\n }\n\n /**\n * Select specific columns or computed values from the query\n *\n * @param callback - A function that receives table references and returns an object with selected fields or expressions\n * @returns A QueryBuilder that returns only the selected fields\n *\n * @example\n * ```ts\n * // Select specific columns\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * name: users.name,\n * email: users.email\n * }))\n *\n * // Select with computed values\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * fullName: concat(users.firstName, ' ', users.lastName),\n * ageInMonths: mul(users.age, 12)\n * }))\n *\n * // Select with aggregates (requires GROUP BY)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count(posts.id)\n * }))\n * ```\n */\n select<TSelectObject extends SelectObject>(\n callback: (refs: RefsForContext<TContext>) => TSelectObject\n ): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const selectObject = callback(refProxy)\n const select = buildNestedSelect(selectObject)\n\n return new BaseQueryBuilder({\n ...this.query,\n select: select,\n fnSelect: undefined, // remove the fnSelect clause if it exists\n }) as any\n }\n\n /**\n * Sort the query results by one or more columns\n *\n * @param callback - A function that receives table references and returns the field to sort by\n * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')\n * @returns A QueryBuilder with the ordering applied\n *\n * @example\n * ```ts\n * // Sort by a single column\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.name)\n *\n * // Sort descending\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.createdAt, 'desc')\n *\n * // Multiple sorts (chain orderBy calls)\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.lastName)\n * .orderBy(({users}) => users.firstName)\n * ```\n */\n orderBy(\n callback: OrderByCallback<TContext>,\n options: OrderByDirection | OrderByOptions = `asc`\n ): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const result = callback(refProxy)\n\n const opts: CompareOptions =\n typeof options === `string`\n ? { direction: options, nulls: `first`, stringSort: `locale` }\n : {\n direction: options.direction ?? `asc`,\n nulls: options.nulls ?? `first`,\n stringSort: options.stringSort ?? `locale`,\n locale:\n options.stringSort === `locale` ? options.locale : undefined,\n localeOptions:\n options.stringSort === `locale`\n ? options.localeOptions\n : undefined,\n }\n\n const makeOrderByClause = (res: any) => {\n return {\n expression: toExpression(res),\n compareOptions: opts,\n }\n }\n\n // Create the new OrderBy structure with expression and direction\n const orderByClauses = Array.isArray(result)\n ? result.map((r) => makeOrderByClause(r))\n : [makeOrderByClause(result)]\n\n const existingOrderBy: OrderBy = this.query.orderBy || []\n\n return new BaseQueryBuilder({\n ...this.query,\n orderBy: [...existingOrderBy, ...orderByClauses],\n }) as any\n }\n\n /**\n * Group rows by one or more columns for aggregation\n *\n * @param callback - A function that receives table references and returns the field(s) to group by\n * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)\n *\n * @example\n * ```ts\n * // Group by a single column\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count()\n * }))\n *\n * // Group by multiple columns\n * query\n * .from({ sales: salesCollection })\n * .groupBy(({sales}) => [sales.region, sales.category])\n * .select(({sales, sum}) => ({\n * region: sales.region,\n * category: sales.category,\n * totalSales: sum(sales.amount)\n * }))\n * ```\n */\n groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const result = callback(refProxy)\n\n const newExpressions = Array.isArray(result)\n ? result.map((r) => toExpression(r))\n : [toExpression(result)]\n\n // Extend existing groupBy expressions (multiple groupBy calls should accumulate)\n const existingGroupBy = this.query.groupBy || []\n return new BaseQueryBuilder({\n ...this.query,\n groupBy: [...existingGroupBy, ...newExpressions],\n }) as any\n }\n\n /**\n * Limit the number of rows returned by the query\n * `orderBy` is required for `limit`\n *\n * @param count - Maximum number of rows to return\n * @returns A QueryBuilder with the limit applied\n *\n * @example\n * ```ts\n * // Get top 5 posts by likes\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.likes, 'desc')\n * .limit(5)\n * ```\n */\n limit(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n limit: count,\n }) as any\n }\n\n /**\n * Skip a number of rows before returning results\n * `orderBy` is required for `offset`\n *\n * @param count - Number of rows to skip\n * @returns A QueryBuilder with the offset applied\n *\n * @example\n * ```ts\n * // Get second page of results\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.createdAt, 'desc')\n * .offset(page * pageSize)\n * .limit(pageSize)\n * ```\n */\n offset(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n offset: count,\n }) as any\n }\n\n /**\n * Specify that the query should return distinct rows.\n * Deduplicates rows based on the selected columns.\n * @returns A QueryBuilder with distinct enabled\n *\n * @example\n * ```ts\n * // Get countries our users are from\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => users.country)\n * .distinct()\n * ```\n */\n distinct(): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n distinct: true,\n }) as any\n }\n\n // Helper methods\n private _getCurrentAliases(): Array<string> {\n const aliases: Array<string> = []\n\n // Add the from alias\n if (this.query.from) {\n aliases.push(this.query.from.alias)\n }\n\n // Add join aliases\n if (this.query.join) {\n for (const join of this.query.join) {\n aliases.push(join.from.alias)\n }\n }\n\n return aliases\n }\n\n /**\n * Functional variants of the query builder\n * These are imperative function that are called for ery row.\n * Warning: that these cannot be optimized by the query compiler, and may prevent\n * some type of optimizations being possible.\n * @example\n * ```ts\n * q.fn.select((row) => ({\n * name: row.user.name.toUpperCase(),\n * age: row.user.age + 1,\n * }))\n * ```\n */\n get fn() {\n const builder = this\n return {\n /**\n * Select fields using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns the selected value\n * @returns A QueryBuilder with functional selection applied\n *\n * @example\n * ```ts\n * // Functional select (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.select(row => ({\n * name: row.users.name.toUpperCase(),\n * age: row.users.age + 1,\n * }))\n * ```\n */\n select<TFuncSelectResult>(\n callback: (row: TContext[`schema`]) => TFuncSelectResult\n ): QueryBuilder<WithResult<TContext, TFuncSelectResult>> {\n return new BaseQueryBuilder({\n ...builder.query,\n select: undefined, // remove the select clause if it exists\n fnSelect: callback,\n })\n },\n /**\n * Filter rows using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns a boolean\n * @returns A QueryBuilder with functional filtering applied\n *\n * @example\n * ```ts\n * // Functional where (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.where(row => row.users.name.startsWith('A'))\n * ```\n */\n where(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnWhere: [\n ...(builder.query.fnWhere || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n /**\n * Filter grouped rows using a function that operates on each aggregated row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives an aggregated row and returns a boolean\n * @returns A QueryBuilder with functional having filter applied\n *\n * @example\n * ```ts\n * // Functional having (not optimized)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .fn.having(row => row.count > 5)\n * ```\n */\n having(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnHaving: [\n ...(builder.query.fnHaving || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n }\n }\n\n _getQuery(): QueryIR {\n if (!this.query.from) {\n throw new QueryMustHaveFromClauseError()\n }\n return this.query as QueryIR\n }\n}\n\n// Helper to ensure we have a BasicExpression/Aggregate for a value\nfunction toExpr(value: any): BasicExpression | Aggregate {\n if (value === undefined) return toExpression(null)\n if (\n value instanceof AggregateExpr ||\n value instanceof FuncExpr ||\n value instanceof PropRef ||\n value instanceof ValueExpr\n ) {\n return value as BasicExpression | Aggregate\n }\n return toExpression(value)\n}\n\nfunction isPlainObject(value: any): value is Record<string, any> {\n return (\n value !== null &&\n typeof value === `object` &&\n !isExpressionLike(value) &&\n !value.__refProxy\n )\n}\n\nfunction buildNestedSelect(obj: any): any {\n if (!isPlainObject(obj)) return toExpr(obj)\n const out: Record<string, any> = {}\n for (const [k, v] of Object.entries(obj)) {\n if (typeof k === `string` && k.startsWith(`__SPREAD_SENTINEL__`)) {\n // Preserve sentinel key and its value (value is unimportant at compile time)\n out[k] = v\n continue\n }\n out[k] = buildNestedSelect(v)\n }\n return out\n}\n\n// Internal function to build a query from a callback\n// used by liveQueryCollectionOptions.query\nexport function buildQuery<TContext extends Context>(\n fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>\n): QueryIR {\n const result = fn(new BaseQueryBuilder())\n return getQueryIR(result)\n}\n\n// Internal function to get the QueryIR from a builder\nexport function getQueryIR(\n builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder\n): QueryIR {\n return (builder as unknown as BaseQueryBuilder)._getQuery()\n}\n\n// Type-only exports for the query builder\nexport type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>\n\nexport type InitialQueryBuilderConstructor = new () => InitialQueryBuilder\n\nexport type QueryBuilder<TContext extends Context> = Omit<\n BaseQueryBuilder<TContext>,\n `from` | `_getQuery`\n>\n\n// Main query builder class alias with the constructor type modified to hide all\n// but the from method on the initial instance\nexport const Query: InitialQueryBuilderConstructor = BaseQueryBuilder\n\n// Helper type to extract context from a QueryBuilder\nexport type ExtractContext<T> =\n T extends BaseQueryBuilder<infer TContext>\n ? TContext\n : T extends QueryBuilder<infer TContext>\n ? TContext\n : never\n\n// Export the types from types.ts for convenience\nexport type { Context, Source, GetResult, RefLeaf as Ref } from \"./types.js\"\n"],"names":["OnlyOneSourceAllowedError","CollectionImpl","CollectionRef","SubQueryMustHaveFromClauseError","QueryRef","InvalidSourceError","refProxy","createRefProxy","JoinConditionMustBeEqualityError","toExpression","QueryMustHaveFromClauseError","AggregateExpr","FuncExpr","PropRef","ValueExpr","isExpressionLike"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/query/builder/index.ts"],"sourcesContent":["import { CollectionImpl } from \"../../collection/index.js\"\nimport {\n Aggregate as AggregateExpr,\n CollectionRef,\n Func as FuncExpr,\n PropRef,\n QueryRef,\n Value as ValueExpr,\n isExpressionLike,\n} from \"../ir.js\"\nimport {\n InvalidSourceError,\n JoinConditionMustBeEqualityError,\n OnlyOneSourceAllowedError,\n QueryMustHaveFromClauseError,\n SubQueryMustHaveFromClauseError,\n} from \"../../errors.js\"\nimport { createRefProxy, toExpression } from \"./ref-proxy.js\"\nimport type { NamespacedRow } from \"../../types.js\"\nimport type {\n Aggregate,\n BasicExpression,\n JoinClause,\n OrderBy,\n OrderByDirection,\n QueryIR,\n} from \"../ir.js\"\nimport type {\n CompareOptions,\n Context,\n GroupByCallback,\n JoinOnCallback,\n MergeContextForJoinCallback,\n MergeContextWithJoinType,\n OrderByCallback,\n OrderByOptions,\n RefsForContext,\n ResultTypeFromSelect,\n SchemaFromSource,\n SelectObject,\n Source,\n WhereCallback,\n WithResult,\n} from \"./types.js\"\n\nexport class BaseQueryBuilder<TContext extends Context = Context> {\n private readonly query: Partial<QueryIR> = {}\n\n constructor(query: Partial<QueryIR> = {}) {\n this.query = { ...query }\n }\n\n /**\n * Creates a CollectionRef or QueryRef from a source object\n * @param source - An object with a single key-value pair\n * @param context - Context string for error messages (e.g., \"from clause\", \"join clause\")\n * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference\n */\n private _createRefForSource<TSource extends Source>(\n source: TSource,\n context: string\n ): [string, CollectionRef | QueryRef] {\n if (Object.keys(source).length !== 1) {\n throw new OnlyOneSourceAllowedError(context)\n }\n\n const alias = Object.keys(source)[0]!\n const sourceValue = source[alias]\n\n let ref: CollectionRef | QueryRef\n\n if (sourceValue instanceof CollectionImpl) {\n ref = new CollectionRef(sourceValue, alias)\n } else if (sourceValue instanceof BaseQueryBuilder) {\n const subQuery = sourceValue._getQuery()\n if (!(subQuery as Partial<QueryIR>).from) {\n throw new SubQueryMustHaveFromClauseError(context)\n }\n ref = new QueryRef(subQuery, alias)\n } else {\n throw new InvalidSourceError(alias)\n }\n\n return [alias, ref]\n }\n\n /**\n * Specify the source table or subquery for the query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @returns A QueryBuilder with the specified source\n *\n * @example\n * ```ts\n * // Query from a collection\n * query.from({ users: usersCollection })\n *\n * // Query from a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query.from({ activeUsers })\n * ```\n */\n from<TSource extends Source>(\n source: TSource\n ): QueryBuilder<{\n baseSchema: SchemaFromSource<TSource>\n schema: SchemaFromSource<TSource>\n fromSourceName: keyof TSource & string\n hasJoins: false\n }> {\n const [, from] = this._createRefForSource(source, `from clause`)\n\n return new BaseQueryBuilder({\n ...this.query,\n from,\n }) as any\n }\n\n /**\n * Join another table or subquery to the current query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')\n * @returns A QueryBuilder with the joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n *\n * // Inner join with explicit type\n * query\n * .from({ u: usersCollection })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')\n * ```\n *\n * // Join with a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query\n * .from({ activeUsers })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))\n */\n join<\n TSource extends Source,\n TJoinType extends `inner` | `left` | `right` | `full` = `left`,\n >(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >,\n type: TJoinType = `left` as TJoinType\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>\n > {\n const [alias, from] = this._createRefForSource(source, `join clause`)\n\n // Create a temporary context for the callback\n const currentAliases = this._getCurrentAliases()\n const newAliases = [...currentAliases, alias]\n const refProxy = createRefProxy(newAliases) as RefsForContext<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n\n // Get the join condition expression\n const onExpression = onCallback(refProxy)\n\n // Extract left and right from the expression\n // For now, we'll assume it's an eq function with two arguments\n let left: BasicExpression\n let right: BasicExpression\n\n if (\n onExpression.type === `func` &&\n onExpression.name === `eq` &&\n onExpression.args.length === 2\n ) {\n left = onExpression.args[0]!\n right = onExpression.args[1]!\n } else {\n throw new JoinConditionMustBeEqualityError()\n }\n\n const joinClause: JoinClause = {\n from,\n type,\n left,\n right,\n }\n\n const existingJoins = this.query.join || []\n\n return new BaseQueryBuilder({\n ...this.query,\n join: [...existingJoins, joinClause],\n }) as any\n }\n\n /**\n * Perform a LEFT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the left joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .leftJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n leftJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `left`>\n > {\n return this.join(source, onCallback, `left`)\n }\n\n /**\n * Perform a RIGHT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the right joined table available\n *\n * @example\n * ```ts\n * // Right join users with posts\n * query\n * .from({ users: usersCollection })\n * .rightJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n rightJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `right`>\n > {\n return this.join(source, onCallback, `right`)\n }\n\n /**\n * Perform an INNER JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the inner joined table available\n *\n * @example\n * ```ts\n * // Inner join users with posts\n * query\n * .from({ users: usersCollection })\n * .innerJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n innerJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `inner`>\n > {\n return this.join(source, onCallback, `inner`)\n }\n\n /**\n * Perform a FULL JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the full joined table available\n *\n * @example\n * ```ts\n * // Full join users with posts\n * query\n * .from({ users: usersCollection })\n * .fullJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n fullJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContextForJoinCallback<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `full`>\n > {\n return this.join(source, onCallback, `full`)\n }\n\n /**\n * Filter rows based on a condition\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the where condition applied\n *\n * @example\n * ```ts\n * // Simple condition\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n *\n * // Multiple conditions\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => and(\n * gt(users.age, 18),\n * eq(users.active, true)\n * ))\n *\n * // Multiple where calls are ANDed together\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n * .where(({users}) => eq(users.active, true))\n * ```\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const expression = callback(refProxy)\n\n const existingWhere = this.query.where || []\n\n return new BaseQueryBuilder({\n ...this.query,\n where: [...existingWhere, expression],\n }) as any\n }\n\n /**\n * Filter grouped rows based on aggregate conditions\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the having condition applied\n *\n * @example\n * ```ts\n * // Filter groups by count\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .having(({posts}) => gt(count(posts.id), 5))\n *\n * // Filter by average\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(avg(orders.total), 100))\n *\n * // Multiple having calls are ANDed together\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(count(orders.id), 5))\n * .having(({orders}) => gt(avg(orders.total), 100))\n * ```\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const expression = callback(refProxy)\n\n const existingHaving = this.query.having || []\n\n return new BaseQueryBuilder({\n ...this.query,\n having: [...existingHaving, expression],\n }) as any\n }\n\n /**\n * Select specific columns or computed values from the query\n *\n * @param callback - A function that receives table references and returns an object with selected fields or expressions\n * @returns A QueryBuilder that returns only the selected fields\n *\n * @example\n * ```ts\n * // Select specific columns\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * name: users.name,\n * email: users.email\n * }))\n *\n * // Select with computed values\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * fullName: concat(users.firstName, ' ', users.lastName),\n * ageInMonths: mul(users.age, 12)\n * }))\n *\n * // Select with aggregates (requires GROUP BY)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count(posts.id)\n * }))\n * ```\n */\n select<TSelectObject extends SelectObject>(\n callback: (refs: RefsForContext<TContext>) => TSelectObject\n ): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const selectObject = callback(refProxy)\n const select = buildNestedSelect(selectObject)\n\n return new BaseQueryBuilder({\n ...this.query,\n select: select,\n fnSelect: undefined, // remove the fnSelect clause if it exists\n }) as any\n }\n\n /**\n * Sort the query results by one or more columns\n *\n * @param callback - A function that receives table references and returns the field to sort by\n * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')\n * @returns A QueryBuilder with the ordering applied\n *\n * @example\n * ```ts\n * // Sort by a single column\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.name)\n *\n * // Sort descending\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.createdAt, 'desc')\n *\n * // Multiple sorts (chain orderBy calls)\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.lastName)\n * .orderBy(({users}) => users.firstName)\n * ```\n */\n orderBy(\n callback: OrderByCallback<TContext>,\n options: OrderByDirection | OrderByOptions = `asc`\n ): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const result = callback(refProxy)\n\n const opts: CompareOptions =\n typeof options === `string`\n ? { direction: options, nulls: `first`, stringSort: `locale` }\n : {\n direction: options.direction ?? `asc`,\n nulls: options.nulls ?? `first`,\n stringSort: options.stringSort ?? `locale`,\n locale:\n options.stringSort === `locale` ? options.locale : undefined,\n localeOptions:\n options.stringSort === `locale`\n ? options.localeOptions\n : undefined,\n }\n\n const makeOrderByClause = (res: any) => {\n return {\n expression: toExpression(res),\n compareOptions: opts,\n }\n }\n\n // Create the new OrderBy structure with expression and direction\n const orderByClauses = Array.isArray(result)\n ? result.map((r) => makeOrderByClause(r))\n : [makeOrderByClause(result)]\n\n const existingOrderBy: OrderBy = this.query.orderBy || []\n\n return new BaseQueryBuilder({\n ...this.query,\n orderBy: [...existingOrderBy, ...orderByClauses],\n }) as any\n }\n\n /**\n * Group rows by one or more columns for aggregation\n *\n * @param callback - A function that receives table references and returns the field(s) to group by\n * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)\n *\n * @example\n * ```ts\n * // Group by a single column\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count()\n * }))\n *\n * // Group by multiple columns\n * query\n * .from({ sales: salesCollection })\n * .groupBy(({sales}) => [sales.region, sales.category])\n * .select(({sales, sum}) => ({\n * region: sales.region,\n * category: sales.category,\n * totalSales: sum(sales.amount)\n * }))\n * ```\n */\n groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefsForContext<TContext>\n const result = callback(refProxy)\n\n const newExpressions = Array.isArray(result)\n ? result.map((r) => toExpression(r))\n : [toExpression(result)]\n\n // Extend existing groupBy expressions (multiple groupBy calls should accumulate)\n const existingGroupBy = this.query.groupBy || []\n return new BaseQueryBuilder({\n ...this.query,\n groupBy: [...existingGroupBy, ...newExpressions],\n }) as any\n }\n\n /**\n * Limit the number of rows returned by the query\n * `orderBy` is required for `limit`\n *\n * @param count - Maximum number of rows to return\n * @returns A QueryBuilder with the limit applied\n *\n * @example\n * ```ts\n * // Get top 5 posts by likes\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.likes, 'desc')\n * .limit(5)\n * ```\n */\n limit(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n limit: count,\n }) as any\n }\n\n /**\n * Skip a number of rows before returning results\n * `orderBy` is required for `offset`\n *\n * @param count - Number of rows to skip\n * @returns A QueryBuilder with the offset applied\n *\n * @example\n * ```ts\n * // Get second page of results\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.createdAt, 'desc')\n * .offset(page * pageSize)\n * .limit(pageSize)\n * ```\n */\n offset(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n offset: count,\n }) as any\n }\n\n /**\n * Specify that the query should return distinct rows.\n * Deduplicates rows based on the selected columns.\n * @returns A QueryBuilder with distinct enabled\n *\n * @example\n * ```ts\n * // Get countries our users are from\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => users.country)\n * .distinct()\n * ```\n */\n distinct(): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n distinct: true,\n }) as any\n }\n\n // Helper methods\n private _getCurrentAliases(): Array<string> {\n const aliases: Array<string> = []\n\n // Add the from alias\n if (this.query.from) {\n aliases.push(this.query.from.alias)\n }\n\n // Add join aliases\n if (this.query.join) {\n for (const join of this.query.join) {\n aliases.push(join.from.alias)\n }\n }\n\n return aliases\n }\n\n /**\n * Functional variants of the query builder\n * These are imperative function that are called for ery row.\n * Warning: that these cannot be optimized by the query compiler, and may prevent\n * some type of optimizations being possible.\n * @example\n * ```ts\n * q.fn.select((row) => ({\n * name: row.user.name.toUpperCase(),\n * age: row.user.age + 1,\n * }))\n * ```\n */\n get fn() {\n const builder = this\n return {\n /**\n * Select fields using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns the selected value\n * @returns A QueryBuilder with functional selection applied\n *\n * @example\n * ```ts\n * // Functional select (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.select(row => ({\n * name: row.users.name.toUpperCase(),\n * age: row.users.age + 1,\n * }))\n * ```\n */\n select<TFuncSelectResult>(\n callback: (row: TContext[`schema`]) => TFuncSelectResult\n ): QueryBuilder<WithResult<TContext, TFuncSelectResult>> {\n return new BaseQueryBuilder({\n ...builder.query,\n select: undefined, // remove the select clause if it exists\n fnSelect: callback,\n })\n },\n /**\n * Filter rows using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns a boolean\n * @returns A QueryBuilder with functional filtering applied\n *\n * @example\n * ```ts\n * // Functional where (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.where(row => row.users.name.startsWith('A'))\n * ```\n */\n where(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnWhere: [\n ...(builder.query.fnWhere || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n /**\n * Filter grouped rows using a function that operates on each aggregated row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives an aggregated row and returns a boolean\n * @returns A QueryBuilder with functional having filter applied\n *\n * @example\n * ```ts\n * // Functional having (not optimized)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .fn.having(row => row.count > 5)\n * ```\n */\n having(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnHaving: [\n ...(builder.query.fnHaving || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n }\n }\n\n _getQuery(): QueryIR {\n if (!this.query.from) {\n throw new QueryMustHaveFromClauseError()\n }\n return this.query as QueryIR\n }\n}\n\n// Helper to ensure we have a BasicExpression/Aggregate for a value\nfunction toExpr(value: any): BasicExpression | Aggregate {\n if (value === undefined) return toExpression(null)\n if (\n value instanceof AggregateExpr ||\n value instanceof FuncExpr ||\n value instanceof PropRef ||\n value instanceof ValueExpr\n ) {\n return value as BasicExpression | Aggregate\n }\n return toExpression(value)\n}\n\nfunction isPlainObject(value: any): value is Record<string, any> {\n return (\n value !== null &&\n typeof value === `object` &&\n !isExpressionLike(value) &&\n !value.__refProxy\n )\n}\n\nfunction buildNestedSelect(obj: any): any {\n if (!isPlainObject(obj)) return toExpr(obj)\n const out: Record<string, any> = {}\n for (const [k, v] of Object.entries(obj)) {\n if (typeof k === `string` && k.startsWith(`__SPREAD_SENTINEL__`)) {\n // Preserve sentinel key and its value (value is unimportant at compile time)\n out[k] = v\n continue\n }\n out[k] = buildNestedSelect(v)\n }\n return out\n}\n\n// Internal function to build a query from a callback\n// used by liveQueryCollectionOptions.query\nexport function buildQuery<TContext extends Context>(\n fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>\n): QueryIR {\n const result = fn(new BaseQueryBuilder())\n return getQueryIR(result)\n}\n\n// Internal function to get the QueryIR from a builder\nexport function getQueryIR(\n builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder\n): QueryIR {\n return (builder as unknown as BaseQueryBuilder)._getQuery()\n}\n\n// Type-only exports for the query builder\nexport type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>\n\nexport type InitialQueryBuilderConstructor = new () => InitialQueryBuilder\n\nexport type QueryBuilder<TContext extends Context> = Omit<\n BaseQueryBuilder<TContext>,\n `from` | `_getQuery`\n>\n\n// Main query builder class alias with the constructor type modified to hide all\n// but the from method on the initial instance\nexport const Query: InitialQueryBuilderConstructor = BaseQueryBuilder\n\n// Helper type to extract context from a QueryBuilder\nexport type ExtractContext<T> =\n T extends BaseQueryBuilder<infer TContext>\n ? TContext\n : T extends QueryBuilder<infer TContext>\n ? TContext\n : never\n\n// Export the types from types.ts for convenience\nexport type { Context, Source, GetResult, RefLeaf as Ref } from \"./types.js\"\n"],"names":["OnlyOneSourceAllowedError","CollectionImpl","CollectionRef","SubQueryMustHaveFromClauseError","QueryRef","InvalidSourceError","refProxy","createRefProxy","JoinConditionMustBeEqualityError","toExpression","QueryMustHaveFromClauseError","AggregateExpr","FuncExpr","PropRef","ValueExpr","isExpressionLike"],"mappings":";;;;;;AA6CO,MAAM,iBAAqD;AAAA,EAGhE,YAAY,QAA0B,IAAI;AAF1C,SAAiB,QAA0B,CAAA;AAGzC,SAAK,QAAQ,EAAE,GAAG,MAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBACN,QACA,SACoC;AACpC,QAAI,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG;AACpC,YAAM,IAAIA,OAAAA,0BAA0B,OAAO;AAAA,IAC7C;AAEA,UAAM,QAAQ,OAAO,KAAK,MAAM,EAAE,CAAC;AACnC,UAAM,cAAc,OAAO,KAAK;AAEhC,QAAI;AAEJ,QAAI,uBAAuBC,MAAAA,gBAAgB;AACzC,YAAM,IAAIC,GAAAA,cAAc,aAAa,KAAK;AAAA,IAC5C,WAAW,uBAAuB,kBAAkB;AAClD,YAAM,WAAW,YAAY,UAAA;AAC7B,UAAI,CAAE,SAA8B,MAAM;AACxC,cAAM,IAAIC,OAAAA,gCAAgC,OAAO;AAAA,MACnD;AACA,YAAM,IAAIC,GAAAA,SAAS,UAAU,KAAK;AAAA,IACpC,OAAO;AACL,YAAM,IAAIC,OAAAA,mBAAmB,KAAK;AAAA,IACpC;AAEA,WAAO,CAAC,OAAO,GAAG;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,KACE,QAMC;AACD,UAAM,CAAA,EAAG,IAAI,IAAI,KAAK,oBAAoB,QAAQ,aAAa;AAE/D,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR;AAAA,IAAA,CACD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,KAIE,QACA,YAGA,OAAkB,QAGlB;AACA,UAAM,CAAC,OAAO,IAAI,IAAI,KAAK,oBAAoB,QAAQ,aAAa;AAGpE,UAAM,iBAAiB,KAAK,mBAAA;AAC5B,UAAM,aAAa,CAAC,GAAG,gBAAgB,KAAK;AAC5C,UAAMC,aAAWC,SAAAA,eAAe,UAAU;AAK1C,UAAM,eAAe,WAAWD,UAAQ;AAIxC,QAAI;AACJ,QAAI;AAEJ,QACE,aAAa,SAAS,UACtB,aAAa,SAAS,QACtB,aAAa,KAAK,WAAW,GAC7B;AACA,aAAO,aAAa,KAAK,CAAC;AAC1B,cAAQ,aAAa,KAAK,CAAC;AAAA,IAC7B,OAAO;AACL,YAAM,IAAIE,OAAAA,iCAAA;AAAA,IACZ;AAEA,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,UAAM,gBAAgB,KAAK,MAAM,QAAQ,CAAA;AAEzC,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,MAAM,CAAC,GAAG,eAAe,UAAU;AAAA,IAAA,CACpC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,SACE,QACA,YAKA;AACA,WAAO,KAAK,KAAK,QAAQ,YAAY,MAAM;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,UACE,QACA,YAKA;AACA,WAAO,KAAK,KAAK,QAAQ,YAAY,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,UACE,QACA,YAKA;AACA,WAAO,KAAK,KAAK,QAAQ,YAAY,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,SACE,QACA,YAKA;AACA,WAAO,KAAK,KAAK,QAAQ,YAAY,MAAM;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,UAA2D;AAC/D,UAAM,UAAU,KAAK,mBAAA;AACrB,UAAMF,aAAWC,SAAAA,eAAe,OAAO;AACvC,UAAM,aAAa,SAASD,UAAQ;AAEpC,UAAM,gBAAgB,KAAK,MAAM,SAAS,CAAA;AAE1C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,OAAO,CAAC,GAAG,eAAe,UAAU;AAAA,IAAA,CACrC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,OAAO,UAA2D;AAChE,UAAM,UAAU,KAAK,mBAAA;AACrB,UAAMA,aAAWC,SAAAA,eAAe,OAAO;AACvC,UAAM,aAAa,SAASD,UAAQ;AAEpC,UAAM,iBAAiB,KAAK,MAAM,UAAU,CAAA;AAE5C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,QAAQ,CAAC,GAAG,gBAAgB,UAAU;AAAA,IAAA,CACvC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,OACE,UACyE;AACzE,UAAM,UAAU,KAAK,mBAAA;AACrB,UAAMA,aAAWC,SAAAA,eAAe,OAAO;AACvC,UAAM,eAAe,SAASD,UAAQ;AACtC,UAAM,SAAS,kBAAkB,YAAY;AAE7C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR;AAAA,MACA,UAAU;AAAA;AAAA,IAAA,CACX;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,QACE,UACA,UAA6C,OACrB;AACxB,UAAM,UAAU,KAAK,mBAAA;AACrB,UAAMA,aAAWC,SAAAA,eAAe,OAAO;AACvC,UAAM,SAAS,SAASD,UAAQ;AAEhC,UAAM,OACJ,OAAO,YAAY,WACf,EAAE,WAAW,SAAS,OAAO,SAAS,YAAY,SAAA,IAClD;AAAA,MACE,WAAW,QAAQ,aAAa;AAAA,MAChC,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,QAAQ,cAAc;AAAA,MAClC,QACE,QAAQ,eAAe,WAAW,QAAQ,SAAS;AAAA,MACrD,eACE,QAAQ,eAAe,WACnB,QAAQ,gBACR;AAAA,IAAA;AAGd,UAAM,oBAAoB,CAAC,QAAa;AACtC,aAAO;AAAA,QACL,YAAYG,SAAAA,aAAa,GAAG;AAAA,QAC5B,gBAAgB;AAAA,MAAA;AAAA,IAEpB;AAGA,UAAM,iBAAiB,MAAM,QAAQ,MAAM,IACvC,OAAO,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC,IACtC,CAAC,kBAAkB,MAAM,CAAC;AAE9B,UAAM,kBAA2B,KAAK,MAAM,WAAW,CAAA;AAEvD,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,SAAS,CAAC,GAAG,iBAAiB,GAAG,cAAc;AAAA,IAAA,CAChD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,QAAQ,UAA6D;AACnE,UAAM,UAAU,KAAK,mBAAA;AACrB,UAAMH,aAAWC,SAAAA,eAAe,OAAO;AACvC,UAAM,SAAS,SAASD,UAAQ;AAEhC,UAAM,iBAAiB,MAAM,QAAQ,MAAM,IACvC,OAAO,IAAI,CAAC,MAAMG,SAAAA,aAAa,CAAC,CAAC,IACjC,CAACA,SAAAA,aAAa,MAAM,CAAC;AAGzB,UAAM,kBAAkB,KAAK,MAAM,WAAW,CAAA;AAC9C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,SAAS,CAAC,GAAG,iBAAiB,GAAG,cAAc;AAAA,IAAA,CAChD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,OAAuC;AAC3C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,OAAO,OAAuC;AAC5C,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,QAAQ;AAAA,IAAA,CACT;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,WAAmC;AACjC,WAAO,IAAI,iBAAiB;AAAA,MAC1B,GAAG,KAAK;AAAA,MACR,UAAU;AAAA,IAAA,CACX;AAAA,EACH;AAAA;AAAA,EAGQ,qBAAoC;AAC1C,UAAM,UAAyB,CAAA;AAG/B,QAAI,KAAK,MAAM,MAAM;AACnB,cAAQ,KAAK,KAAK,MAAM,KAAK,KAAK;AAAA,IACpC;AAGA,QAAI,KAAK,MAAM,MAAM;AACnB,iBAAW,QAAQ,KAAK,MAAM,MAAM;AAClC,gBAAQ,KAAK,KAAK,KAAK,KAAK;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,IAAI,KAAK;AACP,UAAM,UAAU;AAChB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAmBL,OACE,UACuD;AACvD,eAAO,IAAI,iBAAiB;AAAA,UAC1B,GAAG,QAAQ;AAAA,UACX,QAAQ;AAAA;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,MACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBA,MACE,UACwB;AACxB,eAAO,IAAI,iBAAiB;AAAA,UAC1B,GAAG,QAAQ;AAAA,UACX,SAAS;AAAA,YACP,GAAI,QAAQ,MAAM,WAAW,CAAA;AAAA,YAC7B;AAAA,UAAA;AAAA,QACF,CACD;AAAA,MACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiBA,OACE,UACwB;AACxB,eAAO,IAAI,iBAAiB;AAAA,UAC1B,GAAG,QAAQ;AAAA,UACX,UAAU;AAAA,YACR,GAAI,QAAQ,MAAM,YAAY,CAAA;AAAA,YAC9B;AAAA,UAAA;AAAA,QACF,CACD;AAAA,MACH;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,YAAqB;AACnB,QAAI,CAAC,KAAK,MAAM,MAAM;AACpB,YAAM,IAAIC,OAAAA,6BAAA;AAAA,IACZ;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAGA,SAAS,OAAO,OAAyC;AACvD,MAAI,UAAU,OAAW,QAAOD,SAAAA,aAAa,IAAI;AACjD,MACE,iBAAiBE,GAAAA,aACjB,iBAAiBC,GAAAA,QACjB,iBAAiBC,GAAAA,WACjB,iBAAiBC,UACjB;AACA,WAAO;AAAA,EACT;AACA,SAAOL,SAAAA,aAAa,KAAK;AAC3B;AAEA,SAAS,cAAc,OAA0C;AAC/D,SACE,UAAU,QACV,OAAO,UAAU,YACjB,CAACM,oBAAiB,KAAK,KACvB,CAAC,MAAM;AAEX;AAEA,SAAS,kBAAkB,KAAe;AACxC,MAAI,CAAC,cAAc,GAAG,EAAG,QAAO,OAAO,GAAG;AAC1C,QAAM,MAA2B,CAAA;AACjC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACxC,QAAI,OAAO,MAAM,YAAY,EAAE,WAAW,qBAAqB,GAAG;AAEhE,UAAI,CAAC,IAAI;AACT;AAAA,IACF;AACA,QAAI,CAAC,IAAI,kBAAkB,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAIO,SAAS,WACd,IACS;AACT,QAAM,SAAS,GAAG,IAAI,kBAAkB;AACxC,SAAO,WAAW,MAAM;AAC1B;AAGO,SAAS,WACd,SACS;AACT,SAAQ,QAAwC,UAAA;AAClD;AAcO,MAAM,QAAwC;;;;;"}
|
|
@@ -9,7 +9,7 @@ const joins = require("./joins.cjs");
|
|
|
9
9
|
const groupBy = require("./group-by.cjs");
|
|
10
10
|
const orderBy = require("./order-by.cjs");
|
|
11
11
|
const select = require("./select.cjs");
|
|
12
|
-
function compileQuery(rawQuery, inputs, collections, callbacks, lazyCollections, optimizableOrderByCollections, cache = /* @__PURE__ */ new WeakMap(), queryMapping = /* @__PURE__ */ new WeakMap()) {
|
|
12
|
+
function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, lazyCollections, optimizableOrderByCollections, cache = /* @__PURE__ */ new WeakMap(), queryMapping = /* @__PURE__ */ new WeakMap()) {
|
|
13
13
|
const cachedResult = cache.get(rawQuery);
|
|
14
14
|
if (cachedResult) {
|
|
15
15
|
return cachedResult;
|
|
@@ -27,6 +27,7 @@ function compileQuery(rawQuery, inputs, collections, callbacks, lazyCollections,
|
|
|
27
27
|
query.from,
|
|
28
28
|
allInputs,
|
|
29
29
|
collections,
|
|
30
|
+
subscriptions,
|
|
30
31
|
callbacks,
|
|
31
32
|
lazyCollections,
|
|
32
33
|
optimizableOrderByCollections,
|
|
@@ -51,6 +52,7 @@ function compileQuery(rawQuery, inputs, collections, callbacks, lazyCollections,
|
|
|
51
52
|
cache,
|
|
52
53
|
queryMapping,
|
|
53
54
|
collections,
|
|
55
|
+
subscriptions,
|
|
54
56
|
callbacks,
|
|
55
57
|
lazyCollections,
|
|
56
58
|
optimizableOrderByCollections,
|
|
@@ -195,7 +197,7 @@ function compileQuery(rawQuery, inputs, collections, callbacks, lazyCollections,
|
|
|
195
197
|
cache.set(rawQuery, compilationResult);
|
|
196
198
|
return compilationResult;
|
|
197
199
|
}
|
|
198
|
-
function processFrom(from, allInputs, collections, callbacks, lazyCollections, optimizableOrderByCollections, cache, queryMapping) {
|
|
200
|
+
function processFrom(from, allInputs, collections, subscriptions, callbacks, lazyCollections, optimizableOrderByCollections, cache, queryMapping) {
|
|
199
201
|
switch (from.type) {
|
|
200
202
|
case `collectionRef`: {
|
|
201
203
|
const input = allInputs[from.collection.id];
|
|
@@ -210,6 +212,7 @@ function processFrom(from, allInputs, collections, callbacks, lazyCollections, o
|
|
|
210
212
|
originalQuery,
|
|
211
213
|
allInputs,
|
|
212
214
|
collections,
|
|
215
|
+
subscriptions,
|
|
213
216
|
callbacks,
|
|
214
217
|
lazyCollections,
|
|
215
218
|
optimizableOrderByCollections,
|