@b2bc-devkit/sheetorm 0.1.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/npm/SheetOrm.d.ts +41 -0
- package/dist/npm/SheetOrm.d.ts.map +1 -0
- package/dist/npm/SheetOrm.js +41 -0
- package/dist/npm/SheetOrm.js.map +1 -0
- package/dist/npm/core/Decorators.d.ts +145 -0
- package/dist/npm/core/Decorators.d.ts.map +1 -0
- package/dist/npm/core/Decorators.js +226 -0
- package/dist/npm/core/Decorators.js.map +1 -0
- package/dist/npm/core/Record.d.ts +167 -0
- package/dist/npm/core/Record.d.ts.map +1 -0
- package/dist/npm/core/Record.js +298 -0
- package/dist/npm/core/Record.js.map +1 -0
- package/dist/npm/core/RecordConstructor.d.ts +20 -0
- package/dist/npm/core/RecordConstructor.d.ts.map +1 -0
- package/dist/npm/core/RecordConstructor.js +2 -0
- package/dist/npm/core/RecordConstructor.js.map +1 -0
- package/dist/npm/core/RecordStatic.d.ts +19 -0
- package/dist/npm/core/RecordStatic.d.ts.map +1 -0
- package/dist/npm/core/RecordStatic.js +2 -0
- package/dist/npm/core/RecordStatic.js.map +1 -0
- package/dist/npm/core/Registry.d.ts +100 -0
- package/dist/npm/core/Registry.d.ts.map +1 -0
- package/dist/npm/core/Registry.js +206 -0
- package/dist/npm/core/Registry.js.map +1 -0
- package/dist/npm/core/SheetRepository.d.ts +331 -0
- package/dist/npm/core/SheetRepository.d.ts.map +1 -0
- package/dist/npm/core/SheetRepository.js +1041 -0
- package/dist/npm/core/SheetRepository.js.map +1 -0
- package/dist/npm/core/cache/MemoryCache.d.ts +67 -0
- package/dist/npm/core/cache/MemoryCache.d.ts.map +1 -0
- package/dist/npm/core/cache/MemoryCache.js +112 -0
- package/dist/npm/core/cache/MemoryCache.js.map +1 -0
- package/dist/npm/core/types/Entity.d.ts +21 -0
- package/dist/npm/core/types/Entity.d.ts.map +1 -0
- package/dist/npm/core/types/Entity.js +2 -0
- package/dist/npm/core/types/Entity.js.map +1 -0
- package/dist/npm/core/types/FieldDefinition.d.ts +21 -0
- package/dist/npm/core/types/FieldDefinition.d.ts.map +1 -0
- package/dist/npm/core/types/FieldDefinition.js +2 -0
- package/dist/npm/core/types/FieldDefinition.js.map +1 -0
- package/dist/npm/core/types/FieldType.d.ts +12 -0
- package/dist/npm/core/types/FieldType.d.ts.map +1 -0
- package/dist/npm/core/types/FieldType.js +2 -0
- package/dist/npm/core/types/FieldType.js.map +1 -0
- package/dist/npm/core/types/Filter.d.ts +16 -0
- package/dist/npm/core/types/Filter.d.ts.map +1 -0
- package/dist/npm/core/types/Filter.js +2 -0
- package/dist/npm/core/types/Filter.js.map +1 -0
- package/dist/npm/core/types/FilterOperator.d.ts +16 -0
- package/dist/npm/core/types/FilterOperator.d.ts.map +1 -0
- package/dist/npm/core/types/FilterOperator.js +2 -0
- package/dist/npm/core/types/FilterOperator.js.map +1 -0
- package/dist/npm/core/types/GroupResult.d.ts +14 -0
- package/dist/npm/core/types/GroupResult.d.ts.map +1 -0
- package/dist/npm/core/types/GroupResult.js +2 -0
- package/dist/npm/core/types/GroupResult.js.map +1 -0
- package/dist/npm/core/types/ICacheProvider.d.ts +20 -0
- package/dist/npm/core/types/ICacheProvider.d.ts.map +1 -0
- package/dist/npm/core/types/ICacheProvider.js +2 -0
- package/dist/npm/core/types/ICacheProvider.js.map +1 -0
- package/dist/npm/core/types/ISheetAdapter.d.ts +54 -0
- package/dist/npm/core/types/ISheetAdapter.d.ts.map +1 -0
- package/dist/npm/core/types/ISheetAdapter.js +2 -0
- package/dist/npm/core/types/ISheetAdapter.js.map +1 -0
- package/dist/npm/core/types/ISpreadsheetAdapter.d.ts +34 -0
- package/dist/npm/core/types/ISpreadsheetAdapter.d.ts.map +1 -0
- package/dist/npm/core/types/ISpreadsheetAdapter.js +2 -0
- package/dist/npm/core/types/ISpreadsheetAdapter.js.map +1 -0
- package/dist/npm/core/types/IndexDefinition.d.ts +16 -0
- package/dist/npm/core/types/IndexDefinition.d.ts.map +1 -0
- package/dist/npm/core/types/IndexDefinition.js +2 -0
- package/dist/npm/core/types/IndexDefinition.js.map +1 -0
- package/dist/npm/core/types/LifecycleHooks.d.ts +32 -0
- package/dist/npm/core/types/LifecycleHooks.d.ts.map +1 -0
- package/dist/npm/core/types/LifecycleHooks.js +2 -0
- package/dist/npm/core/types/LifecycleHooks.js.map +1 -0
- package/dist/npm/core/types/PaginatedResult.d.ts +18 -0
- package/dist/npm/core/types/PaginatedResult.d.ts.map +1 -0
- package/dist/npm/core/types/PaginatedResult.js +2 -0
- package/dist/npm/core/types/PaginatedResult.js.map +1 -0
- package/dist/npm/core/types/QueryOptions.d.ts +21 -0
- package/dist/npm/core/types/QueryOptions.d.ts.map +1 -0
- package/dist/npm/core/types/QueryOptions.js +2 -0
- package/dist/npm/core/types/QueryOptions.js.map +1 -0
- package/dist/npm/core/types/SortClause.d.ts +10 -0
- package/dist/npm/core/types/SortClause.d.ts.map +1 -0
- package/dist/npm/core/types/SortClause.js +2 -0
- package/dist/npm/core/types/SortClause.js.map +1 -0
- package/dist/npm/core/types/SystemColumns.d.ts +16 -0
- package/dist/npm/core/types/SystemColumns.d.ts.map +1 -0
- package/dist/npm/core/types/SystemColumns.js +16 -0
- package/dist/npm/core/types/SystemColumns.js.map +1 -0
- package/dist/npm/core/types/TableSchema.d.ts +24 -0
- package/dist/npm/core/types/TableSchema.d.ts.map +1 -0
- package/dist/npm/core/types/TableSchema.js +2 -0
- package/dist/npm/core/types/TableSchema.js.map +1 -0
- package/dist/npm/index/IndexMeta.d.ts +16 -0
- package/dist/npm/index/IndexMeta.d.ts.map +1 -0
- package/dist/npm/index/IndexMeta.js +2 -0
- package/dist/npm/index/IndexMeta.js.map +1 -0
- package/dist/npm/index/IndexStore.d.ts +399 -0
- package/dist/npm/index/IndexStore.d.ts.map +1 -0
- package/dist/npm/index/IndexStore.js +1104 -0
- package/dist/npm/index/IndexStore.js.map +1 -0
- package/dist/npm/index.d.ts +115 -0
- package/dist/npm/index.d.ts.map +1 -0
- package/dist/npm/index.js +367 -0
- package/dist/npm/index.js.map +1 -0
- package/dist/npm/query/Query.d.ts +176 -0
- package/dist/npm/query/Query.d.ts.map +1 -0
- package/dist/npm/query/Query.js +270 -0
- package/dist/npm/query/Query.js.map +1 -0
- package/dist/npm/query/QueryEngine.d.ts +140 -0
- package/dist/npm/query/QueryEngine.d.ts.map +1 -0
- package/dist/npm/query/QueryEngine.js +453 -0
- package/dist/npm/query/QueryEngine.js.map +1 -0
- package/dist/npm/storage/GoogleSheetAdapter.d.ts +120 -0
- package/dist/npm/storage/GoogleSheetAdapter.d.ts.map +1 -0
- package/dist/npm/storage/GoogleSheetAdapter.js +239 -0
- package/dist/npm/storage/GoogleSheetAdapter.js.map +1 -0
- package/dist/npm/storage/GoogleSpreadsheetAdapter.d.ts +55 -0
- package/dist/npm/storage/GoogleSpreadsheetAdapter.d.ts.map +1 -0
- package/dist/npm/storage/GoogleSpreadsheetAdapter.js +99 -0
- package/dist/npm/storage/GoogleSpreadsheetAdapter.js.map +1 -0
- package/dist/npm/testing/ParityCatalog.d.ts +38 -0
- package/dist/npm/testing/ParityCatalog.d.ts.map +1 -0
- package/dist/npm/testing/ParityCatalog.js +400 -0
- package/dist/npm/testing/ParityCatalog.js.map +1 -0
- package/dist/npm/testing/RuntimeBenchmark.d.ts +37 -0
- package/dist/npm/testing/RuntimeBenchmark.d.ts.map +1 -0
- package/dist/npm/testing/RuntimeBenchmark.js +449 -0
- package/dist/npm/testing/RuntimeBenchmark.js.map +1 -0
- package/dist/npm/testing/RuntimeParity.d.ts +62 -0
- package/dist/npm/testing/RuntimeParity.d.ts.map +1 -0
- package/dist/npm/testing/RuntimeParity.js +4030 -0
- package/dist/npm/testing/RuntimeParity.js.map +1 -0
- package/dist/npm/utils/Serialization.d.ts +111 -0
- package/dist/npm/utils/Serialization.d.ts.map +1 -0
- package/dist/npm/utils/Serialization.js +281 -0
- package/dist/npm/utils/Serialization.js.map +1 -0
- package/dist/npm/utils/SheetOrmLogger.d.ts +31 -0
- package/dist/npm/utils/SheetOrmLogger.d.ts.map +1 -0
- package/dist/npm/utils/SheetOrmLogger.js +42 -0
- package/dist/npm/utils/SheetOrmLogger.js.map +1 -0
- package/dist/npm/utils/Uuid.d.ts +29 -0
- package/dist/npm/utils/Uuid.d.ts.map +1 -0
- package/dist/npm/utils/Uuid.js +86 -0
- package/dist/npm/utils/Uuid.js.map +1 -0
- package/license.md +18 -0
- package/package.json +77 -0
- package/readme.md +672 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluent query builder for composing and executing queries against SheetORM data.
|
|
3
|
+
*
|
|
4
|
+
* Provides a chainable API inspired by common ORM patterns:
|
|
5
|
+
* ```ts
|
|
6
|
+
* const cars = Query.from(Car)
|
|
7
|
+
* .where("make", "=", "Toyota")
|
|
8
|
+
* .or("make", "=", "Honda")
|
|
9
|
+
* .orderBy("year", "desc")
|
|
10
|
+
* .limit(10)
|
|
11
|
+
* .execute();
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Filter logic:
|
|
15
|
+
* - `.where()` / `.and()` add filters to the current AND-group.
|
|
16
|
+
* - `.or()` creates a new AND-group (groups are combined with OR logic).
|
|
17
|
+
*
|
|
18
|
+
* Execution methods:
|
|
19
|
+
* - `execute()` — return all matching entities (with sort + pagination).
|
|
20
|
+
* - `first()` — return the first match (or null).
|
|
21
|
+
* - `select()` — paginated result with total count.
|
|
22
|
+
* - `count()` — return the number of matches.
|
|
23
|
+
* - `groupBy()` — bucket results by a field value.
|
|
24
|
+
* - `build()` — export the builder state as a QueryOptions object.
|
|
25
|
+
*
|
|
26
|
+
* @module Query
|
|
27
|
+
*/
|
|
28
|
+
import type { Entity } from "../core/types/Entity.js";
|
|
29
|
+
import type { FilterOperator } from "../core/types/FilterOperator.js";
|
|
30
|
+
import type { QueryOptions } from "../core/types/QueryOptions.js";
|
|
31
|
+
import type { PaginatedResult } from "../core/types/PaginatedResult.js";
|
|
32
|
+
import type { GroupResult } from "../core/types/GroupResult.js";
|
|
33
|
+
/**
|
|
34
|
+
* Callback registered by Record to resolve a class/name → data-provider function.
|
|
35
|
+
* This indirection allows Query to remain decoupled from Record and SheetRepository.
|
|
36
|
+
*/
|
|
37
|
+
type FromResolver = (classOrName: string | {
|
|
38
|
+
new (): Entity;
|
|
39
|
+
tableName: string;
|
|
40
|
+
}) => () => Entity[];
|
|
41
|
+
/**
|
|
42
|
+
* Fluent query builder that composes filter groups, sorting, and pagination,
|
|
43
|
+
* then delegates execution to QueryEngine.
|
|
44
|
+
*
|
|
45
|
+
* @typeParam T - Entity type returned by the query.
|
|
46
|
+
*/
|
|
47
|
+
export declare class Query<T extends Entity> {
|
|
48
|
+
/**
|
|
49
|
+
* Array of filter groups. Each inner array is AND-connected;
|
|
50
|
+
* groups are OR-connected. Starts with one empty group.
|
|
51
|
+
*/
|
|
52
|
+
private filterGroups;
|
|
53
|
+
/** Ordered list of sort clauses to apply after filtering. */
|
|
54
|
+
private sorts;
|
|
55
|
+
/** Maximum number of results (undefined = no limit). */
|
|
56
|
+
private _limit?;
|
|
57
|
+
/** Number of results to skip (undefined = 0). */
|
|
58
|
+
private _offset?;
|
|
59
|
+
/** Callback that returns the full entity dataset (lazy-loaded). */
|
|
60
|
+
private dataProvider;
|
|
61
|
+
/**
|
|
62
|
+
* Module-level resolver injected by Record at import time.
|
|
63
|
+
* Converts a class constructor or table name to a data-provider function.
|
|
64
|
+
*/
|
|
65
|
+
private static _fromResolverFn;
|
|
66
|
+
/**
|
|
67
|
+
* Register the from-resolver. Called once by Record when it wires up
|
|
68
|
+
* the Query integration at module load time.
|
|
69
|
+
*/
|
|
70
|
+
static _setFromResolver(resolver: FromResolver): void;
|
|
71
|
+
/**
|
|
72
|
+
* Create a new Query from a Record subclass.
|
|
73
|
+
* @typeParam U - Concrete Record type.
|
|
74
|
+
*/
|
|
75
|
+
static from<U extends Entity>(ctor: {
|
|
76
|
+
new (): U;
|
|
77
|
+
tableName: string;
|
|
78
|
+
}): Query<U>;
|
|
79
|
+
/**
|
|
80
|
+
* Create a new Query from a table name string.
|
|
81
|
+
*/
|
|
82
|
+
static from(name: string): Query<Entity>;
|
|
83
|
+
/**
|
|
84
|
+
* Construct a Query with the given data provider function.
|
|
85
|
+
* @param dataProvider - Callback returning the full set of entities to query.
|
|
86
|
+
*/
|
|
87
|
+
constructor(dataProvider: () => T[]);
|
|
88
|
+
/**
|
|
89
|
+
* Add a filter to the current AND-group.
|
|
90
|
+
*
|
|
91
|
+
* @param field - Entity field name (supports dot-paths for nested objects).
|
|
92
|
+
* @param operator - Comparison operator (=, !=, <, >, <=, >=, contains, startsWith, in, search).
|
|
93
|
+
* @param value - Value to compare against.
|
|
94
|
+
* @returns this (for chaining).
|
|
95
|
+
*/
|
|
96
|
+
where(field: string, operator: FilterOperator, value: unknown): Query<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Alias for `where()` — adds another AND condition to the current group.
|
|
99
|
+
*/
|
|
100
|
+
and(field: string, operator: FilterOperator, value: unknown): Query<T>;
|
|
101
|
+
/**
|
|
102
|
+
* Start a new OR-group with the given filter condition.
|
|
103
|
+
* Previous filters remain in their own AND-group.
|
|
104
|
+
*/
|
|
105
|
+
or(field: string, operator: FilterOperator, value: unknown): Query<T>;
|
|
106
|
+
/**
|
|
107
|
+
* Add a sort clause (multiple orderBy calls are applied in order).
|
|
108
|
+
*
|
|
109
|
+
* @param field - Entity field name to sort by.
|
|
110
|
+
* @param direction - "asc" (default) or "desc".
|
|
111
|
+
*/
|
|
112
|
+
orderBy(field: string, direction?: "asc" | "desc"): Query<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Set the maximum number of results to return.
|
|
115
|
+
* @throws If count is negative or non-finite.
|
|
116
|
+
*/
|
|
117
|
+
limit(count: number): Query<T>;
|
|
118
|
+
/**
|
|
119
|
+
* Set the number of results to skip before returning.
|
|
120
|
+
* @throws If count is negative or non-finite.
|
|
121
|
+
*/
|
|
122
|
+
offset(count: number): Query<T>;
|
|
123
|
+
/** True if any filter group contains at least one filter. */
|
|
124
|
+
private get hasFilters();
|
|
125
|
+
/** True if there are multiple non-empty filter groups (OR query). */
|
|
126
|
+
private get isOrQuery();
|
|
127
|
+
/** Return the first non-empty filter group (for simple AND queries). */
|
|
128
|
+
private get flatFilters();
|
|
129
|
+
/**
|
|
130
|
+
* Apply all filter groups to the entity dataset.
|
|
131
|
+
* Delegates to `QueryEngine.filterEntities` (AND) or
|
|
132
|
+
* `QueryEngine.filterEntitiesOr` (multiple OR groups).
|
|
133
|
+
*/
|
|
134
|
+
private applyFilters;
|
|
135
|
+
/**
|
|
136
|
+
* Build a declarative QueryOptions object from the current builder state.
|
|
137
|
+
* Useful for passing to SheetRepository.find() or for serialisation.
|
|
138
|
+
*/
|
|
139
|
+
build(): QueryOptions;
|
|
140
|
+
/**
|
|
141
|
+
* Execute the query and return all matching entities.
|
|
142
|
+
*
|
|
143
|
+
* Pipeline: load data → filter → sort → offset/limit.
|
|
144
|
+
*/
|
|
145
|
+
execute(): T[];
|
|
146
|
+
/**
|
|
147
|
+
* Execute the query and return the first matching entity, or null.
|
|
148
|
+
*
|
|
149
|
+
* More efficient than `execute()[0]` when limit is already set.
|
|
150
|
+
*/
|
|
151
|
+
first(): T | null;
|
|
152
|
+
/**
|
|
153
|
+
* Execute with explicit pagination, returning a PaginatedResult.
|
|
154
|
+
*
|
|
155
|
+
* @param offset - 0-based number of items to skip.
|
|
156
|
+
* @param limit - Maximum items per page.
|
|
157
|
+
* @returns Object with items, total, offset, limit, hasNext.
|
|
158
|
+
*/
|
|
159
|
+
select(offset: number, limit: number): PaginatedResult<T>;
|
|
160
|
+
/**
|
|
161
|
+
* Count the number of entities matching the current filters.
|
|
162
|
+
* No sorting or pagination is applied.
|
|
163
|
+
*/
|
|
164
|
+
count(): number;
|
|
165
|
+
/**
|
|
166
|
+
* Group matching entities by a field value.
|
|
167
|
+
*
|
|
168
|
+
* Filters and sorts are applied before grouping.
|
|
169
|
+
*
|
|
170
|
+
* @param field - Entity field name to group by.
|
|
171
|
+
* @returns Array of GroupResult objects (one per distinct field value).
|
|
172
|
+
*/
|
|
173
|
+
groupBy(field: string): GroupResult<T>[];
|
|
174
|
+
}
|
|
175
|
+
export {};
|
|
176
|
+
//# sourceMappingURL=Query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../../src/query/Query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluent query builder for composing and executing queries against SheetORM data.
|
|
3
|
+
*
|
|
4
|
+
* Provides a chainable API inspired by common ORM patterns:
|
|
5
|
+
* ```ts
|
|
6
|
+
* const cars = Query.from(Car)
|
|
7
|
+
* .where("make", "=", "Toyota")
|
|
8
|
+
* .or("make", "=", "Honda")
|
|
9
|
+
* .orderBy("year", "desc")
|
|
10
|
+
* .limit(10)
|
|
11
|
+
* .execute();
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Filter logic:
|
|
15
|
+
* - `.where()` / `.and()` add filters to the current AND-group.
|
|
16
|
+
* - `.or()` creates a new AND-group (groups are combined with OR logic).
|
|
17
|
+
*
|
|
18
|
+
* Execution methods:
|
|
19
|
+
* - `execute()` — return all matching entities (with sort + pagination).
|
|
20
|
+
* - `first()` — return the first match (or null).
|
|
21
|
+
* - `select()` — paginated result with total count.
|
|
22
|
+
* - `count()` — return the number of matches.
|
|
23
|
+
* - `groupBy()` — bucket results by a field value.
|
|
24
|
+
* - `build()` — export the builder state as a QueryOptions object.
|
|
25
|
+
*
|
|
26
|
+
* @module Query
|
|
27
|
+
*/
|
|
28
|
+
import { QueryEngine } from "./QueryEngine.js";
|
|
29
|
+
import { SheetOrmLogger } from "../utils/SheetOrmLogger.js";
|
|
30
|
+
/**
|
|
31
|
+
* Fluent query builder that composes filter groups, sorting, and pagination,
|
|
32
|
+
* then delegates execution to QueryEngine.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam T - Entity type returned by the query.
|
|
35
|
+
*/
|
|
36
|
+
export class Query {
|
|
37
|
+
/**
|
|
38
|
+
* Register the from-resolver. Called once by Record when it wires up
|
|
39
|
+
* the Query integration at module load time.
|
|
40
|
+
*/
|
|
41
|
+
static _setFromResolver(resolver) {
|
|
42
|
+
Query._fromResolverFn = resolver;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Implementation: resolve the data provider and return a fresh Query.
|
|
46
|
+
* @throws If `_fromResolverFn` has not been set (Record not imported).
|
|
47
|
+
*/
|
|
48
|
+
static from(classOrName) {
|
|
49
|
+
if (!Query._fromResolverFn) {
|
|
50
|
+
throw new Error("Query.from() is not available. Import Record from SheetORM to enable it.");
|
|
51
|
+
}
|
|
52
|
+
const provider = Query._fromResolverFn(classOrName);
|
|
53
|
+
return new Query(provider);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Construct a Query with the given data provider function.
|
|
57
|
+
* @param dataProvider - Callback returning the full set of entities to query.
|
|
58
|
+
*/
|
|
59
|
+
constructor(dataProvider) {
|
|
60
|
+
/**
|
|
61
|
+
* Array of filter groups. Each inner array is AND-connected;
|
|
62
|
+
* groups are OR-connected. Starts with one empty group.
|
|
63
|
+
*/
|
|
64
|
+
this.filterGroups = [[]];
|
|
65
|
+
/** Ordered list of sort clauses to apply after filtering. */
|
|
66
|
+
this.sorts = [];
|
|
67
|
+
this.dataProvider = dataProvider;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Add a filter to the current AND-group.
|
|
71
|
+
*
|
|
72
|
+
* @param field - Entity field name (supports dot-paths for nested objects).
|
|
73
|
+
* @param operator - Comparison operator (=, !=, <, >, <=, >=, contains, startsWith, in, search).
|
|
74
|
+
* @param value - Value to compare against.
|
|
75
|
+
* @returns this (for chaining).
|
|
76
|
+
*/
|
|
77
|
+
where(field, operator, value) {
|
|
78
|
+
// Append to the last (current) filter group
|
|
79
|
+
this.filterGroups[this.filterGroups.length - 1].push({ field, operator, value });
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Alias for `where()` — adds another AND condition to the current group.
|
|
84
|
+
*/
|
|
85
|
+
and(field, operator, value) {
|
|
86
|
+
return this.where(field, operator, value);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Start a new OR-group with the given filter condition.
|
|
90
|
+
* Previous filters remain in their own AND-group.
|
|
91
|
+
*/
|
|
92
|
+
or(field, operator, value) {
|
|
93
|
+
// Create a fresh AND-group for the OR branch
|
|
94
|
+
this.filterGroups.push([{ field, operator, value }]);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Add a sort clause (multiple orderBy calls are applied in order).
|
|
99
|
+
*
|
|
100
|
+
* @param field - Entity field name to sort by.
|
|
101
|
+
* @param direction - "asc" (default) or "desc".
|
|
102
|
+
*/
|
|
103
|
+
orderBy(field, direction = "asc") {
|
|
104
|
+
this.sorts.push({ field, direction });
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Set the maximum number of results to return.
|
|
109
|
+
* @throws If count is negative or non-finite.
|
|
110
|
+
*/
|
|
111
|
+
limit(count) {
|
|
112
|
+
if (!Number.isFinite(count) || count < 0) {
|
|
113
|
+
throw new Error(`limit() requires a non-negative finite number, got ${count}`);
|
|
114
|
+
}
|
|
115
|
+
this._limit = Math.floor(count);
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Set the number of results to skip before returning.
|
|
120
|
+
* @throws If count is negative or non-finite.
|
|
121
|
+
*/
|
|
122
|
+
offset(count) {
|
|
123
|
+
if (!Number.isFinite(count) || count < 0) {
|
|
124
|
+
throw new Error(`offset() requires a non-negative finite number, got ${count}`);
|
|
125
|
+
}
|
|
126
|
+
this._offset = Math.floor(count);
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/** True if any filter group contains at least one filter. */
|
|
130
|
+
get hasFilters() {
|
|
131
|
+
return this.filterGroups.some((g) => g.length > 0);
|
|
132
|
+
}
|
|
133
|
+
/** True if there are multiple non-empty filter groups (OR query). */
|
|
134
|
+
get isOrQuery() {
|
|
135
|
+
return this.filterGroups.filter((g) => g.length > 0).length > 1;
|
|
136
|
+
}
|
|
137
|
+
/** Return the first non-empty filter group (for simple AND queries). */
|
|
138
|
+
get flatFilters() {
|
|
139
|
+
return this.filterGroups.find((g) => g.length > 0) ?? [];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Apply all filter groups to the entity dataset.
|
|
143
|
+
* Delegates to `QueryEngine.filterEntities` (AND) or
|
|
144
|
+
* `QueryEngine.filterEntitiesOr` (multiple OR groups).
|
|
145
|
+
*/
|
|
146
|
+
applyFilters(entities) {
|
|
147
|
+
if (!this.hasFilters)
|
|
148
|
+
return entities;
|
|
149
|
+
if (this.isOrQuery) {
|
|
150
|
+
return QueryEngine.filterEntitiesOr(entities, this.filterGroups.filter((g) => g.length > 0));
|
|
151
|
+
}
|
|
152
|
+
return QueryEngine.filterEntities(entities, this.flatFilters);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build a declarative QueryOptions object from the current builder state.
|
|
156
|
+
* Useful for passing to SheetRepository.find() or for serialisation.
|
|
157
|
+
*/
|
|
158
|
+
build() {
|
|
159
|
+
const nonEmpty = this.filterGroups.filter((g) => g.length > 0);
|
|
160
|
+
const isOr = nonEmpty.length > 1;
|
|
161
|
+
return {
|
|
162
|
+
// Simple AND query → single where array
|
|
163
|
+
where: !isOr && nonEmpty.length === 1 ? [...nonEmpty[0]] : undefined,
|
|
164
|
+
// Multi-group OR query → whereGroups array of arrays
|
|
165
|
+
whereGroups: isOr ? nonEmpty.map((g) => [...g]) : undefined,
|
|
166
|
+
orderBy: this.sorts.length > 0 ? [...this.sorts] : undefined,
|
|
167
|
+
limit: this._limit,
|
|
168
|
+
offset: this._offset,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Execute the query and return all matching entities.
|
|
173
|
+
*
|
|
174
|
+
* Pipeline: load data → filter → sort → offset/limit.
|
|
175
|
+
*/
|
|
176
|
+
execute() {
|
|
177
|
+
let entities = this.dataProvider();
|
|
178
|
+
const inputCount = entities.length;
|
|
179
|
+
// Apply filter groups
|
|
180
|
+
entities = this.applyFilters(entities);
|
|
181
|
+
// Apply sort clauses
|
|
182
|
+
if (this.sorts.length > 0) {
|
|
183
|
+
entities = QueryEngine.sortEntities(entities, this.sorts);
|
|
184
|
+
}
|
|
185
|
+
// Apply pagination (offset + limit)
|
|
186
|
+
const offset = this._offset ?? 0;
|
|
187
|
+
const limit = this._limit;
|
|
188
|
+
if (offset !== 0 || limit !== undefined) {
|
|
189
|
+
entities = entities.slice(offset, limit !== undefined ? offset + limit : undefined);
|
|
190
|
+
}
|
|
191
|
+
SheetOrmLogger.log(`[Query] execute input=${inputCount} filters=${this.filterGroups.flat().length} sort=${this.sorts.length} limit=${this._limit ?? "-"} offset=${this._offset ?? 0} → ${entities.length}`);
|
|
192
|
+
return entities;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Execute the query and return the first matching entity, or null.
|
|
196
|
+
*
|
|
197
|
+
* More efficient than `execute()[0]` when limit is already set.
|
|
198
|
+
*/
|
|
199
|
+
first() {
|
|
200
|
+
let entities = this.dataProvider();
|
|
201
|
+
const inputCount = entities.length;
|
|
202
|
+
entities = this.applyFilters(entities);
|
|
203
|
+
if (this.sorts.length > 0) {
|
|
204
|
+
entities = QueryEngine.sortEntities(entities, this.sorts);
|
|
205
|
+
}
|
|
206
|
+
const offset = this._offset ?? 0;
|
|
207
|
+
const limit = this._limit;
|
|
208
|
+
// Short-circuit if limit is explicitly 0
|
|
209
|
+
if (limit === 0) {
|
|
210
|
+
SheetOrmLogger.log(`[Query] first input=${inputCount} filters=${this.filterGroups.flat().length} → null (limit=0)`);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const visible = entities.slice(offset, limit !== undefined ? offset + limit : undefined);
|
|
214
|
+
const result = visible.length > 0 ? visible[0] : null;
|
|
215
|
+
SheetOrmLogger.log(`[Query] first input=${inputCount} filters=${this.filterGroups.flat().length} → ${result ? "found" : "null"}`);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute with explicit pagination, returning a PaginatedResult.
|
|
220
|
+
*
|
|
221
|
+
* @param offset - 0-based number of items to skip.
|
|
222
|
+
* @param limit - Maximum items per page.
|
|
223
|
+
* @returns Object with items, total, offset, limit, hasNext.
|
|
224
|
+
*/
|
|
225
|
+
select(offset, limit) {
|
|
226
|
+
let entities = this.dataProvider();
|
|
227
|
+
const inputCount = entities.length;
|
|
228
|
+
entities = this.applyFilters(entities);
|
|
229
|
+
if (this.sorts.length > 0) {
|
|
230
|
+
entities = QueryEngine.sortEntities(entities, this.sorts);
|
|
231
|
+
}
|
|
232
|
+
// Delegate slicing + total count to QueryEngine
|
|
233
|
+
const paged = QueryEngine.paginateEntities(entities, offset, limit);
|
|
234
|
+
SheetOrmLogger.log(`[Query] select input=${inputCount} filters=${this.filterGroups.flat().length} offset=${offset} limit=${limit} → ${paged.items.length}/${paged.total}`);
|
|
235
|
+
return paged;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Count the number of entities matching the current filters.
|
|
239
|
+
* No sorting or pagination is applied.
|
|
240
|
+
*/
|
|
241
|
+
count() {
|
|
242
|
+
let entities = this.dataProvider();
|
|
243
|
+
const inputCount = entities.length;
|
|
244
|
+
entities = this.applyFilters(entities);
|
|
245
|
+
SheetOrmLogger.log(`[Query] count input=${inputCount} filters=${this.filterGroups.flat().length} → ${entities.length}`);
|
|
246
|
+
return entities.length;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Group matching entities by a field value.
|
|
250
|
+
*
|
|
251
|
+
* Filters and sorts are applied before grouping.
|
|
252
|
+
*
|
|
253
|
+
* @param field - Entity field name to group by.
|
|
254
|
+
* @returns Array of GroupResult objects (one per distinct field value).
|
|
255
|
+
*/
|
|
256
|
+
groupBy(field) {
|
|
257
|
+
let entities = this.dataProvider();
|
|
258
|
+
entities = this.applyFilters(entities);
|
|
259
|
+
if (this.sorts.length > 0) {
|
|
260
|
+
entities = QueryEngine.sortEntities(entities, this.sorts);
|
|
261
|
+
}
|
|
262
|
+
return QueryEngine.groupEntities(entities, field);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Module-level resolver injected by Record at import time.
|
|
267
|
+
* Converts a class constructor or table name to a data-provider function.
|
|
268
|
+
*/
|
|
269
|
+
Query._fromResolverFn = null;
|
|
270
|
+
//# sourceMappingURL=Query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Query.js","sourceRoot":"","sources":["../../../src/query/Query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueryEngine — pure in-memory filter, sort, paginate, and group engine.
|
|
3
|
+
*
|
|
4
|
+
* Every function operates on plain entity arrays and returns new arrays
|
|
5
|
+
* without mutating the input. The implementation is heavily optimised for
|
|
6
|
+
* the GAS V8 runtime (limited JIT, no SIMD, high GC cost):
|
|
7
|
+
*
|
|
8
|
+
* - Filters are **compiled** into predicate closures once; the hot loop
|
|
9
|
+
* invokes closures instead of evaluating operator strings.
|
|
10
|
+
* - Sort keys are **pre-extracted** into column-major arrays to avoid
|
|
11
|
+
* repeated field navigation during comparison callbacks.
|
|
12
|
+
* - The `in` operator converts to a `Set` when the value list exceeds
|
|
13
|
+
* 8 elements for O(1) membership checks.
|
|
14
|
+
* - String comparison operators (`contains`, `startsWith`, `search`)
|
|
15
|
+
* are case-insensitive and pre-lower the filter value once.
|
|
16
|
+
*
|
|
17
|
+
* @module QueryEngine
|
|
18
|
+
*/
|
|
19
|
+
import type { Entity } from "../core/types/Entity.js";
|
|
20
|
+
import type { Filter } from "../core/types/Filter.js";
|
|
21
|
+
import type { SortClause } from "../core/types/SortClause.js";
|
|
22
|
+
import type { QueryOptions } from "../core/types/QueryOptions.js";
|
|
23
|
+
import type { PaginatedResult } from "../core/types/PaginatedResult.js";
|
|
24
|
+
import type { GroupResult } from "../core/types/GroupResult.js";
|
|
25
|
+
/**
|
|
26
|
+
* Filter entities by an array of filters using AND logic.
|
|
27
|
+
*
|
|
28
|
+
* All predicates are compiled once; the hot loop uses a labeled `continue outer`
|
|
29
|
+
* to short-circuit on the first failing predicate per entity (minimal GC pressure).
|
|
30
|
+
*
|
|
31
|
+
* @typeParam T - Entity type.
|
|
32
|
+
* @param entities - Source array (not mutated).
|
|
33
|
+
* @param filters - Filters to apply (all must match — AND).
|
|
34
|
+
* @returns New array of matching entities.
|
|
35
|
+
*/
|
|
36
|
+
declare function filterEntities<T extends Entity>(entities: T[], filters: Filter[]): T[];
|
|
37
|
+
/**
|
|
38
|
+
* Filter entities with OR-connected groups of AND-connected filters.
|
|
39
|
+
*
|
|
40
|
+
* An entity matches if it satisfies **any** group (OR), where within each
|
|
41
|
+
* group all filters must match (AND). Empty groups never match.
|
|
42
|
+
*
|
|
43
|
+
* @typeParam T - Entity type.
|
|
44
|
+
* @param entities - Source array (not mutated).
|
|
45
|
+
* @param groups - Array of filter groups (outer=OR, inner=AND).
|
|
46
|
+
* @returns New array of matching entities.
|
|
47
|
+
*/
|
|
48
|
+
declare function filterEntitiesOr<T extends Entity>(entities: T[], groups: Filter[][]): T[];
|
|
49
|
+
/**
|
|
50
|
+
* Sort entities by multiple {@link SortClause} fields.
|
|
51
|
+
*
|
|
52
|
+
* **Performance strategy** (optimised for GAS V8):
|
|
53
|
+
*
|
|
54
|
+
* 1. Sort keys are **pre-extracted** into column-major arrays
|
|
55
|
+
* (`keys[sortIndex][entityIndex]`) so the comparator never calls
|
|
56
|
+
* field accessors — it reads from pre-populated primitive arrays.
|
|
57
|
+
* 2. An **index array** is sorted instead of the entities themselves,
|
|
58
|
+
* avoiding expensive object moves in the V8 sort implementation.
|
|
59
|
+
* 3. The final result is assembled from the sorted indices in one pass.
|
|
60
|
+
*
|
|
61
|
+
* Null handling: `null`/`undefined` values sort **first** (before any
|
|
62
|
+
* non-null value) regardless of direction.
|
|
63
|
+
*
|
|
64
|
+
* @typeParam T - Entity type.
|
|
65
|
+
* @param entities - Source array (not mutated).
|
|
66
|
+
* @param sorts - Sort clauses in priority order (first = primary sort).
|
|
67
|
+
* @returns New sorted array.
|
|
68
|
+
*/
|
|
69
|
+
declare function sortEntities<T extends Entity>(entities: T[], sorts: SortClause[]): T[];
|
|
70
|
+
/**
|
|
71
|
+
* Apply pagination (offset + limit) to an entity array.
|
|
72
|
+
*
|
|
73
|
+
* Both `offset` and `limit` are sanitised: non-finite, negative, or
|
|
74
|
+
* fractional values are clamped to safe defaults (0 for offset, full
|
|
75
|
+
* length for limit).
|
|
76
|
+
*
|
|
77
|
+
* @typeParam T - Entity type.
|
|
78
|
+
* @param entities - Source array.
|
|
79
|
+
* @param offset - Number of items to skip from the start.
|
|
80
|
+
* @param limit - Maximum number of items to return.
|
|
81
|
+
* @returns Paginated result with metadata (total, hasNext, etc.).
|
|
82
|
+
*/
|
|
83
|
+
declare function paginateEntities<T>(entities: T[], offset: number, limit: number): PaginatedResult<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Group entities by a single field, producing one {@link GroupResult} per distinct value.
|
|
86
|
+
*
|
|
87
|
+
* Uses a `Map` keyed by the field value to collect entities in insertion
|
|
88
|
+
* order. The pre-compiled field accessor avoids repeated path resolution.
|
|
89
|
+
*
|
|
90
|
+
* @typeParam T - Entity type.
|
|
91
|
+
* @param entities - Source array (not mutated).
|
|
92
|
+
* @param field - Dot-separated field path to group by.
|
|
93
|
+
* @returns Array of group results, each containing key, count, and items.
|
|
94
|
+
*/
|
|
95
|
+
declare function groupEntities<T extends Entity>(entities: T[], field: string): GroupResult<T>[];
|
|
96
|
+
/**
|
|
97
|
+
* Execute a complete query pipeline on an in-memory entity array.
|
|
98
|
+
*
|
|
99
|
+
* Pipeline stages (executed in order):
|
|
100
|
+
*
|
|
101
|
+
* 1. **Filter**: `whereGroups` (OR of ANDs) takes precedence over `where`
|
|
102
|
+
* (simple AND). If both are present, only `whereGroups` is applied.
|
|
103
|
+
* 2. **Sort**: Multi-field sort via {@link sortEntities}.
|
|
104
|
+
* 3. **Paginate**: `offset` and `limit` applied via `Array.slice`.
|
|
105
|
+
*
|
|
106
|
+
* This function does NOT call {@link paginateEntities} — it applies a
|
|
107
|
+
* lighter slice-only pagination without producing a `PaginatedResult`
|
|
108
|
+
* wrapper. The caller (typically {@link SheetRepository}) is responsible
|
|
109
|
+
* for wrapping the result if needed.
|
|
110
|
+
*
|
|
111
|
+
* @typeParam T - Entity type.
|
|
112
|
+
* @param entities - Full entity array to query against.
|
|
113
|
+
* @param options - Query options controlling filter, sort, and pagination.
|
|
114
|
+
* @returns Filtered, sorted, and paginated entity array.
|
|
115
|
+
*/
|
|
116
|
+
declare function executeQuery<T extends Entity>(entities: T[], options: QueryOptions): T[];
|
|
117
|
+
/**
|
|
118
|
+
* Static facade exposing the query engine's pure functions as class methods.
|
|
119
|
+
*
|
|
120
|
+
* All methods are stateless — they operate on the provided entity array
|
|
121
|
+
* and return new arrays without side effects. The class form exists so
|
|
122
|
+
* that callers can reference `QueryEngine.filterEntities(...)` instead of
|
|
123
|
+
* importing individual module-level functions.
|
|
124
|
+
*/
|
|
125
|
+
export declare class QueryEngine {
|
|
126
|
+
/** Filter entities with AND-connected conditions. */
|
|
127
|
+
static filterEntities: typeof filterEntities;
|
|
128
|
+
/** Filter entities with OR-connected groups of AND-connected conditions. */
|
|
129
|
+
static filterEntitiesOr: typeof filterEntitiesOr;
|
|
130
|
+
/** Sort entities by multiple fields with asc/desc directions. */
|
|
131
|
+
static sortEntities: typeof sortEntities;
|
|
132
|
+
/** Paginate entities and return metadata (total, hasNext, etc.). */
|
|
133
|
+
static paginateEntities: typeof paginateEntities;
|
|
134
|
+
/** Group entities by a field into key/count/items buckets. */
|
|
135
|
+
static groupEntities: typeof groupEntities;
|
|
136
|
+
/** Execute a full filter → sort → paginate pipeline. */
|
|
137
|
+
static executeQuery: typeof executeQuery;
|
|
138
|
+
}
|
|
139
|
+
export {};
|
|
140
|
+
//# sourceMappingURL=QueryEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../../src/query/QueryEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAoIhE;;;;;;;;;;GAUG;AACH,iBAAS,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAuB/E;AAED;;;;;;;;;;GAUG;AACH,iBAAS,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,CA8ClF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,CA0D/E;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,CAY7F;AAED;;;;;;;;;;GAUG;AACH,iBAAS,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAuBvF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,CAAC,EAAE,CAqCjF;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;IACtB,qDAAqD;IACrD,MAAM,CAAC,cAAc,wBAAkB;IACvC,4EAA4E;IAC5E,MAAM,CAAC,gBAAgB,0BAAoB;IAC3C,iEAAiE;IACjE,MAAM,CAAC,YAAY,sBAAgB;IACnC,oEAAoE;IACpE,MAAM,CAAC,gBAAgB,0BAAoB;IAC3C,8DAA8D;IAC9D,MAAM,CAAC,aAAa,uBAAiB;IACrC,wDAAwD;IACxD,MAAM,CAAC,YAAY,sBAAgB;CACpC"}
|