@objectql/core 3.0.1 → 4.0.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/CHANGELOG.md +17 -3
- package/README.md +31 -9
- package/dist/ai-agent.d.ts +4 -3
- package/dist/ai-agent.js +10 -3
- package/dist/ai-agent.js.map +1 -1
- package/dist/app.d.ts +29 -6
- package/dist/app.js +117 -58
- package/dist/app.js.map +1 -1
- package/dist/formula-engine.d.ts +7 -0
- package/dist/formula-engine.js +9 -2
- package/dist/formula-engine.js.map +1 -1
- package/dist/formula-plugin.d.ts +52 -0
- package/dist/formula-plugin.js +107 -0
- package/dist/formula-plugin.js.map +1 -0
- package/dist/index.d.ts +16 -3
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +89 -0
- package/dist/plugin.js +136 -0
- package/dist/plugin.js.map +1 -0
- package/dist/query/filter-translator.d.ts +39 -0
- package/dist/query/filter-translator.js +135 -0
- package/dist/query/filter-translator.js.map +1 -0
- package/dist/query/index.d.ts +22 -0
- package/dist/query/index.js +39 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/query-analyzer.d.ts +188 -0
- package/dist/query/query-analyzer.js +349 -0
- package/dist/query/query-analyzer.js.map +1 -0
- package/dist/query/query-builder.d.ts +29 -0
- package/dist/query/query-builder.js +71 -0
- package/dist/query/query-builder.js.map +1 -0
- package/dist/query/query-service.d.ts +152 -0
- package/dist/query/query-service.js +268 -0
- package/dist/query/query-service.js.map +1 -0
- package/dist/repository.d.ts +23 -2
- package/dist/repository.js +81 -14
- package/dist/repository.js.map +1 -1
- package/dist/util.d.ts +7 -0
- package/dist/util.js +18 -3
- package/dist/util.js.map +1 -1
- package/dist/validator-plugin.d.ts +56 -0
- package/dist/validator-plugin.js +106 -0
- package/dist/validator-plugin.js.map +1 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.js +10 -8
- package/dist/validator.js.map +1 -1
- package/jest.config.js +16 -0
- package/package.json +7 -5
- package/src/ai-agent.ts +8 -0
- package/src/app.ts +136 -72
- package/src/formula-engine.ts +8 -0
- package/src/formula-plugin.ts +141 -0
- package/src/index.ts +28 -3
- package/src/plugin.ts +224 -0
- package/src/query/filter-translator.ts +148 -0
- package/src/query/index.ts +24 -0
- package/src/query/query-analyzer.ts +537 -0
- package/src/query/query-builder.ts +81 -0
- package/src/query/query-service.ts +393 -0
- package/src/repository.ts +101 -18
- package/src/util.ts +19 -3
- package/src/validator-plugin.ts +140 -0
- package/src/validator.ts +12 -5
- package/test/__mocks__/@objectstack/runtime.ts +255 -0
- package/test/app.test.ts +23 -35
- package/test/filter-syntax.test.ts +233 -0
- package/test/formula-engine.test.ts +8 -0
- package/test/formula-integration.test.ts +8 -0
- package/test/formula-plugin.test.ts +197 -0
- package/test/introspection.test.ts +8 -0
- package/test/mock-driver.ts +8 -0
- package/test/plugin-integration.test.ts +213 -0
- package/test/repository-validation.test.ts +8 -0
- package/test/repository.test.ts +8 -0
- package/test/util.test.ts +9 -1
- package/test/utils.ts +8 -0
- package/test/validator-plugin.test.ts +126 -0
- package/test/validator.test.ts +8 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/action.d.ts +0 -7
- package/dist/action.js +0 -23
- package/dist/action.js.map +0 -1
- package/dist/hook.d.ts +0 -8
- package/dist/hook.js +0 -25
- package/dist/hook.js.map +0 -1
- package/dist/object.d.ts +0 -3
- package/dist/object.js +0 -28
- package/dist/object.js.map +0 -1
- package/src/action.ts +0 -40
- package/src/hook.ts +0 -42
- package/src/object.ts +0 -26
- package/test/action.test.ts +0 -276
- package/test/hook.test.ts +0 -343
- package/test/object.test.ts +0 -183
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectQL
|
|
4
|
+
* Copyright (c) 2026-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.FilterTranslator = void 0;
|
|
11
|
+
const types_1 = require("@objectql/types");
|
|
12
|
+
/**
|
|
13
|
+
* Filter Translator
|
|
14
|
+
*
|
|
15
|
+
* Translates ObjectQL Filter (FilterCondition) to ObjectStack FilterNode format.
|
|
16
|
+
* Converts modern object-based syntax to legacy array-based syntax for backward compatibility.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* Input: { age: { $gte: 18 }, $or: [{ status: "active" }, { role: "admin" }] }
|
|
20
|
+
* Output: [["age", ">=", 18], "or", [["status", "=", "active"], "or", ["role", "=", "admin"]]]
|
|
21
|
+
*/
|
|
22
|
+
class FilterTranslator {
|
|
23
|
+
/**
|
|
24
|
+
* Translate filters from ObjectQL format to ObjectStack FilterNode format
|
|
25
|
+
*/
|
|
26
|
+
translate(filters) {
|
|
27
|
+
if (!filters) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
// Backward compatibility: if it's already an array (old format), pass through
|
|
31
|
+
if (Array.isArray(filters)) {
|
|
32
|
+
return filters;
|
|
33
|
+
}
|
|
34
|
+
// If it's an empty object, return undefined
|
|
35
|
+
if (typeof filters === 'object' && Object.keys(filters).length === 0) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return this.convertToNode(filters);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Recursively converts FilterCondition to FilterNode array format
|
|
42
|
+
*/
|
|
43
|
+
convertToNode(filter) {
|
|
44
|
+
const nodes = [];
|
|
45
|
+
// Process logical operators first
|
|
46
|
+
if (filter.$and) {
|
|
47
|
+
const andNodes = filter.$and.map((f) => this.convertToNode(f));
|
|
48
|
+
nodes.push(...this.interleaveWithOperator(andNodes, 'and'));
|
|
49
|
+
}
|
|
50
|
+
if (filter.$or) {
|
|
51
|
+
const orNodes = filter.$or.map((f) => this.convertToNode(f));
|
|
52
|
+
if (nodes.length > 0) {
|
|
53
|
+
nodes.push('and');
|
|
54
|
+
}
|
|
55
|
+
nodes.push(...this.interleaveWithOperator(orNodes, 'or'));
|
|
56
|
+
}
|
|
57
|
+
// Note: $not operator is not currently supported in the legacy FilterNode format
|
|
58
|
+
if (filter.$not) {
|
|
59
|
+
throw new types_1.ObjectQLError({
|
|
60
|
+
code: 'UNSUPPORTED_OPERATOR',
|
|
61
|
+
message: '$not operator is not supported. Use $ne for field negation instead.'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Process field conditions
|
|
65
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
66
|
+
if (field.startsWith('$')) {
|
|
67
|
+
continue; // Skip logical operators (already processed)
|
|
68
|
+
}
|
|
69
|
+
if (nodes.length > 0) {
|
|
70
|
+
nodes.push('and');
|
|
71
|
+
}
|
|
72
|
+
// Handle field value
|
|
73
|
+
if (value === null || value === undefined) {
|
|
74
|
+
nodes.push([field, '=', value]);
|
|
75
|
+
}
|
|
76
|
+
else if (typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
|
77
|
+
// Explicit operators - multiple operators on same field are AND-ed together
|
|
78
|
+
const entries = Object.entries(value);
|
|
79
|
+
for (let i = 0; i < entries.length; i++) {
|
|
80
|
+
const [op, opValue] = entries[i];
|
|
81
|
+
// Add 'and' before each operator (except the very first node)
|
|
82
|
+
if (nodes.length > 0 || i > 0) {
|
|
83
|
+
nodes.push('and');
|
|
84
|
+
}
|
|
85
|
+
const legacyOp = this.mapOperatorToLegacy(op);
|
|
86
|
+
nodes.push([field, legacyOp, opValue]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Implicit equality
|
|
91
|
+
nodes.push([field, '=', value]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Return as FilterNode (type assertion for backward compatibility)
|
|
95
|
+
return (nodes.length === 1 ? nodes[0] : nodes);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Interleaves filter nodes with a logical operator
|
|
99
|
+
*/
|
|
100
|
+
interleaveWithOperator(nodes, operator) {
|
|
101
|
+
if (nodes.length === 0)
|
|
102
|
+
return [];
|
|
103
|
+
if (nodes.length === 1)
|
|
104
|
+
return [nodes[0]];
|
|
105
|
+
const result = [nodes[0]];
|
|
106
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
107
|
+
result.push(operator, nodes[i]);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Maps modern $-prefixed operators to legacy format
|
|
113
|
+
*/
|
|
114
|
+
mapOperatorToLegacy(operator) {
|
|
115
|
+
const mapping = {
|
|
116
|
+
'$eq': '=',
|
|
117
|
+
'$ne': '!=',
|
|
118
|
+
'$gt': '>',
|
|
119
|
+
'$gte': '>=',
|
|
120
|
+
'$lt': '<',
|
|
121
|
+
'$lte': '<=',
|
|
122
|
+
'$in': 'in',
|
|
123
|
+
'$nin': 'nin',
|
|
124
|
+
'$contains': 'contains',
|
|
125
|
+
'$startsWith': 'startswith',
|
|
126
|
+
'$endsWith': 'endswith',
|
|
127
|
+
'$null': 'is_null',
|
|
128
|
+
'$exist': 'is_not_null',
|
|
129
|
+
'$between': 'between',
|
|
130
|
+
};
|
|
131
|
+
return mapping[operator] || operator.replace('$', '');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.FilterTranslator = FilterTranslator;
|
|
135
|
+
//# sourceMappingURL=filter-translator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-translator.js","sourceRoot":"","sources":["../../src/query/filter-translator.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAKH,2CAAgD;AAEhD;;;;;;;;;GASG;AACH,MAAa,gBAAgB;IACzB;;OAEG;IACH,SAAS,CAAC,OAAgB;QACtB,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,8EAA8E;QAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,OAAgC,CAAC;QAC5C,CAAC;QAED,4CAA4C;QAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,MAAc;QAChC,MAAM,KAAK,GAAU,EAAE,CAAC;QAExB,kCAAkC;QAClC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9D,CAAC;QAED,iFAAiF;QACjF,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,qBAAa,CAAC;gBACpB,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,qEAAqE;aACjF,CAAC,CAAC;QACP,CAAC;QAED,2BAA2B;QAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,SAAS,CAAC,6CAA6C;YAC3D,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YAED,qBAAqB;YACrB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;YACpC,CAAC;iBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,EAAE,CAAC;gBACxF,4EAA4E;gBAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACtC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;oBAEjC,8DAA8D;oBAC9D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC5B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACtB,CAAC;oBAED,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;oBAC9C,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC3C,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,oBAAoB;gBACpB,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;YACpC,CAAC;QACL,CAAC;QAED,mEAAmE;QACnE,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAA0B,CAAC;IAC5E,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,KAAmB,EAAE,QAAgB;QAChE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1C,MAAM,MAAM,GAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,QAAgB;QACxC,MAAM,OAAO,GAA2B;YACpC,KAAK,EAAE,GAAG;YACV,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,KAAK;YACb,WAAW,EAAE,UAAU;YACvB,aAAa,EAAE,YAAY;YAC3B,WAAW,EAAE,UAAU;YACvB,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,aAAa;YACvB,UAAU,EAAE,SAAS;SACxB,CAAC;QAEF,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;CACJ;AA5HD,4CA4HC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL
|
|
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
|
+
* Query Module
|
|
10
|
+
*
|
|
11
|
+
* This module contains ObjectQL's query-specific functionality:
|
|
12
|
+
* - FilterTranslator: Converts ObjectQL filters to ObjectStack FilterNode
|
|
13
|
+
* - QueryBuilder: Builds ObjectStack QueryAST from ObjectQL UnifiedQuery
|
|
14
|
+
* - QueryService: Executes queries via drivers with profiling support
|
|
15
|
+
* - QueryAnalyzer: Provides query performance analysis and optimization suggestions
|
|
16
|
+
*
|
|
17
|
+
* These are the core components that differentiate ObjectQL from generic runtime systems.
|
|
18
|
+
*/
|
|
19
|
+
export * from './filter-translator';
|
|
20
|
+
export * from './query-builder';
|
|
21
|
+
export * from './query-service';
|
|
22
|
+
export * from './query-analyzer';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectQL
|
|
4
|
+
* Copyright (c) 2026-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
21
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
/**
|
|
25
|
+
* Query Module
|
|
26
|
+
*
|
|
27
|
+
* This module contains ObjectQL's query-specific functionality:
|
|
28
|
+
* - FilterTranslator: Converts ObjectQL filters to ObjectStack FilterNode
|
|
29
|
+
* - QueryBuilder: Builds ObjectStack QueryAST from ObjectQL UnifiedQuery
|
|
30
|
+
* - QueryService: Executes queries via drivers with profiling support
|
|
31
|
+
* - QueryAnalyzer: Provides query performance analysis and optimization suggestions
|
|
32
|
+
*
|
|
33
|
+
* These are the core components that differentiate ObjectQL from generic runtime systems.
|
|
34
|
+
*/
|
|
35
|
+
__exportStar(require("./filter-translator"), exports);
|
|
36
|
+
__exportStar(require("./query-builder"), exports);
|
|
37
|
+
__exportStar(require("./query-service"), exports);
|
|
38
|
+
__exportStar(require("./query-analyzer"), exports);
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;AAEH;;;;;;;;;;GAUG;AAEH,sDAAoC;AACpC,kDAAgC;AAChC,kDAAgC;AAChC,mDAAiC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL Query Analyzer
|
|
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
|
+
import type { UnifiedQuery, MetadataRegistry } from '@objectql/types';
|
|
9
|
+
import { Data } from '@objectstack/spec';
|
|
10
|
+
type QueryAST = Data.QueryAST;
|
|
11
|
+
import { QueryService, QueryOptions } from './query-service';
|
|
12
|
+
/**
|
|
13
|
+
* Query execution plan
|
|
14
|
+
*/
|
|
15
|
+
export interface QueryPlan {
|
|
16
|
+
/**
|
|
17
|
+
* The compiled QueryAST
|
|
18
|
+
*/
|
|
19
|
+
ast: QueryAST;
|
|
20
|
+
/**
|
|
21
|
+
* Estimated number of rows to be scanned
|
|
22
|
+
*/
|
|
23
|
+
estimatedRows?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Indexes that could be used for this query
|
|
26
|
+
*/
|
|
27
|
+
indexes: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Warnings about potential performance issues
|
|
30
|
+
*/
|
|
31
|
+
warnings: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Suggestions for optimization
|
|
34
|
+
*/
|
|
35
|
+
suggestions: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Complexity score (0-100, higher is more complex)
|
|
38
|
+
*/
|
|
39
|
+
complexity: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Query profile result with execution statistics
|
|
43
|
+
*/
|
|
44
|
+
export interface ProfileResult {
|
|
45
|
+
/**
|
|
46
|
+
* Execution time in milliseconds
|
|
47
|
+
*/
|
|
48
|
+
executionTime: number;
|
|
49
|
+
/**
|
|
50
|
+
* Number of rows scanned by the database
|
|
51
|
+
*/
|
|
52
|
+
rowsScanned: number;
|
|
53
|
+
/**
|
|
54
|
+
* Number of rows returned
|
|
55
|
+
*/
|
|
56
|
+
rowsReturned: number;
|
|
57
|
+
/**
|
|
58
|
+
* Whether an index was used
|
|
59
|
+
*/
|
|
60
|
+
indexUsed: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* The query plan
|
|
63
|
+
*/
|
|
64
|
+
plan: QueryPlan;
|
|
65
|
+
/**
|
|
66
|
+
* Efficiency ratio (rowsReturned / rowsScanned)
|
|
67
|
+
* Higher is better (1.0 is perfect, 0.0 is worst)
|
|
68
|
+
*/
|
|
69
|
+
efficiency: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Aggregated query statistics
|
|
73
|
+
*/
|
|
74
|
+
export interface QueryStats {
|
|
75
|
+
/**
|
|
76
|
+
* Total number of queries executed
|
|
77
|
+
*/
|
|
78
|
+
totalQueries: number;
|
|
79
|
+
/**
|
|
80
|
+
* Average execution time in milliseconds
|
|
81
|
+
*/
|
|
82
|
+
avgExecutionTime: number;
|
|
83
|
+
/**
|
|
84
|
+
* Slowest query execution time
|
|
85
|
+
*/
|
|
86
|
+
slowestQuery: number;
|
|
87
|
+
/**
|
|
88
|
+
* Fastest query execution time
|
|
89
|
+
*/
|
|
90
|
+
fastestQuery: number;
|
|
91
|
+
/**
|
|
92
|
+
* Queries by object
|
|
93
|
+
*/
|
|
94
|
+
byObject: Record<string, {
|
|
95
|
+
count: number;
|
|
96
|
+
avgTime: number;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Top slow queries
|
|
100
|
+
*/
|
|
101
|
+
slowQueries: Array<{
|
|
102
|
+
objectName: string;
|
|
103
|
+
executionTime: number;
|
|
104
|
+
query: UnifiedQuery;
|
|
105
|
+
timestamp: Date;
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Query Analyzer
|
|
110
|
+
*
|
|
111
|
+
* Provides query performance analysis and profiling capabilities.
|
|
112
|
+
* This class helps developers optimize queries by:
|
|
113
|
+
* - Analyzing query plans
|
|
114
|
+
* - Profiling execution performance
|
|
115
|
+
* - Tracking statistics
|
|
116
|
+
* - Providing optimization suggestions
|
|
117
|
+
*/
|
|
118
|
+
export declare class QueryAnalyzer {
|
|
119
|
+
private queryService;
|
|
120
|
+
private metadata;
|
|
121
|
+
private stats;
|
|
122
|
+
private executionTimes;
|
|
123
|
+
private readonly MAX_SLOW_QUERIES;
|
|
124
|
+
constructor(queryService: QueryService, metadata: MetadataRegistry);
|
|
125
|
+
/**
|
|
126
|
+
* Analyze a query and generate an execution plan
|
|
127
|
+
*
|
|
128
|
+
* @param objectName - The object to query
|
|
129
|
+
* @param query - The unified query
|
|
130
|
+
* @returns Query plan with optimization suggestions
|
|
131
|
+
*/
|
|
132
|
+
explain(objectName: string, query: UnifiedQuery): Promise<QueryPlan>;
|
|
133
|
+
/**
|
|
134
|
+
* Profile a query execution
|
|
135
|
+
*
|
|
136
|
+
* @param objectName - The object to query
|
|
137
|
+
* @param query - The unified query
|
|
138
|
+
* @param options - Query execution options
|
|
139
|
+
* @returns Profile result with execution statistics
|
|
140
|
+
*/
|
|
141
|
+
profile(objectName: string, query: UnifiedQuery, options?: QueryOptions): Promise<ProfileResult>;
|
|
142
|
+
/**
|
|
143
|
+
* Get aggregated query statistics
|
|
144
|
+
*
|
|
145
|
+
* @returns Current query statistics
|
|
146
|
+
*/
|
|
147
|
+
getStatistics(): QueryStats;
|
|
148
|
+
/**
|
|
149
|
+
* Reset statistics
|
|
150
|
+
*/
|
|
151
|
+
resetStatistics(): void;
|
|
152
|
+
/**
|
|
153
|
+
* Get schema for an object
|
|
154
|
+
* @private
|
|
155
|
+
*/
|
|
156
|
+
private getSchema;
|
|
157
|
+
/**
|
|
158
|
+
* Find indexes that could be used for the query
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
private findApplicableIndexes;
|
|
162
|
+
/**
|
|
163
|
+
* Analyze query for potential issues
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
private analyzeWarnings;
|
|
167
|
+
/**
|
|
168
|
+
* Generate optimization suggestions
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
private generateSuggestions;
|
|
172
|
+
/**
|
|
173
|
+
* Calculate query complexity score (0-100)
|
|
174
|
+
* @private
|
|
175
|
+
*/
|
|
176
|
+
private calculateComplexity;
|
|
177
|
+
/**
|
|
178
|
+
* Estimate number of rows (very rough heuristic)
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
private estimateRows;
|
|
182
|
+
/**
|
|
183
|
+
* Record a query execution for statistics
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
private recordExecution;
|
|
187
|
+
}
|
|
188
|
+
export {};
|