@objectql/core 4.1.0 → 4.2.1
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +42 -0
- package/dist/app.d.ts +61 -51
- package/dist/app.js +101 -431
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +4 -11
- package/dist/index.js +13 -9
- package/dist/index.js.map +1 -1
- package/dist/kernel-factory.d.ts +38 -0
- package/dist/kernel-factory.js +38 -0
- package/dist/kernel-factory.js.map +1 -0
- package/dist/plugin.d.ts +10 -16
- package/dist/plugin.js +104 -58
- package/dist/plugin.js.map +1 -1
- package/dist/repository.d.ts +4 -31
- package/dist/repository.js +7 -275
- package/dist/repository.js.map +1 -1
- package/dist/util.js +4 -2
- package/dist/util.js.map +1 -1
- package/package.json +19 -11
- package/src/app.ts +157 -531
- package/src/index.ts +8 -23
- package/src/kernel-factory.ts +47 -0
- package/src/plugin.ts +116 -94
- package/src/repository.ts +4 -313
- package/src/util.ts +5 -3
- package/test/__mocks__/@objectstack/core.ts +250 -1
- package/test/__mocks__/@objectstack/objectql.ts +127 -0
- package/test/__mocks__/@objectstack/runtime.ts +14 -5
- package/test/introspection.test.ts +1 -1
- package/test/mock-driver.ts +5 -5
- package/test/optimizations.test.ts +2 -2
- package/test/plugin-integration.test.ts +4 -5
- package/test/utils.ts +6 -6
- package/tsconfig.json +3 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/gateway.d.ts +0 -36
- package/dist/gateway.js +0 -89
- package/dist/gateway.js.map +0 -1
- package/dist/optimizations/CompiledHookManager.d.ts +0 -55
- package/dist/optimizations/CompiledHookManager.js +0 -164
- package/dist/optimizations/CompiledHookManager.js.map +0 -1
- package/dist/optimizations/DependencyGraph.d.ts +0 -82
- package/dist/optimizations/DependencyGraph.js +0 -211
- package/dist/optimizations/DependencyGraph.js.map +0 -1
- package/dist/optimizations/GlobalConnectionPool.d.ts +0 -89
- package/dist/optimizations/GlobalConnectionPool.js +0 -193
- package/dist/optimizations/GlobalConnectionPool.js.map +0 -1
- package/dist/optimizations/LazyMetadataLoader.d.ts +0 -75
- package/dist/optimizations/LazyMetadataLoader.js +0 -149
- package/dist/optimizations/LazyMetadataLoader.js.map +0 -1
- package/dist/optimizations/OptimizedMetadataRegistry.d.ts +0 -26
- package/dist/optimizations/OptimizedMetadataRegistry.js +0 -117
- package/dist/optimizations/OptimizedMetadataRegistry.js.map +0 -1
- package/dist/optimizations/OptimizedValidationEngine.d.ts +0 -73
- package/dist/optimizations/OptimizedValidationEngine.js +0 -141
- package/dist/optimizations/OptimizedValidationEngine.js.map +0 -1
- package/dist/optimizations/QueryCompiler.d.ts +0 -51
- package/dist/optimizations/QueryCompiler.js +0 -216
- package/dist/optimizations/QueryCompiler.js.map +0 -1
- package/dist/optimizations/SQLQueryOptimizer.d.ts +0 -96
- package/dist/optimizations/SQLQueryOptimizer.js +0 -265
- package/dist/optimizations/SQLQueryOptimizer.js.map +0 -1
- package/dist/optimizations/index.d.ts +0 -32
- package/dist/optimizations/index.js +0 -44
- package/dist/optimizations/index.js.map +0 -1
- package/dist/protocol.d.ts +0 -180
- package/dist/protocol.js +0 -260
- package/dist/protocol.js.map +0 -1
- package/dist/query/filter-translator.d.ts +0 -27
- package/dist/query/filter-translator.js +0 -38
- package/dist/query/filter-translator.js.map +0 -1
- package/dist/query/index.d.ts +0 -22
- package/dist/query/index.js +0 -39
- package/dist/query/index.js.map +0 -1
- package/dist/query/query-analyzer.d.ts +0 -188
- package/dist/query/query-analyzer.js +0 -348
- package/dist/query/query-analyzer.js.map +0 -1
- package/dist/query/query-builder.d.ts +0 -35
- package/dist/query/query-builder.js +0 -61
- package/dist/query/query-builder.js.map +0 -1
- package/dist/query/query-service.d.ts +0 -153
- package/dist/query/query-service.js +0 -272
- package/dist/query/query-service.js.map +0 -1
- package/jest.config.js +0 -29
- package/src/gateway.ts +0 -101
- package/src/optimizations/CompiledHookManager.ts +0 -185
- package/src/optimizations/DependencyGraph.ts +0 -255
- package/src/optimizations/GlobalConnectionPool.ts +0 -251
- package/src/optimizations/LazyMetadataLoader.ts +0 -180
- package/src/optimizations/OptimizedMetadataRegistry.ts +0 -132
- package/src/optimizations/OptimizedValidationEngine.ts +0 -172
- package/src/optimizations/QueryCompiler.ts +0 -242
- package/src/optimizations/SQLQueryOptimizer.ts +0 -329
- package/src/optimizations/index.ts +0 -34
- package/src/protocol.ts +0 -291
- package/src/query/filter-translator.ts +0 -41
- package/src/query/index.ts +0 -24
- package/src/query/query-analyzer.ts +0 -533
- package/src/query/query-builder.ts +0 -64
- package/src/query/query-service.ts +0 -398
- package/test/app.test.ts +0 -578
- package/test/filter-syntax.test.ts +0 -233
- package/test/gateway.test.ts +0 -88
- package/test/protocol.test.ts +0 -143
- package/test/repository-validation.test.ts +0 -351
- package/test/repository.test.ts +0 -151
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectQL Query Service
|
|
3
|
-
* Copyright (c) 2026-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type {
|
|
10
|
-
Driver,
|
|
11
|
-
ObjectConfig,
|
|
12
|
-
UnifiedQuery,
|
|
13
|
-
Filter,
|
|
14
|
-
MetadataRegistry
|
|
15
|
-
} from '@objectql/types';
|
|
16
|
-
import { Data } from '@objectstack/spec';
|
|
17
|
-
type QueryAST = Data.QueryAST;
|
|
18
|
-
import { QueryBuilder } from './query-builder';
|
|
19
|
-
import { QueryCompiler } from '../optimizations/QueryCompiler';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Options for query execution
|
|
23
|
-
*/
|
|
24
|
-
export interface QueryOptions {
|
|
25
|
-
/**
|
|
26
|
-
* Transaction handle for transactional queries
|
|
27
|
-
*/
|
|
28
|
-
transaction?: any;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Skip validation (for system operations)
|
|
32
|
-
*/
|
|
33
|
-
skipValidation?: boolean;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Include profiling information
|
|
37
|
-
*/
|
|
38
|
-
profile?: boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Custom driver options
|
|
42
|
-
*/
|
|
43
|
-
driverOptions?: Record<string, unknown>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Result of a query execution with optional profiling data
|
|
48
|
-
*/
|
|
49
|
-
export interface QueryResult<T = any> {
|
|
50
|
-
/**
|
|
51
|
-
* The query results
|
|
52
|
-
*/
|
|
53
|
-
value: T;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Total count (for paginated queries)
|
|
57
|
-
*/
|
|
58
|
-
count?: number;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Profiling information (if profile option was enabled)
|
|
62
|
-
*/
|
|
63
|
-
profile?: QueryProfile;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Profiling information for a query
|
|
68
|
-
*/
|
|
69
|
-
export interface QueryProfile {
|
|
70
|
-
/**
|
|
71
|
-
* Execution time in milliseconds
|
|
72
|
-
*/
|
|
73
|
-
executionTime: number;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Number of rows scanned
|
|
77
|
-
*/
|
|
78
|
-
rowsScanned?: number;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Whether an index was used
|
|
82
|
-
*/
|
|
83
|
-
indexUsed?: boolean;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The generated QueryAST
|
|
87
|
-
*/
|
|
88
|
-
ast?: QueryAST;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Query Service
|
|
93
|
-
*
|
|
94
|
-
* Handles all query execution logic, separating query concerns from
|
|
95
|
-
* the repository pattern. This service is responsible for:
|
|
96
|
-
* - Building QueryAST from UnifiedQuery
|
|
97
|
-
* - Executing queries via drivers
|
|
98
|
-
* - Optional query profiling and analysis
|
|
99
|
-
*
|
|
100
|
-
* The QueryService is registered as a service in the ObjectQLPlugin
|
|
101
|
-
* and can be used by Repository for all read operations.
|
|
102
|
-
*/
|
|
103
|
-
export class QueryService {
|
|
104
|
-
private queryBuilder: QueryBuilder;
|
|
105
|
-
private queryCompiler: QueryCompiler;
|
|
106
|
-
|
|
107
|
-
constructor(
|
|
108
|
-
private datasources: Record<string, Driver>,
|
|
109
|
-
private metadata: MetadataRegistry
|
|
110
|
-
) {
|
|
111
|
-
this.queryBuilder = new QueryBuilder();
|
|
112
|
-
this.queryCompiler = new QueryCompiler(1000);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get the driver for a specific object
|
|
117
|
-
* @private
|
|
118
|
-
*/
|
|
119
|
-
private getDriver(objectName: string): Driver {
|
|
120
|
-
const obj = this.getSchema(objectName);
|
|
121
|
-
const datasourceName = obj.datasource || 'default';
|
|
122
|
-
const driver = this.datasources[datasourceName];
|
|
123
|
-
|
|
124
|
-
if (!driver) {
|
|
125
|
-
throw new Error(`Datasource '${datasourceName}' not found for object '${objectName}'`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return driver;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get the schema for an object
|
|
133
|
-
* @private
|
|
134
|
-
*/
|
|
135
|
-
private getSchema(objectName: string): ObjectConfig {
|
|
136
|
-
const obj = this.metadata.get('object', objectName);
|
|
137
|
-
if (!obj) {
|
|
138
|
-
throw new Error(`Object '${objectName}' not found in metadata`);
|
|
139
|
-
}
|
|
140
|
-
return obj;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Build QueryAST from UnifiedQuery
|
|
145
|
-
* @private
|
|
146
|
-
*/
|
|
147
|
-
private buildQueryAST(objectName: string, query: UnifiedQuery): QueryAST {
|
|
148
|
-
const ast = this.queryBuilder.build(objectName, query);
|
|
149
|
-
const compiled = this.queryCompiler.compile(objectName, ast);
|
|
150
|
-
return compiled.ast;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Execute a find query
|
|
155
|
-
*
|
|
156
|
-
* @param objectName - The object to query
|
|
157
|
-
* @param query - The unified query
|
|
158
|
-
* @param options - Query execution options
|
|
159
|
-
* @returns Array of matching records
|
|
160
|
-
*/
|
|
161
|
-
async find(
|
|
162
|
-
objectName: string,
|
|
163
|
-
query: UnifiedQuery = {},
|
|
164
|
-
options: QueryOptions = {}
|
|
165
|
-
): Promise<QueryResult<any[]>> {
|
|
166
|
-
const driver = this.getDriver(objectName);
|
|
167
|
-
const startTime = options.profile ? Date.now() : 0;
|
|
168
|
-
|
|
169
|
-
// Build QueryAST
|
|
170
|
-
const ast = this.buildQueryAST(objectName, query);
|
|
171
|
-
|
|
172
|
-
// Execute query via driver
|
|
173
|
-
const driverOptions = {
|
|
174
|
-
transaction: options.transaction,
|
|
175
|
-
...options.driverOptions
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
let results: any[];
|
|
179
|
-
let count: number | undefined;
|
|
180
|
-
|
|
181
|
-
if (driver.find) {
|
|
182
|
-
// Legacy driver interface
|
|
183
|
-
const result: any = await driver.find(objectName, query, driverOptions);
|
|
184
|
-
results = Array.isArray(result) ? result : (result?.value || []);
|
|
185
|
-
count = (typeof result === 'object' && !Array.isArray(result) && result?.count !== undefined) ? result.count : undefined;
|
|
186
|
-
} else if (driver.executeQuery) {
|
|
187
|
-
// New DriverInterface
|
|
188
|
-
const result = await driver.executeQuery(ast, driverOptions);
|
|
189
|
-
results = result.value || [];
|
|
190
|
-
count = result.count;
|
|
191
|
-
} else {
|
|
192
|
-
throw new Error(`Driver does not support query execution`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const executionTime = options.profile ? Date.now() - startTime : 0;
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
value: results,
|
|
199
|
-
count,
|
|
200
|
-
profile: options.profile ? {
|
|
201
|
-
executionTime,
|
|
202
|
-
ast,
|
|
203
|
-
rowsScanned: results.length,
|
|
204
|
-
} : undefined
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Execute a findOne query by ID
|
|
210
|
-
*
|
|
211
|
-
* @param objectName - The object to query
|
|
212
|
-
* @param id - The record ID
|
|
213
|
-
* @param options - Query execution options
|
|
214
|
-
* @returns The matching record or undefined
|
|
215
|
-
*/
|
|
216
|
-
async findOne(
|
|
217
|
-
objectName: string,
|
|
218
|
-
id: string | number,
|
|
219
|
-
options: QueryOptions = {}
|
|
220
|
-
): Promise<QueryResult<any>> {
|
|
221
|
-
const driver = this.getDriver(objectName);
|
|
222
|
-
const startTime = options.profile ? Date.now() : 0;
|
|
223
|
-
|
|
224
|
-
const driverOptions = {
|
|
225
|
-
transaction: options.transaction,
|
|
226
|
-
...options.driverOptions
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
let result: any;
|
|
230
|
-
|
|
231
|
-
if (driver.findOne) {
|
|
232
|
-
// Legacy driver interface
|
|
233
|
-
result = await driver.findOne(objectName, id, driverOptions);
|
|
234
|
-
} else if (driver.get) {
|
|
235
|
-
// Alternative method name
|
|
236
|
-
result = await driver.get(objectName, String(id), driverOptions);
|
|
237
|
-
} else if (driver.executeQuery) {
|
|
238
|
-
// Fallback to query with ID filter
|
|
239
|
-
const query: UnifiedQuery = {
|
|
240
|
-
where: { _id: id }
|
|
241
|
-
};
|
|
242
|
-
const ast = this.buildQueryAST(objectName, query);
|
|
243
|
-
const queryResult = await driver.executeQuery(ast, driverOptions);
|
|
244
|
-
result = queryResult.value?.[0];
|
|
245
|
-
} else {
|
|
246
|
-
throw new Error(`Driver does not support findOne operation`);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const executionTime = options.profile ? Date.now() - startTime : 0;
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
value: result,
|
|
253
|
-
profile: options.profile ? {
|
|
254
|
-
executionTime,
|
|
255
|
-
rowsScanned: result ? 1 : 0,
|
|
256
|
-
} : undefined
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Execute a count query
|
|
262
|
-
*
|
|
263
|
-
* @param objectName - The object to query
|
|
264
|
-
* @param where - Optional filter condition
|
|
265
|
-
* @param options - Query execution options
|
|
266
|
-
* @returns Count of matching records
|
|
267
|
-
*/
|
|
268
|
-
async count(
|
|
269
|
-
objectName: string,
|
|
270
|
-
where?: Filter,
|
|
271
|
-
options: QueryOptions = {}
|
|
272
|
-
): Promise<QueryResult<number>> {
|
|
273
|
-
const driver = this.getDriver(objectName);
|
|
274
|
-
const startTime = options.profile ? Date.now() : 0;
|
|
275
|
-
|
|
276
|
-
const query: UnifiedQuery = where ? { where } : {};
|
|
277
|
-
const ast = this.buildQueryAST(objectName, query);
|
|
278
|
-
|
|
279
|
-
const driverOptions = {
|
|
280
|
-
transaction: options.transaction,
|
|
281
|
-
...options.driverOptions
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
let count: number;
|
|
285
|
-
|
|
286
|
-
if (driver.count) {
|
|
287
|
-
// Legacy driver interface
|
|
288
|
-
count = await driver.count(objectName, where || {}, driverOptions);
|
|
289
|
-
} else if (driver.executeQuery) {
|
|
290
|
-
// Use executeQuery and count results
|
|
291
|
-
// Note: This is inefficient for large datasets
|
|
292
|
-
// Ideally, driver should support count-specific optimization
|
|
293
|
-
const result = await driver.executeQuery(ast, driverOptions);
|
|
294
|
-
count = result.count ?? result.value?.length ?? 0;
|
|
295
|
-
} else {
|
|
296
|
-
throw new Error(`Driver does not support count operation`);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const executionTime = options.profile ? Date.now() - startTime : 0;
|
|
300
|
-
|
|
301
|
-
return {
|
|
302
|
-
value: count,
|
|
303
|
-
profile: options.profile ? {
|
|
304
|
-
executionTime,
|
|
305
|
-
ast,
|
|
306
|
-
} : undefined
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Execute an aggregate query
|
|
312
|
-
*
|
|
313
|
-
* @param objectName - The object to query
|
|
314
|
-
* @param query - The aggregation query using UnifiedQuery format
|
|
315
|
-
* @param options - Query execution options
|
|
316
|
-
* @returns Aggregation results
|
|
317
|
-
*/
|
|
318
|
-
async aggregate(
|
|
319
|
-
objectName: string,
|
|
320
|
-
query: UnifiedQuery,
|
|
321
|
-
options: QueryOptions = {}
|
|
322
|
-
): Promise<QueryResult<any[]>> {
|
|
323
|
-
const driver = this.getDriver(objectName);
|
|
324
|
-
const startTime = options.profile ? Date.now() : 0;
|
|
325
|
-
|
|
326
|
-
const driverOptions = {
|
|
327
|
-
transaction: options.transaction,
|
|
328
|
-
...options.driverOptions
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
let results: any[];
|
|
332
|
-
|
|
333
|
-
if (driver.aggregate) {
|
|
334
|
-
// Driver supports aggregation
|
|
335
|
-
results = await driver.aggregate(objectName, query, driverOptions);
|
|
336
|
-
} else {
|
|
337
|
-
// Driver doesn't support aggregation
|
|
338
|
-
throw new Error(`Driver does not support aggregate operations. Consider using a driver that supports aggregation.`);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const executionTime = options.profile ? Date.now() - startTime : 0;
|
|
342
|
-
|
|
343
|
-
return {
|
|
344
|
-
value: results,
|
|
345
|
-
profile: options.profile ? {
|
|
346
|
-
executionTime,
|
|
347
|
-
rowsScanned: results.length,
|
|
348
|
-
} : undefined
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Execute a direct SQL/query passthrough
|
|
354
|
-
*
|
|
355
|
-
* This bypasses ObjectQL's query builder and executes raw queries.
|
|
356
|
-
* Use with caution as it bypasses security and validation.
|
|
357
|
-
*
|
|
358
|
-
* @param objectName - The object (determines which datasource to use)
|
|
359
|
-
* @param queryString - Raw query string (SQL, MongoDB query, etc.)
|
|
360
|
-
* @param params - Query parameters (for parameterized queries)
|
|
361
|
-
* @param options - Query execution options
|
|
362
|
-
* @returns Query results
|
|
363
|
-
*/
|
|
364
|
-
async directQuery(
|
|
365
|
-
objectName: string,
|
|
366
|
-
queryString: string,
|
|
367
|
-
params?: any[],
|
|
368
|
-
options: QueryOptions = {}
|
|
369
|
-
): Promise<QueryResult<any>> {
|
|
370
|
-
const driver = this.getDriver(objectName);
|
|
371
|
-
const startTime = options.profile ? Date.now() : 0;
|
|
372
|
-
|
|
373
|
-
const driverOptions = {
|
|
374
|
-
transaction: options.transaction,
|
|
375
|
-
...options.driverOptions
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
let results: any;
|
|
379
|
-
|
|
380
|
-
if (driver.directQuery) {
|
|
381
|
-
results = await driver.directQuery(queryString, params);
|
|
382
|
-
} else if (driver.query) {
|
|
383
|
-
// Alternative method name
|
|
384
|
-
results = await driver.query(queryString, params);
|
|
385
|
-
} else {
|
|
386
|
-
throw new Error(`Driver does not support direct query execution`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const executionTime = options.profile ? Date.now() - startTime : 0;
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
value: results,
|
|
393
|
-
profile: options.profile ? {
|
|
394
|
-
executionTime,
|
|
395
|
-
} : undefined
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
}
|