@mikro-orm/knex 6.0.0-dev.1 → 6.0.0-dev.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +8 -4
- package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +35 -18
- package/AbstractSqlDriver.d.ts +66 -0
- package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +422 -129
- package/AbstractSqlPlatform.d.ts +22 -0
- package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +46 -19
- package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +1 -0
- package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +3 -0
- package/{dist/README.md → README.md} +66 -109
- package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +6 -10
- package/{dist/SqlEntityManager.js → SqlEntityManager.js} +5 -10
- package/SqlEntityRepository.d.ts +24 -0
- package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +11 -8
- package/{dist/index.d.ts → index.d.ts} +1 -0
- package/{dist/index.js → index.js} +6 -1
- package/index.mjs +208 -0
- package/package.json +20 -49
- package/query/ArrayCriteriaNode.d.ts +9 -0
- package/{dist/query → query}/CriteriaNode.d.ts +8 -8
- package/{dist/query → query}/CriteriaNode.js +23 -13
- package/query/CriteriaNodeFactory.d.ts +12 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +10 -13
- package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
- package/{dist/query → query}/ObjectCriteriaNode.js +26 -9
- package/{dist/query → query}/QueryBuilder.d.ts +90 -34
- package/query/QueryBuilder.js +1098 -0
- package/{dist/query → query}/QueryBuilderHelper.d.ts +23 -19
- package/{dist/query → query}/QueryBuilderHelper.js +300 -176
- package/query/ScalarCriteriaNode.d.ts +10 -0
- package/{dist/query → query}/ScalarCriteriaNode.js +9 -6
- package/{dist/query → query}/enums.js +1 -1
- package/{dist/query → query}/index.js +5 -1
- package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
- package/{dist/schema → schema}/DatabaseSchema.js +33 -23
- package/{dist/schema → schema}/DatabaseTable.d.ts +12 -10
- package/{dist/schema → schema}/DatabaseTable.js +55 -29
- package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
- package/{dist/schema → schema}/SchemaComparator.js +68 -10
- package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
- package/{dist/schema → schema}/SchemaHelper.js +56 -11
- package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +139 -55
- package/{dist/schema → schema}/index.d.ts +1 -1
- package/{dist/schema → schema}/index.js +6 -2
- package/{dist/typings.d.ts → typings.d.ts} +27 -18
- package/CHANGELOG.md +0 -661
- package/dist/AbstractSqlDriver.d.ts +0 -55
- package/dist/AbstractSqlPlatform.d.ts +0 -21
- package/dist/LICENSE +0 -21
- package/dist/SqlEntityRepository.d.ts +0 -23
- package/dist/index.mjs +0 -26
- package/dist/package.json +0 -99
- package/dist/query/ArrayCriteriaNode.d.ts +0 -9
- package/dist/query/CriteriaNodeFactory.d.ts +0 -12
- package/dist/query/QueryBuilder.js +0 -688
- package/dist/query/ScalarCriteriaNode.d.ts +0 -9
- /package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
- /package/{dist/query → query}/enums.d.ts +0 -0
- /package/{dist/query → query}/index.d.ts +0 -0
- /package/{dist/typings.js → typings.js} +0 -0
|
@@ -8,16 +8,32 @@ const enums_1 = require("./enums");
|
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
10
|
class QueryBuilderHelper {
|
|
11
|
-
|
|
11
|
+
entityName;
|
|
12
|
+
alias;
|
|
13
|
+
aliasMap;
|
|
14
|
+
subQueries;
|
|
15
|
+
knex;
|
|
16
|
+
driver;
|
|
17
|
+
platform;
|
|
18
|
+
metadata;
|
|
19
|
+
constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
|
|
12
20
|
this.entityName = entityName;
|
|
13
21
|
this.alias = alias;
|
|
14
22
|
this.aliasMap = aliasMap;
|
|
15
23
|
this.subQueries = subQueries;
|
|
16
|
-
this.metadata = metadata;
|
|
17
24
|
this.knex = knex;
|
|
18
|
-
this.
|
|
25
|
+
this.driver = driver;
|
|
26
|
+
this.platform = this.driver.getPlatform();
|
|
27
|
+
this.metadata = this.driver.getMetadata();
|
|
19
28
|
}
|
|
20
29
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
30
|
+
if (core_1.Utils.isRawSql(field)) {
|
|
31
|
+
return this.knex.raw(field.sql, field.params);
|
|
32
|
+
}
|
|
33
|
+
/* istanbul ignore next */
|
|
34
|
+
if (typeof field !== 'string') {
|
|
35
|
+
return field;
|
|
36
|
+
}
|
|
21
37
|
const isTableNameAliasRequired = this.isTableNameAliasRequired(type);
|
|
22
38
|
const fields = core_1.Utils.splitPrimaryKeys(field);
|
|
23
39
|
if (fields.length > 1) {
|
|
@@ -26,18 +42,36 @@ class QueryBuilderHelper {
|
|
|
26
42
|
const [a, f] = this.splitField(p);
|
|
27
43
|
const prop = this.getProperty(f, a);
|
|
28
44
|
if (prop) {
|
|
29
|
-
parts.push(...prop.fieldNames.map(f => this.mapper(f, type, value, alias)));
|
|
45
|
+
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias)));
|
|
30
46
|
}
|
|
31
47
|
else {
|
|
32
|
-
parts.push(this.mapper(`${a}.${f}
|
|
48
|
+
parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
|
|
33
49
|
}
|
|
34
50
|
}
|
|
51
|
+
// flatten the value if we see we are expanding nested composite key
|
|
52
|
+
// hackish, but cleaner solution would require quite a lot of refactoring
|
|
53
|
+
if (fields.length !== parts.length && Array.isArray(value)) {
|
|
54
|
+
value.forEach(row => {
|
|
55
|
+
if (Array.isArray(row)) {
|
|
56
|
+
const tmp = core_1.Utils.flatten(row);
|
|
57
|
+
row.length = 0;
|
|
58
|
+
row.push(...tmp);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
35
62
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
36
63
|
}
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
65
|
+
if (rawField) {
|
|
66
|
+
return this.knex.raw(rawField.sql, rawField.params);
|
|
67
|
+
}
|
|
39
68
|
const [a, f] = this.splitField(field);
|
|
40
69
|
const prop = this.getProperty(f, a);
|
|
70
|
+
let ret = field;
|
|
71
|
+
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
72
|
+
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
73
|
+
return this.alias + '.' + prop.fieldNames[0];
|
|
74
|
+
}
|
|
41
75
|
const noPrefix = prop && prop.persist === false;
|
|
42
76
|
if (prop?.fieldNameRaw) {
|
|
43
77
|
return this.knex.raw(this.prefix(field, isTableNameAliasRequired));
|
|
@@ -48,7 +82,7 @@ class QueryBuilderHelper {
|
|
|
48
82
|
const as = alias === null ? '' : ` as ${aliased}`;
|
|
49
83
|
return this.knex.raw(`${prop.formula(alias2)}${as}`);
|
|
50
84
|
}
|
|
51
|
-
if (prop?.
|
|
85
|
+
if (prop?.hasConvertToJSValueSQL) {
|
|
52
86
|
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
53
87
|
const valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
54
88
|
if (alias === null) {
|
|
@@ -57,15 +91,12 @@ class QueryBuilderHelper {
|
|
|
57
91
|
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
|
|
58
92
|
}
|
|
59
93
|
// do not wrap custom expressions
|
|
60
|
-
if (!
|
|
94
|
+
if (!rawField) {
|
|
61
95
|
ret = this.prefix(field);
|
|
62
96
|
}
|
|
63
97
|
if (alias) {
|
|
64
98
|
ret += ' as ' + alias;
|
|
65
99
|
}
|
|
66
|
-
if (customExpression) {
|
|
67
|
-
return this.knex.raw(ret, value);
|
|
68
|
-
}
|
|
69
100
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
70
101
|
return ret;
|
|
71
102
|
}
|
|
@@ -75,111 +106,153 @@ class QueryBuilderHelper {
|
|
|
75
106
|
if (Array.isArray(data)) {
|
|
76
107
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
77
108
|
}
|
|
78
|
-
data = Object.assign({}, data); // copy first
|
|
79
109
|
const meta = this.metadata.find(this.entityName);
|
|
80
|
-
|
|
81
|
-
if (!meta?.properties[k]) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const prop = meta.properties[k];
|
|
85
|
-
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
86
|
-
const copy = data[k];
|
|
87
|
-
delete data[k];
|
|
88
|
-
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
92
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, true);
|
|
93
|
-
}
|
|
94
|
-
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
95
|
-
const quoted = this.platform.quoteValue(data[k]);
|
|
96
|
-
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform));
|
|
97
|
-
}
|
|
98
|
-
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
99
|
-
data[k] = JSON.stringify(data[k]);
|
|
100
|
-
}
|
|
101
|
-
if (prop.fieldNames) {
|
|
102
|
-
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
110
|
+
data = this.mapData(data, meta?.properties, convertCustomTypes);
|
|
105
111
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
112
|
+
/* istanbul ignore next */
|
|
106
113
|
data[meta.primaryKeys[0]] = this.platform.usesDefaultKeyword() ? this.knex.raw('default') : undefined;
|
|
107
114
|
}
|
|
108
115
|
return data;
|
|
109
116
|
}
|
|
110
|
-
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
111
|
-
const
|
|
112
|
-
const prop2 = meta.properties[prop.mappedBy || prop.inversedBy];
|
|
117
|
+
joinOneToReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
118
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
113
119
|
const table = this.getTableName(prop.type);
|
|
114
120
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
115
121
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
116
122
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
123
|
+
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
117
124
|
return {
|
|
118
|
-
prop, type, cond, ownerAlias, alias, table,
|
|
125
|
+
prop, type, cond, ownerAlias, alias, table, schema,
|
|
119
126
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
120
127
|
};
|
|
121
128
|
}
|
|
122
|
-
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
129
|
+
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
123
130
|
return {
|
|
124
131
|
prop, type, cond, ownerAlias, alias,
|
|
125
132
|
table: this.getTableName(prop.type),
|
|
133
|
+
schema: this.driver.getSchemaName(prop.targetMeta, { schema }),
|
|
126
134
|
joinColumns: prop.referencedColumnNames,
|
|
127
135
|
primaryKeys: prop.fieldNames,
|
|
128
136
|
};
|
|
129
137
|
}
|
|
130
|
-
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
138
|
+
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
|
|
139
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
131
140
|
const ret = {
|
|
132
141
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
133
|
-
prop, type,
|
|
142
|
+
prop, type, ownerAlias,
|
|
134
143
|
alias: pivotAlias,
|
|
135
144
|
inverseAlias: alias,
|
|
136
145
|
joinColumns: prop.joinColumns,
|
|
137
146
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
138
147
|
primaryKeys: prop.referencedColumnNames,
|
|
139
|
-
|
|
148
|
+
cond: {},
|
|
149
|
+
table: pivotMeta.tableName,
|
|
150
|
+
schema: this.driver.getSchemaName(pivotMeta, { schema }),
|
|
140
151
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
141
152
|
},
|
|
142
153
|
};
|
|
143
154
|
if (type === 'pivotJoin') {
|
|
144
155
|
return ret;
|
|
145
156
|
}
|
|
146
|
-
const prop2 =
|
|
147
|
-
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
157
|
+
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
158
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
|
|
148
159
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
149
160
|
return ret;
|
|
150
161
|
}
|
|
151
|
-
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
152
|
-
const pivotMeta = this.metadata.find(field);
|
|
153
|
-
const prop2 = pivotMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
154
|
-
return {
|
|
155
|
-
prop, type, cond, ownerAlias, alias,
|
|
156
|
-
table: pivotMeta.collection,
|
|
157
|
-
schema: pivotMeta.schema,
|
|
158
|
-
joinColumns: prop.joinColumns,
|
|
159
|
-
inverseJoinColumns: prop2.joinColumns,
|
|
160
|
-
primaryKeys: prop.referencedColumnNames,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
162
|
processJoins(qb, joins, schema) {
|
|
164
163
|
Object.values(joins).forEach(join => {
|
|
165
164
|
let table = `${join.table} as ${join.alias}`;
|
|
166
|
-
const method = join.type === '
|
|
165
|
+
const method = join.type === 'innerJoin' ? 'inner join' : 'left join';
|
|
167
166
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
168
167
|
if (schema) {
|
|
169
168
|
table = `${schema}.${table}`;
|
|
170
169
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
this.appendJoinClause(inner, join.cond);
|
|
170
|
+
const conditions = [];
|
|
171
|
+
const params = [];
|
|
172
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
173
|
+
const left = `${join.ownerAlias}.${primaryKey}`;
|
|
174
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
175
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
178
176
|
});
|
|
177
|
+
if (join.prop.targetMeta.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
178
|
+
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
179
|
+
const alias = join.inverseAlias ?? join.alias;
|
|
180
|
+
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
|
|
181
|
+
}
|
|
182
|
+
Object.keys(join.cond).forEach(key => {
|
|
183
|
+
conditions.push(this.processJoinClause(key, join.cond[key], params));
|
|
184
|
+
});
|
|
185
|
+
return qb.joinRaw(`${method} ${this.knex.ref(table)} on ${conditions.join(' and ')}`, params);
|
|
179
186
|
});
|
|
180
187
|
}
|
|
188
|
+
processJoinClause(key, value, params, operator = '$eq') {
|
|
189
|
+
if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
|
|
190
|
+
const parts = value.map(sub => {
|
|
191
|
+
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], params)));
|
|
192
|
+
});
|
|
193
|
+
return this.wrapQueryGroup(parts, key);
|
|
194
|
+
}
|
|
195
|
+
if (this.isSimpleRegExp(value)) {
|
|
196
|
+
params.push(this.getRegExpParam(value));
|
|
197
|
+
return `${this.knex.ref(this.mapper(key))} like ?`;
|
|
198
|
+
}
|
|
199
|
+
if (value instanceof RegExp) {
|
|
200
|
+
value = this.platform.getRegExpValue(value);
|
|
201
|
+
}
|
|
202
|
+
if (core_1.Utils.isOperator(key, false) && core_1.Utils.isPlainObject(value)) {
|
|
203
|
+
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], params, key));
|
|
204
|
+
return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
|
|
205
|
+
}
|
|
206
|
+
if (core_1.Utils.isPlainObject(value) && Object.keys(value).every(k => core_1.Utils.isOperator(k, false))) {
|
|
207
|
+
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], params, op));
|
|
208
|
+
return this.wrapQueryGroup(parts);
|
|
209
|
+
}
|
|
210
|
+
operator = operator === '$not' ? '$eq' : operator;
|
|
211
|
+
if (value === null) {
|
|
212
|
+
return `${this.knex.ref(this.mapper(key))} is ${operator === '$ne' ? 'not ' : ''}null`;
|
|
213
|
+
}
|
|
214
|
+
if (operator === '$fulltext') {
|
|
215
|
+
const [fromAlias, fromField] = this.splitField(key);
|
|
216
|
+
const property = this.getProperty(fromField, fromAlias);
|
|
217
|
+
const query = this.knex.raw(this.platform.getFullTextWhereClause(property), {
|
|
218
|
+
column: this.mapper(key),
|
|
219
|
+
query: this.knex.raw('?'),
|
|
220
|
+
}).toSQL().toNative();
|
|
221
|
+
params.push(value);
|
|
222
|
+
return query.sql;
|
|
223
|
+
}
|
|
224
|
+
const replacement = this.getOperatorReplacement(operator, { [operator]: value });
|
|
225
|
+
if (['$in', '$nin'].includes(operator) && Array.isArray(value)) {
|
|
226
|
+
params.push(...value);
|
|
227
|
+
return `${this.knex.ref(this.mapper(key))} ${replacement} (${value.map(() => '?').join(', ')})`;
|
|
228
|
+
}
|
|
229
|
+
if (operator === '$exists') {
|
|
230
|
+
value = null;
|
|
231
|
+
}
|
|
232
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
233
|
+
if (rawField) {
|
|
234
|
+
let sql = rawField.sql;
|
|
235
|
+
params.push(...rawField.params);
|
|
236
|
+
params.push(...core_1.Utils.asArray(value));
|
|
237
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
238
|
+
sql += ' = ?';
|
|
239
|
+
}
|
|
240
|
+
return sql;
|
|
241
|
+
}
|
|
242
|
+
const sql = this.mapper(key);
|
|
243
|
+
if (value !== null) {
|
|
244
|
+
params.push(value);
|
|
245
|
+
}
|
|
246
|
+
return `${this.knex.ref(sql)} ${replacement} ${value === null ? 'null' : '?'}`;
|
|
247
|
+
}
|
|
248
|
+
wrapQueryGroup(parts, operator = '$and') {
|
|
249
|
+
if (parts.length === 1) {
|
|
250
|
+
return parts[0];
|
|
251
|
+
}
|
|
252
|
+
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
253
|
+
}
|
|
181
254
|
mapJoinColumns(type, join) {
|
|
182
|
-
if (join.prop && join.prop.
|
|
255
|
+
if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
|
|
183
256
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
184
257
|
return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
|
|
185
258
|
});
|
|
@@ -192,7 +265,7 @@ class QueryBuilderHelper {
|
|
|
192
265
|
isOneToOneInverse(field) {
|
|
193
266
|
const meta = this.metadata.find(this.entityName);
|
|
194
267
|
const prop = meta.properties[field];
|
|
195
|
-
return prop && prop.
|
|
268
|
+
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
196
269
|
}
|
|
197
270
|
getTableName(entityName) {
|
|
198
271
|
const meta = this.metadata.find(entityName);
|
|
@@ -205,6 +278,9 @@ class QueryBuilderHelper {
|
|
|
205
278
|
if (!(re instanceof RegExp)) {
|
|
206
279
|
return false;
|
|
207
280
|
}
|
|
281
|
+
if (re.flags.includes('i')) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
208
284
|
// when including the opening bracket/paren we consider it complex
|
|
209
285
|
return !re.source.match(/[{[(]/);
|
|
210
286
|
}
|
|
@@ -228,13 +304,13 @@ class QueryBuilderHelper {
|
|
|
228
304
|
}
|
|
229
305
|
appendOnConflictClause(type, onConflict, qb) {
|
|
230
306
|
onConflict.forEach(item => {
|
|
231
|
-
const sub = qb.onConflict(item.fields);
|
|
307
|
+
const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
|
|
232
308
|
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
233
309
|
core_1.Utils.runIfNotEmpty(() => {
|
|
234
310
|
let mergeParam = item.merge;
|
|
235
311
|
if (core_1.Utils.isObject(item.merge)) {
|
|
236
312
|
mergeParam = {};
|
|
237
|
-
|
|
313
|
+
core_1.Utils.keys(item.merge).forEach(key => {
|
|
238
314
|
const k = this.mapper(key, type);
|
|
239
315
|
mergeParam[k] = item.merge[key];
|
|
240
316
|
});
|
|
@@ -265,34 +341,29 @@ class QueryBuilderHelper {
|
|
|
265
341
|
}
|
|
266
342
|
appendQuerySubCondition(qb, type, method, cond, key, operator) {
|
|
267
343
|
const m = operator === '$or' ? 'orWhere' : method;
|
|
344
|
+
if (cond[key] instanceof core_1.RawQueryFragment) {
|
|
345
|
+
cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
|
|
346
|
+
}
|
|
268
347
|
if (this.isSimpleRegExp(cond[key])) {
|
|
269
348
|
return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
|
|
270
349
|
}
|
|
271
350
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
272
351
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
273
352
|
}
|
|
274
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
275
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
276
|
-
}
|
|
277
353
|
const op = cond[key] === null ? 'is' : '=';
|
|
354
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
355
|
+
if (raw) {
|
|
356
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
357
|
+
if (value.length > 0) {
|
|
358
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
359
|
+
}
|
|
360
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
361
|
+
}
|
|
278
362
|
if (this.subQueries[key]) {
|
|
279
363
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
280
364
|
}
|
|
281
365
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
282
366
|
}
|
|
283
|
-
processCustomExpression(clause, m, key, cond, type = enums_1.QueryType.SELECT) {
|
|
284
|
-
// unwind parameters when ? found in field name
|
|
285
|
-
const count = key.concat('?').match(/\?/g).length - 1;
|
|
286
|
-
const value = core_1.Utils.asArray(cond[key]);
|
|
287
|
-
const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
288
|
-
const params2 = value.slice(count);
|
|
289
|
-
const k = this.mapper(key, type, params1);
|
|
290
|
-
if (params2.length > 0) {
|
|
291
|
-
const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
|
|
292
|
-
return void clause[m](k, val);
|
|
293
|
-
}
|
|
294
|
-
clause[m](k);
|
|
295
|
-
}
|
|
296
367
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
297
368
|
// grouped condition for one field
|
|
298
369
|
let value = cond[key];
|
|
@@ -301,7 +372,7 @@ class QueryBuilderHelper {
|
|
|
301
372
|
return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
|
|
302
373
|
}
|
|
303
374
|
if (value instanceof RegExp) {
|
|
304
|
-
value =
|
|
375
|
+
value = this.platform.getRegExpValue(value);
|
|
305
376
|
}
|
|
306
377
|
// operators
|
|
307
378
|
const op = Object.keys(core_1.QueryOperator).find(op => op in value);
|
|
@@ -311,68 +382,43 @@ class QueryBuilderHelper {
|
|
|
311
382
|
const replacement = this.getOperatorReplacement(op, value);
|
|
312
383
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
313
384
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
314
|
-
const
|
|
315
|
-
value[op] = this.knex.raw(
|
|
385
|
+
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
386
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
|
|
316
387
|
}
|
|
317
388
|
if (this.subQueries[key]) {
|
|
318
389
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
319
390
|
}
|
|
320
|
-
|
|
391
|
+
if (op === '$fulltext') {
|
|
392
|
+
const [a, f] = this.splitField(key);
|
|
393
|
+
const prop = this.getProperty(f, a);
|
|
394
|
+
/* istanbul ignore next */
|
|
395
|
+
if (!prop) {
|
|
396
|
+
throw new Error(`Cannot use $fulltext operator on ${key}, property not found`);
|
|
397
|
+
}
|
|
398
|
+
qb[m](this.knex.raw(this.platform.getFullTextWhereClause(prop), {
|
|
399
|
+
column: this.mapper(key, type, undefined, null),
|
|
400
|
+
query: value[op],
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
405
|
+
qb[m](mappedKey, replacement, value[op]);
|
|
406
|
+
}
|
|
321
407
|
}
|
|
322
408
|
getOperatorReplacement(op, value) {
|
|
323
409
|
let replacement = core_1.QueryOperator[op];
|
|
410
|
+
if (op === '$exists') {
|
|
411
|
+
replacement = value[op] ? 'is not' : 'is';
|
|
412
|
+
value[op] = null;
|
|
413
|
+
}
|
|
324
414
|
if (value[op] === null && ['$eq', '$ne'].includes(op)) {
|
|
325
415
|
replacement = op === '$eq' ? 'is' : 'is not';
|
|
326
416
|
}
|
|
327
417
|
if (op === '$re') {
|
|
328
|
-
replacement = this.platform.getRegExpOperator();
|
|
418
|
+
replacement = this.platform.getRegExpOperator(value[op], value.$flags);
|
|
329
419
|
}
|
|
330
420
|
return replacement;
|
|
331
421
|
}
|
|
332
|
-
appendJoinClause(clause, cond, operator) {
|
|
333
|
-
Object.keys(cond).forEach(k => {
|
|
334
|
-
if (k === '$and' || k === '$or') {
|
|
335
|
-
const method = operator === '$or' ? 'orOn' : 'andOn';
|
|
336
|
-
const m = k === '$or' ? 'orOn' : 'andOn';
|
|
337
|
-
return clause[method](outer => cond[k].forEach((sub) => {
|
|
338
|
-
if (core_1.Utils.getObjectKeysSize(sub) === 1) {
|
|
339
|
-
return this.appendJoinClause(outer, sub, k);
|
|
340
|
-
}
|
|
341
|
-
outer[m](inner => this.appendJoinClause(inner, sub, '$and'));
|
|
342
|
-
}));
|
|
343
|
-
}
|
|
344
|
-
this.appendJoinSubClause(clause, cond, k, operator);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
appendJoinSubClause(clause, cond, key, operator) {
|
|
348
|
-
const m = operator === '$or' ? 'orOn' : 'andOn';
|
|
349
|
-
if (cond[key] instanceof RegExp) {
|
|
350
|
-
return void clause[m](this.mapper(key), 'like', this.knex.raw('?', this.getRegExpParam(cond[key])));
|
|
351
|
-
}
|
|
352
|
-
if (core_1.Utils.isPlainObject(cond[key])) {
|
|
353
|
-
return this.processObjectSubClause(cond, key, clause, m);
|
|
354
|
-
}
|
|
355
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
356
|
-
return this.processCustomExpression(clause, m, key, cond);
|
|
357
|
-
}
|
|
358
|
-
const op = cond[key] === null ? 'is' : '=';
|
|
359
|
-
clause[m](this.knex.raw(`${this.knex.ref(this.mapper(key, enums_1.QueryType.SELECT, cond[key]))} ${op} ?`, cond[key]));
|
|
360
|
-
}
|
|
361
|
-
processObjectSubClause(cond, key, clause, m) {
|
|
362
|
-
// grouped condition for one field
|
|
363
|
-
if (core_1.Utils.getObjectKeysSize(cond[key]) > 1) {
|
|
364
|
-
const subCondition = Object.entries(cond[key]).map(([subKey, subValue]) => ({ [key]: { [subKey]: subValue } }));
|
|
365
|
-
return void clause[m](inner => subCondition.map(sub => this.appendJoinClause(inner, sub, '$and')));
|
|
366
|
-
}
|
|
367
|
-
// operators
|
|
368
|
-
for (const [op, replacement] of Object.entries(core_1.QueryOperator)) {
|
|
369
|
-
if (!(op in cond[key])) {
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
clause[m](this.mapper(key), replacement, this.knex.raw('?', cond[key][op]));
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
422
|
getQueryOrder(type, orderBy, populate) {
|
|
377
423
|
if (Array.isArray(orderBy)) {
|
|
378
424
|
return orderBy
|
|
@@ -384,42 +430,72 @@ class QueryBuilderHelper {
|
|
|
384
430
|
}
|
|
385
431
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
386
432
|
const ret = [];
|
|
387
|
-
Object.keys(orderBy).forEach(
|
|
388
|
-
const direction = orderBy[
|
|
433
|
+
Object.keys(orderBy).forEach(key => {
|
|
434
|
+
const direction = orderBy[key];
|
|
389
435
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
390
|
-
|
|
391
|
-
|
|
436
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
437
|
+
if (raw) {
|
|
438
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
392
439
|
return;
|
|
393
440
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const prop = this.getProperty(
|
|
399
|
-
const noPrefix = (prop && prop.persist === false) ||
|
|
400
|
-
const column = this.mapper(noPrefix ?
|
|
441
|
+
core_1.Utils.splitPrimaryKeys(key).forEach(f => {
|
|
442
|
+
// eslint-disable-next-line prefer-const
|
|
443
|
+
let [alias, field] = this.splitField(f, true);
|
|
444
|
+
alias = populate[alias] || alias;
|
|
445
|
+
const prop = this.getProperty(field, alias);
|
|
446
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
447
|
+
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
401
448
|
/* istanbul ignore next */
|
|
402
449
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
403
450
|
const customOrder = prop?.customOrder;
|
|
404
|
-
|
|
451
|
+
let colPart = customOrder
|
|
405
452
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
406
453
|
: rawColumn;
|
|
454
|
+
if (core_1.Utils.isRawSql(colPart)) {
|
|
455
|
+
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
456
|
+
}
|
|
407
457
|
ret.push(`${colPart} ${order.toLowerCase()}`);
|
|
408
458
|
});
|
|
409
459
|
});
|
|
410
460
|
return ret.join(', ');
|
|
411
461
|
}
|
|
412
|
-
finalize(type, qb, meta) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
462
|
+
finalize(type, qb, meta, data, returning) {
|
|
463
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
// always respect explicit returning hint
|
|
467
|
+
if (returning && returning.length > 0) {
|
|
468
|
+
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
472
|
+
const returningProps = meta.hydrateProps
|
|
473
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
474
|
+
.filter(prop => !(prop.name in data));
|
|
475
|
+
if (returningProps.length > 0) {
|
|
476
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
481
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
482
|
+
if (returningProps.length > 0) {
|
|
483
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
484
|
+
}
|
|
417
485
|
}
|
|
418
486
|
}
|
|
419
|
-
splitField(field) {
|
|
487
|
+
splitField(field, greedyAlias = false) {
|
|
420
488
|
const parts = field.split('.');
|
|
421
|
-
|
|
422
|
-
|
|
489
|
+
if (parts.length === 1) {
|
|
490
|
+
return [this.alias, parts[0]];
|
|
491
|
+
}
|
|
492
|
+
if (greedyAlias) {
|
|
493
|
+
const fromField = parts.pop();
|
|
494
|
+
const fromAlias = parts.join('.');
|
|
495
|
+
return [fromAlias, fromField];
|
|
496
|
+
}
|
|
497
|
+
const fromAlias = parts.shift();
|
|
498
|
+
const fromField = parts.join('.');
|
|
423
499
|
return [fromAlias, fromField];
|
|
424
500
|
}
|
|
425
501
|
getLockSQL(qb, lockMode, lockTables = []) {
|
|
@@ -438,33 +514,29 @@ class QueryBuilderHelper {
|
|
|
438
514
|
}
|
|
439
515
|
updateVersionProperty(qb, data) {
|
|
440
516
|
const meta = this.metadata.find(this.entityName);
|
|
441
|
-
if (!meta
|
|
517
|
+
if (!meta?.versionProperty || meta.versionProperty in data) {
|
|
442
518
|
return;
|
|
443
519
|
}
|
|
444
520
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
445
521
|
let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
446
|
-
if (versionProperty.
|
|
522
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
447
523
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
448
524
|
}
|
|
449
525
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
450
526
|
}
|
|
451
|
-
static isCustomExpression(field, hasAlias = false) {
|
|
452
|
-
// if we do not have alias, we don't consider spaces as custom expressions
|
|
453
|
-
const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
|
|
454
|
-
return !!field.match(re);
|
|
455
|
-
}
|
|
456
527
|
prefix(field, always = false, quote = false) {
|
|
457
528
|
let ret;
|
|
458
529
|
if (!this.isPrefixed(field)) {
|
|
459
530
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
460
531
|
const fieldName = this.fieldName(field, this.alias, always);
|
|
461
|
-
if (
|
|
462
|
-
return fieldName;
|
|
532
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
533
|
+
return fieldName.sql;
|
|
463
534
|
}
|
|
464
535
|
ret = alias + fieldName;
|
|
465
536
|
}
|
|
466
537
|
else {
|
|
467
|
-
const [a,
|
|
538
|
+
const [a, ...rest] = field.split('.');
|
|
539
|
+
const f = rest.join('.');
|
|
468
540
|
ret = a + '.' + this.fieldName(f, a);
|
|
469
541
|
}
|
|
470
542
|
if (quote) {
|
|
@@ -498,26 +570,78 @@ class QueryBuilderHelper {
|
|
|
498
570
|
}
|
|
499
571
|
if (prop.fieldNameRaw) {
|
|
500
572
|
if (!always) {
|
|
501
|
-
return prop.fieldNameRaw
|
|
502
|
-
.replace(
|
|
503
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
573
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
574
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
575
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
504
576
|
}
|
|
505
577
|
if (alias) {
|
|
506
|
-
return prop.fieldNameRaw.replace(
|
|
578
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
507
579
|
}
|
|
508
580
|
/* istanbul ignore next */
|
|
509
|
-
return prop.fieldNameRaw;
|
|
581
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
510
582
|
}
|
|
511
583
|
/* istanbul ignore next */
|
|
512
|
-
return prop.fieldNames[0] ?? field;
|
|
584
|
+
return prop.fieldNames?.[0] ?? field;
|
|
513
585
|
}
|
|
514
586
|
getProperty(field, alias) {
|
|
515
|
-
const entityName = this.aliasMap[alias] || this.entityName;
|
|
587
|
+
const entityName = this.aliasMap[alias]?.entityName || this.entityName;
|
|
516
588
|
const meta = this.metadata.find(entityName);
|
|
517
|
-
|
|
589
|
+
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
590
|
+
if (alias && meta) {
|
|
591
|
+
const prop = meta.properties[alias];
|
|
592
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
593
|
+
// we want to select the full object property so hydration works as expected
|
|
594
|
+
if (prop.object) {
|
|
595
|
+
return prop;
|
|
596
|
+
}
|
|
597
|
+
const parts = field.split('.');
|
|
598
|
+
const nest = (p) => parts.length > 0 ? nest(p.embeddedProps[parts.shift()]) : p;
|
|
599
|
+
return nest(prop);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return meta?.properties[field];
|
|
518
603
|
}
|
|
519
604
|
isTableNameAliasRequired(type) {
|
|
520
|
-
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type);
|
|
605
|
+
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type ?? enums_1.QueryType.SELECT);
|
|
606
|
+
}
|
|
607
|
+
mapData(data, properties, convertCustomTypes) {
|
|
608
|
+
if (!properties) {
|
|
609
|
+
return data;
|
|
610
|
+
}
|
|
611
|
+
data = Object.assign({}, data); // copy first
|
|
612
|
+
Object.keys(data).forEach(k => {
|
|
613
|
+
const prop = properties[k];
|
|
614
|
+
if (!prop) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (prop.embeddedProps && !prop.object) {
|
|
618
|
+
const copy = data[k];
|
|
619
|
+
delete data[k];
|
|
620
|
+
Object.assign(data, this.mapData(copy, prop.embeddedProps, convertCustomTypes));
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
624
|
+
const copy = core_1.Utils.flatten(data[k]);
|
|
625
|
+
delete data[k];
|
|
626
|
+
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
630
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
|
|
631
|
+
}
|
|
632
|
+
if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
|
|
633
|
+
const quoted = this.platform.quoteValue(data[k]);
|
|
634
|
+
const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
|
|
635
|
+
data[k] = this.knex.raw(sql.replace(/\?/g, '\\?'));
|
|
636
|
+
}
|
|
637
|
+
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
638
|
+
data[k] = JSON.stringify(data[k]);
|
|
639
|
+
}
|
|
640
|
+
if (prop.fieldNames) {
|
|
641
|
+
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
return data;
|
|
521
645
|
}
|
|
522
646
|
}
|
|
523
647
|
exports.QueryBuilderHelper = QueryBuilderHelper;
|