@acmekit/index 2.13.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/README.md +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/index.d.ts +4 -0
- package/dist/loaders/index.d.ts.map +1 -0
- package/dist/loaders/index.js +27 -0
- package/dist/loaders/index.js.map +1 -0
- package/dist/migrations/Migration20231019174230.d.ts +5 -0
- package/dist/migrations/Migration20231019174230.d.ts.map +1 -0
- package/dist/migrations/Migration20231019174230.js +12 -0
- package/dist/migrations/Migration20231019174230.js.map +1 -0
- package/dist/migrations/Migration20241209173313.d.ts +6 -0
- package/dist/migrations/Migration20241209173313.d.ts.map +1 -0
- package/dist/migrations/Migration20241209173313.js +36 -0
- package/dist/migrations/Migration20241209173313.js.map +1 -0
- package/dist/migrations/Migration20250122154720.d.ts +6 -0
- package/dist/migrations/Migration20250122154720.d.ts.map +1 -0
- package/dist/migrations/Migration20250122154720.js +16 -0
- package/dist/migrations/Migration20250122154720.js.map +1 -0
- package/dist/migrations/Migration20250127105159.d.ts +6 -0
- package/dist/migrations/Migration20250127105159.d.ts.map +1 -0
- package/dist/migrations/Migration20250127105159.js +16 -0
- package/dist/migrations/Migration20250127105159.js.map +1 -0
- package/dist/migrations/Migration20250127144442.d.ts +6 -0
- package/dist/migrations/Migration20250127144442.d.ts.map +1 -0
- package/dist/migrations/Migration20250127144442.js +16 -0
- package/dist/migrations/Migration20250127144442.js.map +1 -0
- package/dist/migrations/Migration20250128132404.d.ts +6 -0
- package/dist/migrations/Migration20250128132404.d.ts.map +1 -0
- package/dist/migrations/Migration20250128132404.js +16 -0
- package/dist/migrations/Migration20250128132404.js.map +1 -0
- package/dist/migrations/Migration20250218132404.d.ts +6 -0
- package/dist/migrations/Migration20250218132404.d.ts.map +1 -0
- package/dist/migrations/Migration20250218132404.js +48 -0
- package/dist/migrations/Migration20250218132404.js.map +1 -0
- package/dist/migrations/Migration20250515161913.d.ts +6 -0
- package/dist/migrations/Migration20250515161913.d.ts.map +1 -0
- package/dist/migrations/Migration20250515161913.js +62 -0
- package/dist/migrations/Migration20250515161913.js.map +1 -0
- package/dist/models/index-data.d.ts +8 -0
- package/dist/models/index-data.d.ts.map +1 -0
- package/dist/models/index-data.js +12 -0
- package/dist/models/index-data.js.map +1 -0
- package/dist/models/index-metadata.d.ts +10 -0
- package/dist/models/index-metadata.d.ts.map +1 -0
- package/dist/models/index-metadata.js +23 -0
- package/dist/models/index-metadata.js.map +1 -0
- package/dist/models/index-relation.d.ts +11 -0
- package/dist/models/index-relation.d.ts.map +1 -0
- package/dist/models/index-relation.js +14 -0
- package/dist/models/index-relation.js.map +1 -0
- package/dist/models/index-sync.d.ts +7 -0
- package/dist/models/index-sync.d.ts.map +1 -0
- package/dist/models/index-sync.js +18 -0
- package/dist/models/index-sync.js.map +1 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +15 -0
- package/dist/models/index.js.map +1 -0
- package/dist/services/data-synchronizer.d.ts +35 -0
- package/dist/services/data-synchronizer.d.ts.map +1 -0
- package/dist/services/data-synchronizer.js +238 -0
- package/dist/services/data-synchronizer.js.map +1 -0
- package/dist/services/index-data.d.ts +8 -0
- package/dist/services/index-data.d.ts.map +1 -0
- package/dist/services/index-data.js +9 -0
- package/dist/services/index-data.js.map +1 -0
- package/dist/services/index-metadata.d.ts +8 -0
- package/dist/services/index-metadata.d.ts.map +1 -0
- package/dist/services/index-metadata.js +9 -0
- package/dist/services/index-metadata.js.map +1 -0
- package/dist/services/index-module-service.d.ts +75 -0
- package/dist/services/index-module-service.d.ts.map +1 -0
- package/dist/services/index-module-service.js +340 -0
- package/dist/services/index-module-service.js.map +1 -0
- package/dist/services/index-relation.d.ts +8 -0
- package/dist/services/index-relation.d.ts.map +1 -0
- package/dist/services/index-relation.js +9 -0
- package/dist/services/index-relation.js.map +1 -0
- package/dist/services/index-sync.d.ts +8 -0
- package/dist/services/index-sync.d.ts.map +1 -0
- package/dist/services/index-sync.js +9 -0
- package/dist/services/index-sync.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +9 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/postgres-provider.d.ts +132 -0
- package/dist/services/postgres-provider.d.ts.map +1 -0
- package/dist/services/postgres-provider.js +516 -0
- package/dist/services/postgres-provider.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/base-graphql-schema.d.ts +2 -0
- package/dist/utils/base-graphql-schema.d.ts.map +1 -0
- package/dist/utils/base-graphql-schema.js +10 -0
- package/dist/utils/base-graphql-schema.js.map +1 -0
- package/dist/utils/build-config.d.ts +28 -0
- package/dist/utils/build-config.d.ts.map +1 -0
- package/dist/utils/build-config.js +840 -0
- package/dist/utils/build-config.js.map +1 -0
- package/dist/utils/create-partitions.d.ts +4 -0
- package/dist/utils/create-partitions.d.ts.map +1 -0
- package/dist/utils/create-partitions.js +80 -0
- package/dist/utils/create-partitions.js.map +1 -0
- package/dist/utils/default-schema.d.ts +2 -0
- package/dist/utils/default-schema.d.ts.map +1 -0
- package/dist/utils/default-schema.js +41 -0
- package/dist/utils/default-schema.js.map +1 -0
- package/dist/utils/flatten-object-keys.d.ts +23 -0
- package/dist/utils/flatten-object-keys.d.ts.map +1 -0
- package/dist/utils/flatten-object-keys.js +54 -0
- package/dist/utils/flatten-object-keys.js.map +1 -0
- package/dist/utils/gql-to-types.d.ts +3 -0
- package/dist/utils/gql-to-types.d.ts.map +1 -0
- package/dist/utils/gql-to-types.js +98 -0
- package/dist/utils/gql-to-types.js.map +1 -0
- package/dist/utils/index-metadata-status.d.ts +7 -0
- package/dist/utils/index-metadata-status.d.ts.map +1 -0
- package/dist/utils/index-metadata-status.js +11 -0
- package/dist/utils/index-metadata-status.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/normalize-fields-selection.d.ts +2 -0
- package/dist/utils/normalize-fields-selection.d.ts.map +1 -0
- package/dist/utils/normalize-fields-selection.js +10 -0
- package/dist/utils/normalize-fields-selection.js.map +1 -0
- package/dist/utils/normalze-table-name.d.ts +3 -0
- package/dist/utils/normalze-table-name.d.ts.map +1 -0
- package/dist/utils/normalze-table-name.js +13 -0
- package/dist/utils/normalze-table-name.js.map +1 -0
- package/dist/utils/query-builder.d.ts +61 -0
- package/dist/utils/query-builder.d.ts.map +1 -0
- package/dist/utils/query-builder.js +791 -0
- package/dist/utils/query-builder.js.map +1 -0
- package/dist/utils/sync/configuration.d.ts +17 -0
- package/dist/utils/sync/configuration.d.ts.map +1 -0
- package/dist/utils/sync/configuration.js +114 -0
- package/dist/utils/sync/configuration.js.map +1 -0
- package/dist/utils/sync/orchestrator.d.ts +33 -0
- package/dist/utils/sync/orchestrator.d.ts.map +1 -0
- package/dist/utils/sync/orchestrator.js +195 -0
- package/dist/utils/sync/orchestrator.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _QueryBuilder_searchVectorColumnName;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.QueryBuilder = exports.OPERATOR_MAP = void 0;
|
|
10
|
+
const utils_1 = require("@acmekit/framework/utils");
|
|
11
|
+
const normalze_table_name_1 = require("./normalze-table-name");
|
|
12
|
+
const AND_OPERATOR = "$and";
|
|
13
|
+
const OR_OPERATOR = "$or";
|
|
14
|
+
const NOT_OPERATOR = "$not";
|
|
15
|
+
function escapeJsonPathString(val) {
|
|
16
|
+
// Escape for JSONPath string
|
|
17
|
+
return val.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'");
|
|
18
|
+
}
|
|
19
|
+
function buildSafeJsonPathQuery(field, operator, value) {
|
|
20
|
+
let jsonPathOperator = operator;
|
|
21
|
+
let caseInsensitiveFlag = "";
|
|
22
|
+
if (operator === "=") {
|
|
23
|
+
jsonPathOperator = "==";
|
|
24
|
+
}
|
|
25
|
+
else if (operator.toUpperCase().includes("LIKE")) {
|
|
26
|
+
jsonPathOperator = "like_regex";
|
|
27
|
+
if (operator.toUpperCase() === "ILIKE") {
|
|
28
|
+
caseInsensitiveFlag = ' flag "i"';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (operator === "IS") {
|
|
32
|
+
jsonPathOperator = "==";
|
|
33
|
+
}
|
|
34
|
+
else if (operator === "IS NOT") {
|
|
35
|
+
jsonPathOperator = "!=";
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "string") {
|
|
38
|
+
let val = value;
|
|
39
|
+
if (jsonPathOperator === "like_regex") {
|
|
40
|
+
// Convert SQL LIKE wildcards to regex
|
|
41
|
+
val = val.replace(/%/g, ".*").replace(/_/g, ".");
|
|
42
|
+
}
|
|
43
|
+
value = `"${escapeJsonPathString(val)}"`;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if ((operator === "IS" || operator === "IS NOT") && value === null) {
|
|
47
|
+
value = "null";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return `$.${field} ${jsonPathOperator} ${value}${caseInsensitiveFlag}`;
|
|
51
|
+
}
|
|
52
|
+
exports.OPERATOR_MAP = {
|
|
53
|
+
$eq: "=",
|
|
54
|
+
$lt: "<",
|
|
55
|
+
$gt: ">",
|
|
56
|
+
$lte: "<=",
|
|
57
|
+
$gte: ">=",
|
|
58
|
+
$ne: "!=",
|
|
59
|
+
$in: "IN",
|
|
60
|
+
$nin: "NOT IN",
|
|
61
|
+
$is: "IS",
|
|
62
|
+
$like: "LIKE",
|
|
63
|
+
$ilike: "ILIKE",
|
|
64
|
+
};
|
|
65
|
+
class QueryBuilder {
|
|
66
|
+
constructor(args) {
|
|
67
|
+
_QueryBuilder_searchVectorColumnName.set(this, "document_tsv");
|
|
68
|
+
this.schema = args.schema;
|
|
69
|
+
this.entityMap = args.entityMap;
|
|
70
|
+
this.selector = args.selector;
|
|
71
|
+
this.options = args.options;
|
|
72
|
+
this.knex = args.knex;
|
|
73
|
+
this.structure = this.selector.select;
|
|
74
|
+
this.allSchemaFields = new Set(Object.values(this.schema).flatMap((entity) => entity.fields ?? []));
|
|
75
|
+
this.rawConfig = args.rawConfig;
|
|
76
|
+
this.requestedFields = args.requestedFields;
|
|
77
|
+
this.idsOnly = args.idsOnly ?? false;
|
|
78
|
+
}
|
|
79
|
+
isLogicalOperator(key) {
|
|
80
|
+
return key === AND_OPERATOR || key === OR_OPERATOR || key === NOT_OPERATOR;
|
|
81
|
+
}
|
|
82
|
+
getStructureKeys(structure) {
|
|
83
|
+
const collectKeys = (obj, keys = new Set()) => {
|
|
84
|
+
if (!(0, utils_1.isObject)(obj)) {
|
|
85
|
+
return keys;
|
|
86
|
+
}
|
|
87
|
+
Object.keys(obj).forEach((key) => {
|
|
88
|
+
if (this.isLogicalOperator(key)) {
|
|
89
|
+
if (Array.isArray(obj[key])) {
|
|
90
|
+
obj[key].forEach((item) => collectKeys(item, keys));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (key !== "entity") {
|
|
94
|
+
keys.add(key);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return keys;
|
|
98
|
+
};
|
|
99
|
+
return [...collectKeys(structure ?? {})];
|
|
100
|
+
}
|
|
101
|
+
getEntity(path, throwWhenNotFound = true) {
|
|
102
|
+
if (!this.schema._schemaPropertiesMap[path]) {
|
|
103
|
+
if (!throwWhenNotFound) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Could not find entity for path: ${path}. It might not be indexed.`);
|
|
107
|
+
}
|
|
108
|
+
return this.schema._schemaPropertiesMap[path];
|
|
109
|
+
}
|
|
110
|
+
getGraphQLType(path, field) {
|
|
111
|
+
if (this.isLogicalOperator(field)) {
|
|
112
|
+
return "JSON";
|
|
113
|
+
}
|
|
114
|
+
const entity = this.getEntity(path)?.ref?.entity;
|
|
115
|
+
const fieldRef = this.entityMap[entity]._fields[field];
|
|
116
|
+
if (!fieldRef) {
|
|
117
|
+
throw new Error(`Field ${field} is not indexed.`);
|
|
118
|
+
}
|
|
119
|
+
const fieldType = fieldRef.type.toString();
|
|
120
|
+
const isArray = fieldType.startsWith("[");
|
|
121
|
+
const currentType = fieldType.replace(/\[|\]|\!/g, "");
|
|
122
|
+
return currentType + (isArray ? "[]" : "");
|
|
123
|
+
}
|
|
124
|
+
transformValueToType(path, field, value) {
|
|
125
|
+
if (value === null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const typeToFn = {
|
|
129
|
+
Int: (val) => parseInt(val, 10),
|
|
130
|
+
Float: (val) => parseFloat(val),
|
|
131
|
+
String: (val) => String(val),
|
|
132
|
+
Boolean: (val) => Boolean(val),
|
|
133
|
+
ID: (val) => String(val),
|
|
134
|
+
Date: (val) => new Date(val).toISOString(),
|
|
135
|
+
DateTime: (val) => new Date(val).toISOString(),
|
|
136
|
+
Time: (val) => new Date(`1970-01-01T${val}Z`).toISOString(),
|
|
137
|
+
};
|
|
138
|
+
const fullPath = [path, ...field];
|
|
139
|
+
const prop = fullPath.pop();
|
|
140
|
+
const fieldPath = fullPath.join(".");
|
|
141
|
+
const graphqlType = this.getGraphQLType(fieldPath, prop).replace("[]", "");
|
|
142
|
+
const fn = typeToFn[graphqlType];
|
|
143
|
+
if (Array.isArray(value)) {
|
|
144
|
+
return value.map((v) => (!fn ? v : fn(v)));
|
|
145
|
+
}
|
|
146
|
+
return !fn ? value : fn(value);
|
|
147
|
+
}
|
|
148
|
+
getPostgresCastType(path, field) {
|
|
149
|
+
const graphqlToPostgresTypeMap = {
|
|
150
|
+
Int: "::int",
|
|
151
|
+
Float: "::double precision",
|
|
152
|
+
Boolean: "::boolean",
|
|
153
|
+
Date: "::timestamp",
|
|
154
|
+
DateTime: "::timestamp",
|
|
155
|
+
Time: "::time",
|
|
156
|
+
"": "",
|
|
157
|
+
};
|
|
158
|
+
const defaultValues = {
|
|
159
|
+
Int: "0",
|
|
160
|
+
Float: "0",
|
|
161
|
+
Boolean: "false",
|
|
162
|
+
Date: "1970-01-01 00:00:00",
|
|
163
|
+
DateTime: "1970-01-01 00:00:00",
|
|
164
|
+
Time: "00:00:00",
|
|
165
|
+
"": "",
|
|
166
|
+
};
|
|
167
|
+
const fullPath = [path, ...field];
|
|
168
|
+
const prop = fullPath.pop();
|
|
169
|
+
const fieldPath = fullPath.join(".");
|
|
170
|
+
let graphqlType = this.getGraphQLType(fieldPath, prop);
|
|
171
|
+
const isList = graphqlType.endsWith("[]");
|
|
172
|
+
graphqlType = graphqlType.replace("[]", "");
|
|
173
|
+
const cast = (graphqlToPostgresTypeMap[graphqlType] ?? "") + (isList ? "[]" : "");
|
|
174
|
+
function generateCoalesceExpression(field) {
|
|
175
|
+
const defaultValue = defaultValues[graphqlType];
|
|
176
|
+
return `COALESCE(${field}, '${defaultValue}')${cast}`;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
cast,
|
|
180
|
+
coalesce: generateCoalesceExpression,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
parseWhere(aliasMapping, obj, builder, parentPath = "") {
|
|
184
|
+
const keys = Object.keys(obj);
|
|
185
|
+
const getPathAndField = (key) => {
|
|
186
|
+
const fullKey = parentPath ? `${parentPath}.${key}` : key;
|
|
187
|
+
const path = fullKey.split(".");
|
|
188
|
+
const field = [path.pop()];
|
|
189
|
+
while (!aliasMapping[path.join(".")] && path.length > 0) {
|
|
190
|
+
field.unshift(path.pop());
|
|
191
|
+
}
|
|
192
|
+
const attr = path.join(".");
|
|
193
|
+
return { field, attr };
|
|
194
|
+
};
|
|
195
|
+
const getPathOperation = (attr, path, value) => {
|
|
196
|
+
const partialPath = path.length > 1 ? path.slice(0, -1) : path;
|
|
197
|
+
const val = this.transformValueToType(attr, partialPath, value);
|
|
198
|
+
const result = path.reduceRight((acc, key) => {
|
|
199
|
+
return { [key]: acc };
|
|
200
|
+
}, val);
|
|
201
|
+
return JSON.stringify(result);
|
|
202
|
+
};
|
|
203
|
+
keys.forEach((key) => {
|
|
204
|
+
const pathAsArray = (parentPath ? `${parentPath}.${key}` : key).split(".");
|
|
205
|
+
const fieldOrLogicalOperator = pathAsArray.pop();
|
|
206
|
+
let value = obj[key];
|
|
207
|
+
if (this.isLogicalOperator(fieldOrLogicalOperator) &&
|
|
208
|
+
!Array.isArray(value)) {
|
|
209
|
+
value = [value];
|
|
210
|
+
}
|
|
211
|
+
if (fieldOrLogicalOperator === AND_OPERATOR && Array.isArray(value)) {
|
|
212
|
+
builder.where((qb) => {
|
|
213
|
+
value.forEach((cond) => {
|
|
214
|
+
qb.andWhere((subBuilder) => this.parseWhere(aliasMapping, cond, subBuilder, pathAsArray.join(".")));
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else if (fieldOrLogicalOperator === OR_OPERATOR &&
|
|
219
|
+
Array.isArray(value)) {
|
|
220
|
+
builder.where((qb) => {
|
|
221
|
+
value.forEach((cond) => {
|
|
222
|
+
qb.orWhere((subBuilder) => this.parseWhere(aliasMapping, cond, subBuilder, pathAsArray.join(".")));
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
else if (fieldOrLogicalOperator === NOT_OPERATOR &&
|
|
227
|
+
(Array.isArray(value) || (0, utils_1.isObject)(value))) {
|
|
228
|
+
builder.whereNot((qb) => {
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
value.forEach((cond) => {
|
|
231
|
+
qb.andWhere((subBuilder) => this.parseWhere(aliasMapping, cond, subBuilder, pathAsArray.join(".")));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.parseWhere(aliasMapping, value, qb, pathAsArray.join("."));
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else if ((0, utils_1.isObject)(value) &&
|
|
240
|
+
!Array.isArray(value) &&
|
|
241
|
+
!this.isLogicalOperator(fieldOrLogicalOperator)) {
|
|
242
|
+
const currentPath = parentPath ? `${parentPath}.${key}` : key;
|
|
243
|
+
const subKeys = Object.keys(value);
|
|
244
|
+
const hasOperators = subKeys.some((subKey) => exports.OPERATOR_MAP[subKey]);
|
|
245
|
+
if (hasOperators) {
|
|
246
|
+
const { field, attr } = getPathAndField(key);
|
|
247
|
+
const subKeys = Object.keys(value);
|
|
248
|
+
subKeys.forEach((subKey) => {
|
|
249
|
+
let operator = exports.OPERATOR_MAP[subKey];
|
|
250
|
+
if (operator) {
|
|
251
|
+
const nested = new Array(field.length).join("->?");
|
|
252
|
+
const subValue = this.transformValueToType(attr, field, value[subKey]);
|
|
253
|
+
let val = operator === "IN" || operator === "NOT IN"
|
|
254
|
+
? subValue
|
|
255
|
+
: [subValue];
|
|
256
|
+
if (operator === "=" && subValue === null) {
|
|
257
|
+
operator = "IS";
|
|
258
|
+
}
|
|
259
|
+
else if (operator === "!=" && subValue === null) {
|
|
260
|
+
operator = "IS NOT";
|
|
261
|
+
}
|
|
262
|
+
if (operator === "=") {
|
|
263
|
+
const hasId = field[field.length - 1] === "id";
|
|
264
|
+
if (hasId) {
|
|
265
|
+
builder.whereRaw(`${aliasMapping[attr]}.id = ?`, subValue);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
builder.whereRaw(`${aliasMapping[attr]}.data @> '${getPathOperation(attr, field, subValue)}'::jsonb`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else if (operator === "IN" || operator === "NOT IN") {
|
|
272
|
+
if (val && !Array.isArray(val)) {
|
|
273
|
+
val = [val];
|
|
274
|
+
}
|
|
275
|
+
if (!val || val.length === 0) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const inPlaceholders = val.map(() => "?").join(",");
|
|
279
|
+
const hasId = field[field.length - 1] === "id";
|
|
280
|
+
const isNegated = operator === "NOT IN";
|
|
281
|
+
if (hasId) {
|
|
282
|
+
builder.whereRaw(`${aliasMapping[attr]}.id ${isNegated ? "NOT IN" : "IN"} (${inPlaceholders})`, val);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const targetField = field[field.length - 1];
|
|
286
|
+
const jsonbValues = val.map((item) => JSON.stringify({
|
|
287
|
+
[targetField]: item === null ? null : item,
|
|
288
|
+
}));
|
|
289
|
+
if (isNegated) {
|
|
290
|
+
builder.whereRaw(`NOT EXISTS (SELECT 1 FROM unnest(ARRAY[${inPlaceholders}]::JSONB[]) AS v(val) WHERE ${aliasMapping[attr]}.data${nested} @> v.val)`, jsonbValues);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @> ANY(ARRAY[${inPlaceholders}]::JSONB[])`, jsonbValues);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
const potentialIdFields = field[field.length - 1];
|
|
299
|
+
const hasId = potentialIdFields === "id";
|
|
300
|
+
if (hasId) {
|
|
301
|
+
builder.whereRaw(`(${aliasMapping[attr]}.id) ${operator} ?`, [
|
|
302
|
+
...val,
|
|
303
|
+
]);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
const targetField = field[field.length - 1];
|
|
307
|
+
const jsonPath = buildSafeJsonPathQuery(targetField, operator, val[0]);
|
|
308
|
+
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @@ ?`, [
|
|
309
|
+
jsonPath,
|
|
310
|
+
]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
throw new Error(`Unsupported operator: ${subKey}`);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.parseWhere(aliasMapping, value, builder, currentPath);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
const { field, attr } = getPathAndField(key);
|
|
325
|
+
const nested = new Array(field.length).join("->?");
|
|
326
|
+
value = this.transformValueToType(attr, field, value);
|
|
327
|
+
if (Array.isArray(value)) {
|
|
328
|
+
if (value.length === 0) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const inPlaceholders = value.map(() => "?").join(",");
|
|
332
|
+
const hasId = field[field.length - 1] === "id";
|
|
333
|
+
if (hasId) {
|
|
334
|
+
builder.whereRaw(`${aliasMapping[attr]}.id IN (${inPlaceholders})`, [...value]);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
const targetField = field[field.length - 1];
|
|
338
|
+
const jsonbValues = value.map((item) => JSON.stringify({ [targetField]: item === null ? null : item }));
|
|
339
|
+
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @> ANY(ARRAY[${inPlaceholders}]::JSONB[])`, jsonbValues);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else if ((0, utils_1.isDefined)(value)) {
|
|
343
|
+
let operator = "=";
|
|
344
|
+
if (operator === "=") {
|
|
345
|
+
const hasId = field[field.length - 1] === "id";
|
|
346
|
+
if (hasId) {
|
|
347
|
+
builder.whereRaw(`${aliasMapping[attr]}.id = ?`, value);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
builder.whereRaw(`${aliasMapping[attr]}.data @> '${getPathOperation(attr, field, value)}'::jsonb`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
if (value === null) {
|
|
355
|
+
operator = "IS";
|
|
356
|
+
}
|
|
357
|
+
const hasId = field[field.length - 1] === "id";
|
|
358
|
+
if (hasId) {
|
|
359
|
+
builder.whereRaw(`(${aliasMapping[attr]}.id) ${operator} ?`, [
|
|
360
|
+
value,
|
|
361
|
+
]);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
const targetField = field[field.length - 1];
|
|
365
|
+
const jsonPath = buildSafeJsonPathQuery(targetField, operator, value);
|
|
366
|
+
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @@ ?`, [
|
|
367
|
+
jsonPath,
|
|
368
|
+
]);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
return builder;
|
|
375
|
+
}
|
|
376
|
+
getShortAlias(aliasMapping, alias, level = 0) {
|
|
377
|
+
aliasMapping.__aliasIndex ??= 0;
|
|
378
|
+
if (aliasMapping[alias]) {
|
|
379
|
+
return aliasMapping[alias];
|
|
380
|
+
}
|
|
381
|
+
aliasMapping[alias] =
|
|
382
|
+
"t_" + aliasMapping.__aliasIndex++ + (level > 0 ? `_${level}` : "");
|
|
383
|
+
return aliasMapping[alias];
|
|
384
|
+
}
|
|
385
|
+
buildQueryParts(structure, parentAlias, parentEntity, parentProperty, aliasPath = [], level = 0, aliasMapping = {}) {
|
|
386
|
+
const currentAliasPath = [...aliasPath, parentProperty].join(".");
|
|
387
|
+
const isSelectableField = this.allSchemaFields.has(parentProperty);
|
|
388
|
+
const entities = this.getEntity(currentAliasPath, false);
|
|
389
|
+
// !entityRef.alias means the object has not table, it's a nested object
|
|
390
|
+
if (isSelectableField || !entities || !entities?.ref?.alias) {
|
|
391
|
+
// We are currently selecting a specific field of the parent entity or the entity is not found on the index schema
|
|
392
|
+
// We don't need to build the query parts for this as there is no join
|
|
393
|
+
return [];
|
|
394
|
+
}
|
|
395
|
+
const mainEntity = entities;
|
|
396
|
+
const mainAlias = this.getShortAlias(aliasMapping, mainEntity.ref.entity.toLowerCase(), level);
|
|
397
|
+
const allEntities = [];
|
|
398
|
+
if (!entities.shortCutOf) {
|
|
399
|
+
allEntities.push({
|
|
400
|
+
entity: entities,
|
|
401
|
+
parEntity: parentEntity,
|
|
402
|
+
parAlias: parentAlias,
|
|
403
|
+
alias: mainAlias,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
const intermediateAlias = entities.shortCutOf.split(".");
|
|
408
|
+
for (let i = intermediateAlias.length - 1, x = 0; i >= 0; i--, x++) {
|
|
409
|
+
const intermediateEntity = this.getEntity(intermediateAlias.join("."), false);
|
|
410
|
+
if (!intermediateEntity) {
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
intermediateAlias.pop();
|
|
414
|
+
if (intermediateEntity.ref.entity === parentEntity?.ref.entity) {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
const parentIntermediateEntity = this.getEntity(intermediateAlias.join("."));
|
|
418
|
+
const alias = this.getShortAlias(aliasMapping, intermediateEntity.ref.entity.toLowerCase(), level) +
|
|
419
|
+
"_" +
|
|
420
|
+
x;
|
|
421
|
+
const parAlias = parentIntermediateEntity.ref.entity === parentEntity?.ref.entity
|
|
422
|
+
? parentAlias
|
|
423
|
+
: this.getShortAlias(aliasMapping, parentIntermediateEntity.ref.entity.toLowerCase(), level) +
|
|
424
|
+
"_" +
|
|
425
|
+
(x + 1);
|
|
426
|
+
if (x === 0) {
|
|
427
|
+
aliasMapping[currentAliasPath] = alias;
|
|
428
|
+
}
|
|
429
|
+
allEntities.unshift({
|
|
430
|
+
entity: intermediateEntity,
|
|
431
|
+
parEntity: parentIntermediateEntity,
|
|
432
|
+
parAlias,
|
|
433
|
+
alias,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
let queryParts = [];
|
|
438
|
+
for (const join of allEntities) {
|
|
439
|
+
const joinBuilder = this.knex.queryBuilder();
|
|
440
|
+
const { alias, entity, parEntity, parAlias } = join;
|
|
441
|
+
aliasMapping[currentAliasPath] = alias;
|
|
442
|
+
if (level > 0) {
|
|
443
|
+
const cName = (0, normalze_table_name_1.normalizeTableName)(entity.ref.entity);
|
|
444
|
+
let joinTable = `cat_${cName} AS ${alias}`;
|
|
445
|
+
if (entity.isInverse || parEntity.isInverse) {
|
|
446
|
+
const pName = `${entity.ref.entity}${parEntity.ref.entity}`.toLowerCase();
|
|
447
|
+
const pivotTable = (0, normalze_table_name_1.getPivotTableName)(pName);
|
|
448
|
+
joinBuilder.leftJoin(`${pivotTable} AS ${alias}_ref`, `${alias}_ref.child_id`, `${parAlias}.id`);
|
|
449
|
+
joinBuilder.leftJoin(joinTable, `${alias}.id`, `${alias}_ref.parent_id`);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
const pName = `${parEntity.ref.entity}${entity.ref.entity}`.toLowerCase();
|
|
453
|
+
const pivotTable = (0, normalze_table_name_1.getPivotTableName)(pName);
|
|
454
|
+
joinBuilder.leftJoin(`${pivotTable} AS ${alias}_ref`, `${alias}_ref.parent_id`, `${parAlias}.id`);
|
|
455
|
+
joinBuilder.leftJoin(joinTable, `${alias}.id`, `${alias}_ref.child_id`);
|
|
456
|
+
}
|
|
457
|
+
const joinWhere = this.selector.joinWhere ?? {};
|
|
458
|
+
const joinKey = Object.keys(joinWhere).find((key) => {
|
|
459
|
+
const k = key.split(".");
|
|
460
|
+
k.pop();
|
|
461
|
+
const curPath = k.join(".");
|
|
462
|
+
if (curPath === currentAliasPath) {
|
|
463
|
+
const relEntity = this.getEntity(curPath, false);
|
|
464
|
+
return relEntity?.ref?.entity === entity.ref.entity;
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
});
|
|
468
|
+
if (joinKey) {
|
|
469
|
+
this.parseWhere(aliasMapping, { [joinKey]: joinWhere[joinKey] }, joinBuilder);
|
|
470
|
+
}
|
|
471
|
+
queryParts.push(joinBuilder.toQuery().replace("select * ", "").replace("where", "and"));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const children = this.getStructureKeys(structure);
|
|
475
|
+
for (const child of children) {
|
|
476
|
+
const childStructure = structure[child];
|
|
477
|
+
queryParts = queryParts
|
|
478
|
+
.concat(this.buildQueryParts(childStructure, mainAlias, mainEntity, child, aliasPath.concat(parentProperty), level + 1, aliasMapping))
|
|
479
|
+
.filter(Boolean);
|
|
480
|
+
}
|
|
481
|
+
return queryParts;
|
|
482
|
+
}
|
|
483
|
+
buildSelectParts(structure, parentProperty, aliasMapping, aliasPath = [], selectParts = {}) {
|
|
484
|
+
const currentAliasPath = [...aliasPath, parentProperty].join(".");
|
|
485
|
+
const isSelectableField = this.allSchemaFields.has(parentProperty);
|
|
486
|
+
if (isSelectableField) {
|
|
487
|
+
// We are currently selecting a specific field of the parent entity
|
|
488
|
+
// Let's remove the parent alias from the select parts to not select everything entirely
|
|
489
|
+
// and add the specific field to the select parts
|
|
490
|
+
const parentAliasPath = aliasPath.join(".");
|
|
491
|
+
const alias = aliasMapping[parentAliasPath];
|
|
492
|
+
delete selectParts[parentAliasPath];
|
|
493
|
+
if (parentProperty === "id") {
|
|
494
|
+
selectParts[currentAliasPath] = `${alias}.id`;
|
|
495
|
+
}
|
|
496
|
+
else if (!this.idsOnly) {
|
|
497
|
+
selectParts[currentAliasPath] = this.knex.raw(`${alias}.data->'${parentProperty}'`);
|
|
498
|
+
}
|
|
499
|
+
return selectParts;
|
|
500
|
+
}
|
|
501
|
+
const alias = aliasMapping[currentAliasPath];
|
|
502
|
+
// If the entity is not found in the schema (not indexed), we don't need to build the select parts
|
|
503
|
+
if (!alias) {
|
|
504
|
+
return selectParts;
|
|
505
|
+
}
|
|
506
|
+
if (!this.idsOnly) {
|
|
507
|
+
selectParts[currentAliasPath] = `${alias}.data`;
|
|
508
|
+
}
|
|
509
|
+
selectParts[currentAliasPath + ".id"] = `${alias}.id`;
|
|
510
|
+
const children = this.getStructureKeys(structure);
|
|
511
|
+
for (const child of children) {
|
|
512
|
+
const childStructure = structure[child];
|
|
513
|
+
this.buildSelectParts(childStructure, child, aliasMapping, aliasPath.concat(parentProperty), selectParts);
|
|
514
|
+
}
|
|
515
|
+
return selectParts;
|
|
516
|
+
}
|
|
517
|
+
transformOrderBy(arr) {
|
|
518
|
+
const result = {};
|
|
519
|
+
const map = new Map();
|
|
520
|
+
map.set(true, "ASC");
|
|
521
|
+
map.set(1, "ASC");
|
|
522
|
+
map.set("ASC", "ASC");
|
|
523
|
+
map.set(false, "DESC");
|
|
524
|
+
map.set(-1, "DESC");
|
|
525
|
+
map.set("DESC", "DESC");
|
|
526
|
+
function nested(obj, prefix = "") {
|
|
527
|
+
const keys = Object.keys(obj);
|
|
528
|
+
if (!keys.length) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
else if (keys.length > 1) {
|
|
532
|
+
throw new Error("Order by only supports one key per object.");
|
|
533
|
+
}
|
|
534
|
+
const key = keys[0];
|
|
535
|
+
let value = obj[key];
|
|
536
|
+
if ((0, utils_1.isObject)(value)) {
|
|
537
|
+
nested(value, prefix + key + ".");
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
if ((0, utils_1.isString)(value)) {
|
|
541
|
+
value = value.toUpperCase();
|
|
542
|
+
}
|
|
543
|
+
result[prefix + key] = map.get(value) ?? "ASC";
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
arr.forEach((obj) => nested(obj));
|
|
547
|
+
return result;
|
|
548
|
+
}
|
|
549
|
+
buildQuery({ hasPagination = true, hasCount = false, }) {
|
|
550
|
+
const selectOnlyStructure = this.selector.select;
|
|
551
|
+
const structure = this.requestedFields;
|
|
552
|
+
const filter = this.selector.where ?? {};
|
|
553
|
+
const { orderBy: order, skip, take } = this.options ?? {};
|
|
554
|
+
const orderBy = this.transformOrderBy((order && !Array.isArray(order) ? [order] : order) ?? []);
|
|
555
|
+
const take_ = !isNaN(+take) ? +take : 15;
|
|
556
|
+
const skip_ = !isNaN(+skip) ? +skip : 0;
|
|
557
|
+
const rootKey = this.getStructureKeys(structure)[0];
|
|
558
|
+
const rootStructure = structure[rootKey];
|
|
559
|
+
const entity = this.getEntity(rootKey);
|
|
560
|
+
const rootEntity = entity.ref.entity.toLowerCase();
|
|
561
|
+
const aliasMapping = {};
|
|
562
|
+
let hasTextSearch = false;
|
|
563
|
+
let textSearchQuery = null;
|
|
564
|
+
const searchQueryFilterProp = `${rootKey}.q`;
|
|
565
|
+
if (searchQueryFilterProp in filter) {
|
|
566
|
+
if (!filter[searchQueryFilterProp]) {
|
|
567
|
+
delete filter[searchQueryFilterProp];
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
hasTextSearch = true;
|
|
571
|
+
textSearchQuery = filter[searchQueryFilterProp];
|
|
572
|
+
delete filter[searchQueryFilterProp];
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
const filterSortStructure = (0, utils_1.unflattenObjectKeys)({
|
|
576
|
+
...(this.rawConfig?.filters
|
|
577
|
+
? (0, utils_1.unflattenObjectKeys)(this.rawConfig?.filters)
|
|
578
|
+
: {}),
|
|
579
|
+
...orderBy,
|
|
580
|
+
})[rootKey] ?? {};
|
|
581
|
+
const joinParts = this.buildQueryParts(filterSortStructure, "", entity, rootKey, [], 0, aliasMapping);
|
|
582
|
+
const rootAlias = aliasMapping[rootKey];
|
|
583
|
+
const innerQueryBuilder = this.knex.queryBuilder();
|
|
584
|
+
// Outer query to select the full data based on the paginated IDs
|
|
585
|
+
const outerQueryBuilder = this.knex.queryBuilder();
|
|
586
|
+
innerQueryBuilder.distinct(`${rootAlias}.id`);
|
|
587
|
+
const orderBySelects = [];
|
|
588
|
+
const orderByClauses = [];
|
|
589
|
+
for (const aliasPath in orderBy) {
|
|
590
|
+
const path = aliasPath.split(".");
|
|
591
|
+
const field = path.pop();
|
|
592
|
+
const attr = path.join(".");
|
|
593
|
+
const alias = aliasMapping[attr];
|
|
594
|
+
const direction = orderBy[aliasPath];
|
|
595
|
+
const pgType = this.getPostgresCastType(attr, [field]);
|
|
596
|
+
const hasId = field === "id";
|
|
597
|
+
let orderExpression = `${rootAlias}.id ${direction}`;
|
|
598
|
+
if (alias) {
|
|
599
|
+
const aggregateAlias = `"${aliasPath}_agg"`;
|
|
600
|
+
let aggregateExpression = `(${alias}.data->>'${field}')${pgType.cast}`;
|
|
601
|
+
if (hasId) {
|
|
602
|
+
aggregateExpression = `${alias}.id`;
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
orderBySelects.push(direction === "ASC"
|
|
606
|
+
? this.knex.raw(`MIN(${aggregateExpression}) AS ${aggregateAlias}`)
|
|
607
|
+
: this.knex.raw(`MAX(${aggregateExpression}) AS ${aggregateAlias}`));
|
|
608
|
+
orderExpression = `${aggregateAlias} ${direction}`;
|
|
609
|
+
}
|
|
610
|
+
outerQueryBuilder.orderByRaw(`${aggregateExpression} ${direction}`);
|
|
611
|
+
}
|
|
612
|
+
orderByClauses.push(orderExpression);
|
|
613
|
+
}
|
|
614
|
+
// Add ordering columns to the select list of the inner query
|
|
615
|
+
if (orderBySelects.length > 0) {
|
|
616
|
+
innerQueryBuilder.select(orderBySelects);
|
|
617
|
+
}
|
|
618
|
+
innerQueryBuilder.from(`cat_${(0, normalze_table_name_1.normalizeTableName)(rootEntity)} AS ${this.getShortAlias(aliasMapping, rootKey)}`);
|
|
619
|
+
joinParts.forEach((joinPart) => {
|
|
620
|
+
innerQueryBuilder.joinRaw(joinPart);
|
|
621
|
+
});
|
|
622
|
+
if (hasTextSearch) {
|
|
623
|
+
const searchWhereParts = [
|
|
624
|
+
`${rootAlias}.${__classPrivateFieldGet(this, _QueryBuilder_searchVectorColumnName, "f")} @@ plainto_tsquery('simple', ?)`,
|
|
625
|
+
...joinParts.flatMap((part) => {
|
|
626
|
+
const aliases = part
|
|
627
|
+
.split(" as ")
|
|
628
|
+
.flatMap((chunk) => chunk.split(" on "))
|
|
629
|
+
.filter((alias) => alias.startsWith('"t_') && !alias.includes("_ref"));
|
|
630
|
+
return aliases.map((alias) => `${alias}.${__classPrivateFieldGet(this, _QueryBuilder_searchVectorColumnName, "f")} @@ plainto_tsquery('simple', ?)`);
|
|
631
|
+
}),
|
|
632
|
+
];
|
|
633
|
+
innerQueryBuilder.whereRaw(`(${searchWhereParts.join(" OR ")})`, Array(searchWhereParts.length).fill(textSearchQuery));
|
|
634
|
+
}
|
|
635
|
+
this.parseWhere(aliasMapping, filter, innerQueryBuilder);
|
|
636
|
+
// Group by root ID in the inner query
|
|
637
|
+
if (orderBySelects.length > 0) {
|
|
638
|
+
innerQueryBuilder.groupBy(`${rootAlias}.id`);
|
|
639
|
+
}
|
|
640
|
+
if (orderByClauses.length > 0) {
|
|
641
|
+
innerQueryBuilder.orderByRaw(orderByClauses.join(", "));
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
innerQueryBuilder.orderBy(`${rootAlias}.id`, "ASC");
|
|
645
|
+
}
|
|
646
|
+
// Count query to estimate the number of results in parallel
|
|
647
|
+
let countQuery;
|
|
648
|
+
if (hasCount) {
|
|
649
|
+
const estimateQuery = innerQueryBuilder.clone();
|
|
650
|
+
estimateQuery.clearSelect().select(1);
|
|
651
|
+
estimateQuery.clearOrder();
|
|
652
|
+
estimateQuery.clearCounters();
|
|
653
|
+
countQuery = this.knex.raw(`SELECT count_estimate(?) AS estimate_count`, estimateQuery.toQuery());
|
|
654
|
+
}
|
|
655
|
+
// Apply pagination to the inner query
|
|
656
|
+
if (hasPagination) {
|
|
657
|
+
innerQueryBuilder.limit(take_);
|
|
658
|
+
if (skip_ > 0) {
|
|
659
|
+
innerQueryBuilder.offset(skip_);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const innerQueryAlias = "paginated_ids";
|
|
663
|
+
outerQueryBuilder.from(`cat_${(0, normalze_table_name_1.normalizeTableName)(rootEntity)} AS ${this.getShortAlias(aliasMapping, rootKey)}`);
|
|
664
|
+
outerQueryBuilder.joinRaw(`INNER JOIN (${innerQueryBuilder.toQuery()}) AS ${innerQueryAlias} ON ${rootAlias}.id = ${innerQueryAlias}.id`);
|
|
665
|
+
this.parseWhere(aliasMapping, filter, outerQueryBuilder);
|
|
666
|
+
const joinPartsOuterQuery = this.buildQueryParts(rootStructure, "", entity, rootKey, [], 0, aliasMapping);
|
|
667
|
+
joinPartsOuterQuery.forEach((joinPart) => {
|
|
668
|
+
outerQueryBuilder.joinRaw(joinPart);
|
|
669
|
+
});
|
|
670
|
+
const finalSelectParts = this.buildSelectParts(selectOnlyStructure[rootKey], rootKey, aliasMapping);
|
|
671
|
+
outerQueryBuilder.select(finalSelectParts);
|
|
672
|
+
const finalSql = outerQueryBuilder.toQuery();
|
|
673
|
+
return {
|
|
674
|
+
sql: finalSql,
|
|
675
|
+
sqlCount: countQuery?.toQuery?.(),
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
buildObjectFromResultset(resultSet) {
|
|
679
|
+
const structure = this.structure;
|
|
680
|
+
const rootKey = this.getStructureKeys(structure)[0];
|
|
681
|
+
const maps = {};
|
|
682
|
+
const isListMap = {};
|
|
683
|
+
const referenceMap = {};
|
|
684
|
+
const pathDetails = {};
|
|
685
|
+
const initializeMaps = (structure, path) => {
|
|
686
|
+
const currentPath = path.join(".");
|
|
687
|
+
const entity = this.getEntity(currentPath, false);
|
|
688
|
+
if (!entity) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
maps[currentPath] = {};
|
|
692
|
+
if (path.length > 1) {
|
|
693
|
+
const property = path[path.length - 1];
|
|
694
|
+
const parents = path.slice(0, -1);
|
|
695
|
+
const parentPath = parents.join(".");
|
|
696
|
+
// In the case of specific selection
|
|
697
|
+
// We dont need to check if the property is a list
|
|
698
|
+
const isSelectableField = this.allSchemaFields.has(property);
|
|
699
|
+
if (isSelectableField) {
|
|
700
|
+
pathDetails[currentPath] = { property, parents, parentPath };
|
|
701
|
+
isListMap[currentPath] = false;
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
isListMap[currentPath] = !!this.getEntity(currentPath, false)?.ref?.parents?.find((p) => p.targetProp === property)?.isList;
|
|
705
|
+
pathDetails[currentPath] = { property, parents, parentPath };
|
|
706
|
+
}
|
|
707
|
+
const children = this.getStructureKeys(structure);
|
|
708
|
+
for (const key of children) {
|
|
709
|
+
initializeMaps(structure[key], [...path, key]);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
initializeMaps(structure[rootKey], [rootKey]);
|
|
713
|
+
function buildReferenceKey(path, id, row) {
|
|
714
|
+
let current = "";
|
|
715
|
+
let key = "";
|
|
716
|
+
for (const p of path) {
|
|
717
|
+
current += `${p}`;
|
|
718
|
+
key += row[`${current}.id`] + ".";
|
|
719
|
+
current += ".";
|
|
720
|
+
}
|
|
721
|
+
return key + id;
|
|
722
|
+
}
|
|
723
|
+
const columnMap = {};
|
|
724
|
+
const columnNames = Object.keys(resultSet[0] ?? {});
|
|
725
|
+
for (const property of columnNames) {
|
|
726
|
+
const segments = property.split(".");
|
|
727
|
+
const field = segments.pop();
|
|
728
|
+
const parent = segments.join(".");
|
|
729
|
+
columnMap[parent] ??= [];
|
|
730
|
+
columnMap[parent].push({
|
|
731
|
+
field,
|
|
732
|
+
property,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
resultSet.forEach((row) => {
|
|
736
|
+
for (const path in maps) {
|
|
737
|
+
const id = row[`${path}.id`];
|
|
738
|
+
// root level
|
|
739
|
+
if (!pathDetails[path]) {
|
|
740
|
+
if (!maps[path][id]) {
|
|
741
|
+
maps[path][id] = row[path] || undefined;
|
|
742
|
+
// If there is an id, but no object values, it means that specific fields were selected
|
|
743
|
+
// so we recompose the object with all selected fields. (id will always be selected)
|
|
744
|
+
if (!maps[path][id] && id) {
|
|
745
|
+
maps[path][id] = {};
|
|
746
|
+
for (const column of columnMap[path]) {
|
|
747
|
+
maps[path][id][column.field] = row[column.property];
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
const { property, parents, parentPath } = pathDetails[path];
|
|
754
|
+
const referenceKey = buildReferenceKey(parents, id, row);
|
|
755
|
+
if (referenceMap[referenceKey]) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
maps[path][id] = row[path] || undefined;
|
|
759
|
+
// If there is an id, but no object values, it means that specific fields were selected
|
|
760
|
+
// so we recompose the object with all selected fields. (id will always be selected)
|
|
761
|
+
if (!maps[path][id] && id) {
|
|
762
|
+
maps[path][id] = {};
|
|
763
|
+
for (const column of columnMap[path]) {
|
|
764
|
+
maps[path][id][column.field] = row[column.property];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const parentObj = maps[parentPath][row[`${parentPath}.id`]];
|
|
768
|
+
if (!parentObj) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
const isList = isListMap[parentPath + "." + property];
|
|
772
|
+
if (isList && !Array.isArray(parentObj[property])) {
|
|
773
|
+
parentObj[property] = [];
|
|
774
|
+
}
|
|
775
|
+
if (maps[path][id] !== undefined) {
|
|
776
|
+
if (isList) {
|
|
777
|
+
parentObj[property].push(maps[path][id]);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
parentObj[property] = maps[path][id];
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
referenceMap[referenceKey] = true;
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
return Object.values(maps[rootKey] ?? {});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
exports.QueryBuilder = QueryBuilder;
|
|
790
|
+
_QueryBuilder_searchVectorColumnName = new WeakMap();
|
|
791
|
+
//# sourceMappingURL=query-builder.js.map
|