@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.
Files changed (151) hide show
  1. package/README.md +1 -0
  2. package/dist/index.d.ts +6 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +13 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/loaders/index.d.ts +4 -0
  7. package/dist/loaders/index.d.ts.map +1 -0
  8. package/dist/loaders/index.js +27 -0
  9. package/dist/loaders/index.js.map +1 -0
  10. package/dist/migrations/Migration20231019174230.d.ts +5 -0
  11. package/dist/migrations/Migration20231019174230.d.ts.map +1 -0
  12. package/dist/migrations/Migration20231019174230.js +12 -0
  13. package/dist/migrations/Migration20231019174230.js.map +1 -0
  14. package/dist/migrations/Migration20241209173313.d.ts +6 -0
  15. package/dist/migrations/Migration20241209173313.d.ts.map +1 -0
  16. package/dist/migrations/Migration20241209173313.js +36 -0
  17. package/dist/migrations/Migration20241209173313.js.map +1 -0
  18. package/dist/migrations/Migration20250122154720.d.ts +6 -0
  19. package/dist/migrations/Migration20250122154720.d.ts.map +1 -0
  20. package/dist/migrations/Migration20250122154720.js +16 -0
  21. package/dist/migrations/Migration20250122154720.js.map +1 -0
  22. package/dist/migrations/Migration20250127105159.d.ts +6 -0
  23. package/dist/migrations/Migration20250127105159.d.ts.map +1 -0
  24. package/dist/migrations/Migration20250127105159.js +16 -0
  25. package/dist/migrations/Migration20250127105159.js.map +1 -0
  26. package/dist/migrations/Migration20250127144442.d.ts +6 -0
  27. package/dist/migrations/Migration20250127144442.d.ts.map +1 -0
  28. package/dist/migrations/Migration20250127144442.js +16 -0
  29. package/dist/migrations/Migration20250127144442.js.map +1 -0
  30. package/dist/migrations/Migration20250128132404.d.ts +6 -0
  31. package/dist/migrations/Migration20250128132404.d.ts.map +1 -0
  32. package/dist/migrations/Migration20250128132404.js +16 -0
  33. package/dist/migrations/Migration20250128132404.js.map +1 -0
  34. package/dist/migrations/Migration20250218132404.d.ts +6 -0
  35. package/dist/migrations/Migration20250218132404.d.ts.map +1 -0
  36. package/dist/migrations/Migration20250218132404.js +48 -0
  37. package/dist/migrations/Migration20250218132404.js.map +1 -0
  38. package/dist/migrations/Migration20250515161913.d.ts +6 -0
  39. package/dist/migrations/Migration20250515161913.d.ts.map +1 -0
  40. package/dist/migrations/Migration20250515161913.js +62 -0
  41. package/dist/migrations/Migration20250515161913.js.map +1 -0
  42. package/dist/models/index-data.d.ts +8 -0
  43. package/dist/models/index-data.d.ts.map +1 -0
  44. package/dist/models/index-data.js +12 -0
  45. package/dist/models/index-data.js.map +1 -0
  46. package/dist/models/index-metadata.d.ts +10 -0
  47. package/dist/models/index-metadata.d.ts.map +1 -0
  48. package/dist/models/index-metadata.js +23 -0
  49. package/dist/models/index-metadata.js.map +1 -0
  50. package/dist/models/index-relation.d.ts +11 -0
  51. package/dist/models/index-relation.d.ts.map +1 -0
  52. package/dist/models/index-relation.js +14 -0
  53. package/dist/models/index-relation.js.map +1 -0
  54. package/dist/models/index-sync.d.ts +7 -0
  55. package/dist/models/index-sync.d.ts.map +1 -0
  56. package/dist/models/index-sync.js +18 -0
  57. package/dist/models/index-sync.js.map +1 -0
  58. package/dist/models/index.d.ts +5 -0
  59. package/dist/models/index.d.ts.map +1 -0
  60. package/dist/models/index.js +15 -0
  61. package/dist/models/index.js.map +1 -0
  62. package/dist/services/data-synchronizer.d.ts +35 -0
  63. package/dist/services/data-synchronizer.d.ts.map +1 -0
  64. package/dist/services/data-synchronizer.js +238 -0
  65. package/dist/services/data-synchronizer.js.map +1 -0
  66. package/dist/services/index-data.d.ts +8 -0
  67. package/dist/services/index-data.d.ts.map +1 -0
  68. package/dist/services/index-data.js +9 -0
  69. package/dist/services/index-data.js.map +1 -0
  70. package/dist/services/index-metadata.d.ts +8 -0
  71. package/dist/services/index-metadata.d.ts.map +1 -0
  72. package/dist/services/index-metadata.js +9 -0
  73. package/dist/services/index-metadata.js.map +1 -0
  74. package/dist/services/index-module-service.d.ts +75 -0
  75. package/dist/services/index-module-service.d.ts.map +1 -0
  76. package/dist/services/index-module-service.js +340 -0
  77. package/dist/services/index-module-service.js.map +1 -0
  78. package/dist/services/index-relation.d.ts +8 -0
  79. package/dist/services/index-relation.d.ts.map +1 -0
  80. package/dist/services/index-relation.js +9 -0
  81. package/dist/services/index-relation.js.map +1 -0
  82. package/dist/services/index-sync.d.ts +8 -0
  83. package/dist/services/index-sync.d.ts.map +1 -0
  84. package/dist/services/index-sync.js +9 -0
  85. package/dist/services/index-sync.js.map +1 -0
  86. package/dist/services/index.d.ts +2 -0
  87. package/dist/services/index.d.ts.map +1 -0
  88. package/dist/services/index.js +9 -0
  89. package/dist/services/index.js.map +1 -0
  90. package/dist/services/postgres-provider.d.ts +132 -0
  91. package/dist/services/postgres-provider.d.ts.map +1 -0
  92. package/dist/services/postgres-provider.js +516 -0
  93. package/dist/services/postgres-provider.js.map +1 -0
  94. package/dist/tsconfig.tsbuildinfo +1 -0
  95. package/dist/types/index.d.ts +21 -0
  96. package/dist/types/index.d.ts.map +1 -0
  97. package/dist/types/index.js +12 -0
  98. package/dist/types/index.js.map +1 -0
  99. package/dist/utils/base-graphql-schema.d.ts +2 -0
  100. package/dist/utils/base-graphql-schema.d.ts.map +1 -0
  101. package/dist/utils/base-graphql-schema.js +10 -0
  102. package/dist/utils/base-graphql-schema.js.map +1 -0
  103. package/dist/utils/build-config.d.ts +28 -0
  104. package/dist/utils/build-config.d.ts.map +1 -0
  105. package/dist/utils/build-config.js +840 -0
  106. package/dist/utils/build-config.js.map +1 -0
  107. package/dist/utils/create-partitions.d.ts +4 -0
  108. package/dist/utils/create-partitions.d.ts.map +1 -0
  109. package/dist/utils/create-partitions.js +80 -0
  110. package/dist/utils/create-partitions.js.map +1 -0
  111. package/dist/utils/default-schema.d.ts +2 -0
  112. package/dist/utils/default-schema.d.ts.map +1 -0
  113. package/dist/utils/default-schema.js +41 -0
  114. package/dist/utils/default-schema.js.map +1 -0
  115. package/dist/utils/flatten-object-keys.d.ts +23 -0
  116. package/dist/utils/flatten-object-keys.d.ts.map +1 -0
  117. package/dist/utils/flatten-object-keys.js +54 -0
  118. package/dist/utils/flatten-object-keys.js.map +1 -0
  119. package/dist/utils/gql-to-types.d.ts +3 -0
  120. package/dist/utils/gql-to-types.d.ts.map +1 -0
  121. package/dist/utils/gql-to-types.js +98 -0
  122. package/dist/utils/gql-to-types.js.map +1 -0
  123. package/dist/utils/index-metadata-status.d.ts +7 -0
  124. package/dist/utils/index-metadata-status.d.ts.map +1 -0
  125. package/dist/utils/index-metadata-status.js +11 -0
  126. package/dist/utils/index-metadata-status.js.map +1 -0
  127. package/dist/utils/index.d.ts +10 -0
  128. package/dist/utils/index.d.ts.map +1 -0
  129. package/dist/utils/index.js +26 -0
  130. package/dist/utils/index.js.map +1 -0
  131. package/dist/utils/normalize-fields-selection.d.ts +2 -0
  132. package/dist/utils/normalize-fields-selection.d.ts.map +1 -0
  133. package/dist/utils/normalize-fields-selection.js +10 -0
  134. package/dist/utils/normalize-fields-selection.js.map +1 -0
  135. package/dist/utils/normalze-table-name.d.ts +3 -0
  136. package/dist/utils/normalze-table-name.d.ts.map +1 -0
  137. package/dist/utils/normalze-table-name.js +13 -0
  138. package/dist/utils/normalze-table-name.js.map +1 -0
  139. package/dist/utils/query-builder.d.ts +61 -0
  140. package/dist/utils/query-builder.d.ts.map +1 -0
  141. package/dist/utils/query-builder.js +791 -0
  142. package/dist/utils/query-builder.js.map +1 -0
  143. package/dist/utils/sync/configuration.d.ts +17 -0
  144. package/dist/utils/sync/configuration.d.ts.map +1 -0
  145. package/dist/utils/sync/configuration.js +114 -0
  146. package/dist/utils/sync/configuration.js.map +1 -0
  147. package/dist/utils/sync/orchestrator.d.ts +33 -0
  148. package/dist/utils/sync/orchestrator.d.ts.map +1 -0
  149. package/dist/utils/sync/orchestrator.js +195 -0
  150. package/dist/utils/sync/orchestrator.js.map +1 -0
  151. 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