@mikro-orm/knex 6.0.0-dev.2 → 6.0.0-dev.200
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 +73 -0
- package/AbstractSqlDriver.js +1085 -0
- 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/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -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 +211 -0
- package/package.json +20 -49
- package/query/ArrayCriteriaNode.d.ts +10 -0
- package/{dist/query → query}/ArrayCriteriaNode.js +7 -2
- package/{dist/query → query}/CriteriaNode.d.ts +11 -9
- package/{dist/query → query}/CriteriaNode.js +31 -15
- package/query/CriteriaNodeFactory.d.ts +12 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +32 -15
- package/query/ObjectCriteriaNode.d.ts +15 -0
- package/{dist/query → query}/ObjectCriteriaNode.js +68 -21
- package/query/QueryBuilder.d.ts +284 -0
- package/query/QueryBuilder.js +1218 -0
- package/{dist/query → query}/QueryBuilderHelper.d.ts +26 -23
- package/query/QueryBuilderHelper.js +661 -0
- package/query/ScalarCriteriaNode.d.ts +10 -0
- package/{dist/query → query}/ScalarCriteriaNode.js +12 -8
- package/query/enums.d.ts +15 -0
- package/query/enums.js +20 -0
- package/{dist/query → query}/index.js +5 -1
- package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
- package/{dist/schema → schema}/DatabaseSchema.js +35 -25
- package/{dist/schema → schema}/DatabaseTable.d.ts +21 -13
- package/schema/DatabaseTable.js +645 -0
- package/{dist/schema → schema}/SchemaComparator.d.ts +9 -7
- package/{dist/schema → schema}/SchemaComparator.js +99 -25
- package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
- package/{dist/schema → schema}/SchemaHelper.js +70 -18
- package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +149 -63
- 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} +45 -22
- package/CHANGELOG.md +0 -661
- package/dist/AbstractSqlDriver.d.ts +0 -55
- package/dist/AbstractSqlDriver.js +0 -644
- 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/ObjectCriteriaNode.d.ts +0 -14
- package/dist/query/QueryBuilder.d.ts +0 -211
- package/dist/query/QueryBuilder.js +0 -688
- package/dist/query/QueryBuilderHelper.js +0 -523
- package/dist/query/ScalarCriteriaNode.d.ts +0 -9
- package/dist/query/enums.d.ts +0 -8
- package/dist/query/enums.js +0 -12
- package/dist/schema/DatabaseTable.js +0 -319
- /package/{dist/query → query}/index.d.ts +0 -0
- /package/{dist/typings.js → typings.js} +0 -0
|
@@ -1,688 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueryBuilder = void 0;
|
|
4
|
-
const core_1 = require("@mikro-orm/core");
|
|
5
|
-
const enums_1 = require("./enums");
|
|
6
|
-
const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
|
|
7
|
-
const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
|
|
8
|
-
/**
|
|
9
|
-
* SQL query builder with fluent interface.
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* const qb = orm.em.createQueryBuilder(Publisher);
|
|
13
|
-
* qb.select('*')
|
|
14
|
-
* .where({
|
|
15
|
-
* name: 'test 123',
|
|
16
|
-
* type: PublisherType.GLOBAL,
|
|
17
|
-
* })
|
|
18
|
-
* .orderBy({
|
|
19
|
-
* name: QueryOrder.DESC,
|
|
20
|
-
* type: QueryOrder.ASC,
|
|
21
|
-
* })
|
|
22
|
-
* .limit(2, 1);
|
|
23
|
-
*
|
|
24
|
-
* const publisher = await qb.getSingleResult();
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
class QueryBuilder {
|
|
28
|
-
/**
|
|
29
|
-
* @internal
|
|
30
|
-
*/
|
|
31
|
-
constructor(entityName, metadata, driver, context, alias, connectionType, em) {
|
|
32
|
-
this.entityName = entityName;
|
|
33
|
-
this.metadata = metadata;
|
|
34
|
-
this.driver = driver;
|
|
35
|
-
this.context = context;
|
|
36
|
-
this.connectionType = connectionType;
|
|
37
|
-
this.em = em;
|
|
38
|
-
/** @internal */
|
|
39
|
-
this._populate = [];
|
|
40
|
-
/** @internal */
|
|
41
|
-
this._populateMap = {};
|
|
42
|
-
this.aliasCounter = 0;
|
|
43
|
-
this.flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
44
|
-
this.finalized = false;
|
|
45
|
-
this._joins = {};
|
|
46
|
-
this._aliasMap = {};
|
|
47
|
-
this._cond = {};
|
|
48
|
-
this._orderBy = [];
|
|
49
|
-
this._groupBy = [];
|
|
50
|
-
this._having = {};
|
|
51
|
-
this._joinedProps = new Map();
|
|
52
|
-
this.subQueries = {};
|
|
53
|
-
this.platform = this.driver.getPlatform();
|
|
54
|
-
this.knex = this.driver.getConnection(this.connectionType).getKnex();
|
|
55
|
-
if (alias) {
|
|
56
|
-
this.aliasCounter++;
|
|
57
|
-
}
|
|
58
|
-
this.alias = alias ?? this.getNextAlias(this.entityName);
|
|
59
|
-
this._aliasMap[this.alias] = this.entityName;
|
|
60
|
-
this.helper = new QueryBuilderHelper_1.QueryBuilderHelper(this.entityName, this.alias, this._aliasMap, this.subQueries, this.metadata, this.knex, this.platform);
|
|
61
|
-
}
|
|
62
|
-
select(fields, distinct = false) {
|
|
63
|
-
this._fields = core_1.Utils.asArray(fields);
|
|
64
|
-
if (distinct) {
|
|
65
|
-
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
66
|
-
}
|
|
67
|
-
return this.init(enums_1.QueryType.SELECT);
|
|
68
|
-
}
|
|
69
|
-
addSelect(fields) {
|
|
70
|
-
if (this.type && this.type !== enums_1.QueryType.SELECT) {
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
return this.select([...core_1.Utils.asArray(this._fields), ...core_1.Utils.asArray(fields)]);
|
|
74
|
-
}
|
|
75
|
-
insert(data) {
|
|
76
|
-
return this.init(enums_1.QueryType.INSERT, data);
|
|
77
|
-
}
|
|
78
|
-
update(data) {
|
|
79
|
-
return this.init(enums_1.QueryType.UPDATE, data);
|
|
80
|
-
}
|
|
81
|
-
delete(cond) {
|
|
82
|
-
return this.init(enums_1.QueryType.DELETE, undefined, cond);
|
|
83
|
-
}
|
|
84
|
-
truncate() {
|
|
85
|
-
return this.init(enums_1.QueryType.TRUNCATE);
|
|
86
|
-
}
|
|
87
|
-
count(field, distinct = false) {
|
|
88
|
-
this._fields = [...(field ? core_1.Utils.asArray(field) : this.metadata.find(this.entityName).primaryKeys)];
|
|
89
|
-
if (distinct) {
|
|
90
|
-
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
91
|
-
}
|
|
92
|
-
return this.init(enums_1.QueryType.COUNT);
|
|
93
|
-
}
|
|
94
|
-
join(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
95
|
-
this.joinReference(field, alias, cond, type, path);
|
|
96
|
-
return this;
|
|
97
|
-
}
|
|
98
|
-
leftJoin(field, alias, cond = {}) {
|
|
99
|
-
return this.join(field, alias, cond, 'leftJoin');
|
|
100
|
-
}
|
|
101
|
-
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
102
|
-
const prop = this.joinReference(field, alias, cond, type, path);
|
|
103
|
-
this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
|
|
104
|
-
const [fromAlias] = this.helper.splitField(field);
|
|
105
|
-
const populate = this._joinedProps.get(fromAlias);
|
|
106
|
-
const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
|
|
107
|
-
if (populate) {
|
|
108
|
-
populate.children.push(item);
|
|
109
|
-
}
|
|
110
|
-
else { // root entity
|
|
111
|
-
this._populate.push(item);
|
|
112
|
-
}
|
|
113
|
-
this._joinedProps.set(alias, item);
|
|
114
|
-
return this;
|
|
115
|
-
}
|
|
116
|
-
leftJoinAndSelect(field, alias, cond = {}) {
|
|
117
|
-
return this.joinAndSelect(field, alias, cond, 'leftJoin');
|
|
118
|
-
}
|
|
119
|
-
getFieldsForJoinedLoad(prop, alias) {
|
|
120
|
-
const fields = [];
|
|
121
|
-
prop.targetMeta.props
|
|
122
|
-
.filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
|
|
123
|
-
.forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
|
|
124
|
-
return fields;
|
|
125
|
-
}
|
|
126
|
-
withSubQuery(subQuery, alias) {
|
|
127
|
-
this.subQueries[alias] = subQuery.toString();
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
where(cond, params, operator) {
|
|
131
|
-
if (core_1.Utils.isString(cond)) {
|
|
132
|
-
cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
|
|
133
|
-
operator = operator || '$and';
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
cond = core_1.QueryHelper.processWhere(cond, this.entityName, this.metadata, this.platform, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
137
|
-
}
|
|
138
|
-
const op = operator || params;
|
|
139
|
-
const topLevel = !op || !core_1.Utils.hasObjectKeys(this._cond);
|
|
140
|
-
const criteriaNode = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond);
|
|
141
|
-
if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(this.type) && criteriaNode.willAutoJoin(this)) {
|
|
142
|
-
// use sub-query to support joining
|
|
143
|
-
this.setFlag(this.type === enums_1.QueryType.UPDATE ? core_1.QueryFlag.UPDATE_SUB_QUERY : core_1.QueryFlag.DELETE_SUB_QUERY);
|
|
144
|
-
this.select(this.metadata.find(this.entityName).primaryKeys, true);
|
|
145
|
-
}
|
|
146
|
-
if (topLevel) {
|
|
147
|
-
this._cond = criteriaNode.process(this);
|
|
148
|
-
}
|
|
149
|
-
else if (Array.isArray(this._cond[op])) {
|
|
150
|
-
this._cond[op].push(criteriaNode.process(this));
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
const cond1 = [this._cond, criteriaNode.process(this)];
|
|
154
|
-
this._cond = { [op]: cond1 };
|
|
155
|
-
}
|
|
156
|
-
if (this._onConflict) {
|
|
157
|
-
this._onConflict[this._onConflict.length - 1].where = this._cond;
|
|
158
|
-
this._cond = {};
|
|
159
|
-
}
|
|
160
|
-
return this;
|
|
161
|
-
}
|
|
162
|
-
andWhere(cond, params) {
|
|
163
|
-
return this.where(cond, params, '$and');
|
|
164
|
-
}
|
|
165
|
-
orWhere(cond, params) {
|
|
166
|
-
return this.where(cond, params, '$or');
|
|
167
|
-
}
|
|
168
|
-
orderBy(orderBy) {
|
|
169
|
-
this._orderBy = [];
|
|
170
|
-
core_1.Utils.asArray(orderBy).forEach(o => {
|
|
171
|
-
const processed = core_1.QueryHelper.processWhere(o, this.entityName, this.metadata, this.platform, false);
|
|
172
|
-
this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, processed).process(this));
|
|
173
|
-
});
|
|
174
|
-
return this;
|
|
175
|
-
}
|
|
176
|
-
groupBy(fields) {
|
|
177
|
-
this._groupBy = core_1.Utils.asArray(fields);
|
|
178
|
-
return this;
|
|
179
|
-
}
|
|
180
|
-
having(cond = {}, params) {
|
|
181
|
-
if (core_1.Utils.isString(cond)) {
|
|
182
|
-
cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
|
|
183
|
-
}
|
|
184
|
-
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond).process(this);
|
|
185
|
-
return this;
|
|
186
|
-
}
|
|
187
|
-
onConflict(fields = []) {
|
|
188
|
-
this._onConflict = this._onConflict || [];
|
|
189
|
-
this._onConflict.push({ fields: core_1.Utils.asArray(fields) });
|
|
190
|
-
return this;
|
|
191
|
-
}
|
|
192
|
-
ignore() {
|
|
193
|
-
if (!this._onConflict) {
|
|
194
|
-
throw new Error('You need to call `qb.onConflict()` first to use `qb.ignore()`');
|
|
195
|
-
}
|
|
196
|
-
this._onConflict[this._onConflict.length - 1].ignore = true;
|
|
197
|
-
return this;
|
|
198
|
-
}
|
|
199
|
-
merge(data) {
|
|
200
|
-
if (!this._onConflict) {
|
|
201
|
-
throw new Error('You need to call `qb.onConflict()` first to use `qb.merge()`');
|
|
202
|
-
}
|
|
203
|
-
this._onConflict[this._onConflict.length - 1].merge = data;
|
|
204
|
-
return this;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* @internal
|
|
208
|
-
*/
|
|
209
|
-
populate(populate) {
|
|
210
|
-
this._populate = populate;
|
|
211
|
-
return this;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* @internal
|
|
215
|
-
*/
|
|
216
|
-
ref(field) {
|
|
217
|
-
return this.knex.ref(field);
|
|
218
|
-
}
|
|
219
|
-
raw(sql, bindings = []) {
|
|
220
|
-
const raw = this.knex.raw(sql, bindings);
|
|
221
|
-
raw.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
222
|
-
return raw;
|
|
223
|
-
}
|
|
224
|
-
limit(limit, offset = 0) {
|
|
225
|
-
this._limit = limit;
|
|
226
|
-
if (offset) {
|
|
227
|
-
this.offset(offset);
|
|
228
|
-
}
|
|
229
|
-
return this;
|
|
230
|
-
}
|
|
231
|
-
offset(offset) {
|
|
232
|
-
this._offset = offset;
|
|
233
|
-
return this;
|
|
234
|
-
}
|
|
235
|
-
withSchema(schema) {
|
|
236
|
-
this._schema = schema;
|
|
237
|
-
return this;
|
|
238
|
-
}
|
|
239
|
-
setLockMode(mode, tables) {
|
|
240
|
-
if (mode != null && mode !== core_1.LockMode.OPTIMISTIC && !this.context) {
|
|
241
|
-
throw core_1.ValidationError.transactionRequired();
|
|
242
|
-
}
|
|
243
|
-
this.lockMode = mode;
|
|
244
|
-
this.lockTables = tables;
|
|
245
|
-
return this;
|
|
246
|
-
}
|
|
247
|
-
setFlushMode(flushMode) {
|
|
248
|
-
this.flushMode = flushMode;
|
|
249
|
-
return this;
|
|
250
|
-
}
|
|
251
|
-
setFlag(flag) {
|
|
252
|
-
this.flags.add(flag);
|
|
253
|
-
return this;
|
|
254
|
-
}
|
|
255
|
-
unsetFlag(flag) {
|
|
256
|
-
this.flags.delete(flag);
|
|
257
|
-
return this;
|
|
258
|
-
}
|
|
259
|
-
cache(config = true) {
|
|
260
|
-
this._cache = config;
|
|
261
|
-
return this;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Adds index hint to the FROM clause.
|
|
265
|
-
*/
|
|
266
|
-
indexHint(sql) {
|
|
267
|
-
this._indexHint = sql;
|
|
268
|
-
return this;
|
|
269
|
-
}
|
|
270
|
-
getKnexQuery() {
|
|
271
|
-
this.finalize();
|
|
272
|
-
const qb = this.getQueryBase();
|
|
273
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond && !this._onConflict);
|
|
274
|
-
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
275
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._having, qb, undefined, 'having'), this._having);
|
|
276
|
-
core_1.Utils.runIfNotEmpty(() => {
|
|
277
|
-
const queryOrder = this.helper.getQueryOrder(this.type, this._orderBy, this._populateMap);
|
|
278
|
-
if (queryOrder) {
|
|
279
|
-
return qb.orderByRaw(queryOrder);
|
|
280
|
-
}
|
|
281
|
-
}, this._orderBy);
|
|
282
|
-
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
283
|
-
core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
|
|
284
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type, this._onConflict, qb), this._onConflict);
|
|
285
|
-
if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
|
|
286
|
-
return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
|
|
287
|
-
}
|
|
288
|
-
if (this.lockMode) {
|
|
289
|
-
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
290
|
-
}
|
|
291
|
-
this.helper.finalize(this.type, qb, this.metadata.find(this.entityName));
|
|
292
|
-
return qb;
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Returns the query with parameters as wildcards.
|
|
296
|
-
*/
|
|
297
|
-
getQuery() {
|
|
298
|
-
return this.getKnexQuery().toSQL().toNative().sql;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Returns the list of all parameters for this query.
|
|
302
|
-
*/
|
|
303
|
-
getParams() {
|
|
304
|
-
return this.getKnexQuery().toSQL().toNative().bindings;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Returns raw interpolated query string with all the parameters inlined.
|
|
308
|
-
*/
|
|
309
|
-
getFormattedQuery() {
|
|
310
|
-
const query = this.getKnexQuery().toSQL();
|
|
311
|
-
return this.platform.formatQuery(query.sql, query.bindings);
|
|
312
|
-
}
|
|
313
|
-
getAliasForJoinPath(path) {
|
|
314
|
-
if (!path || path === this.entityName) {
|
|
315
|
-
return this.alias;
|
|
316
|
-
}
|
|
317
|
-
const join = Object.values(this._joins).find(j => j.path === path);
|
|
318
|
-
if (path.endsWith('[pivot]') && join) {
|
|
319
|
-
return join.alias;
|
|
320
|
-
}
|
|
321
|
-
return join?.inverseAlias || join?.alias;
|
|
322
|
-
}
|
|
323
|
-
getNextAlias(entityName = 'e') {
|
|
324
|
-
return this.driver.config.getNamingStrategy().aliasName(entityName, this.aliasCounter++);
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
|
|
328
|
-
* Use `method` to specify what kind of result you want to get (array/single/meta).
|
|
329
|
-
*/
|
|
330
|
-
async execute(method = 'all', mapResults = true) {
|
|
331
|
-
if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type)) {
|
|
332
|
-
this.connectionType = 'write';
|
|
333
|
-
}
|
|
334
|
-
const query = this.getKnexQuery().toSQL();
|
|
335
|
-
const cached = await this.em?.tryCache(this.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
|
|
336
|
-
if (cached?.data) {
|
|
337
|
-
return cached.data;
|
|
338
|
-
}
|
|
339
|
-
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
|
|
340
|
-
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
|
|
341
|
-
const meta = this.metadata.find(this.entityName);
|
|
342
|
-
if (!mapResults || !meta) {
|
|
343
|
-
await this.em?.storeCache(this._cache, cached, res);
|
|
344
|
-
return res;
|
|
345
|
-
}
|
|
346
|
-
if (method === 'all' && Array.isArray(res)) {
|
|
347
|
-
const map = {};
|
|
348
|
-
const mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
|
|
349
|
-
await this.em?.storeCache(this._cache, cached, mapped);
|
|
350
|
-
return mapped;
|
|
351
|
-
}
|
|
352
|
-
const mapped = this.driver.mapResult(res, meta, this._populate, this);
|
|
353
|
-
await this.em?.storeCache(this._cache, cached, mapped);
|
|
354
|
-
return mapped;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Alias for `qb.getResultList()`
|
|
358
|
-
*/
|
|
359
|
-
async getResult() {
|
|
360
|
-
return this.getResultList();
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Executes the query, returning array of results
|
|
364
|
-
*/
|
|
365
|
-
async getResultList() {
|
|
366
|
-
await this.em.tryFlush(this.entityName, { flushMode: this.flushMode });
|
|
367
|
-
let res = await this.execute('all', true);
|
|
368
|
-
if (this._joinedProps.size > 0) {
|
|
369
|
-
res = this.driver.mergeJoinedResult(res, this.metadata.find(this.entityName));
|
|
370
|
-
}
|
|
371
|
-
return res.map(r => this.em.map(this.entityName, r, { schema: this._schema }));
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Executes the query, returning the first result or null
|
|
375
|
-
*/
|
|
376
|
-
async getSingleResult() {
|
|
377
|
-
const res = await this.getResultList();
|
|
378
|
-
return res[0] || null;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Executes count query (without offset and limit), returning total count of results
|
|
382
|
-
*/
|
|
383
|
-
async getCount(field, distinct = false) {
|
|
384
|
-
const qb = this.clone();
|
|
385
|
-
qb.count(field, distinct).limit(undefined).offset(undefined);
|
|
386
|
-
const res = await qb.execute('get', false);
|
|
387
|
-
return res ? +res.count : 0;
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Provides promise-like interface so we can await the QB instance.
|
|
391
|
-
*/
|
|
392
|
-
then(onfulfilled, onrejected) {
|
|
393
|
-
return this.getInnerPromise().then(onfulfilled, onrejected);
|
|
394
|
-
}
|
|
395
|
-
getInnerPromise() {
|
|
396
|
-
if (!this.innerPromise) {
|
|
397
|
-
this.innerPromise = (async () => {
|
|
398
|
-
switch (this.type) {
|
|
399
|
-
case enums_1.QueryType.INSERT:
|
|
400
|
-
case enums_1.QueryType.UPDATE:
|
|
401
|
-
case enums_1.QueryType.DELETE:
|
|
402
|
-
case enums_1.QueryType.TRUNCATE:
|
|
403
|
-
return this.execute('run');
|
|
404
|
-
case enums_1.QueryType.SELECT:
|
|
405
|
-
return this.getResultList();
|
|
406
|
-
case enums_1.QueryType.COUNT:
|
|
407
|
-
return this.getCount();
|
|
408
|
-
}
|
|
409
|
-
})();
|
|
410
|
-
}
|
|
411
|
-
return this.innerPromise;
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* Returns knex instance with sub-query aliased with given alias.
|
|
415
|
-
* You can provide `EntityName.propName` as alias, then the field name will be used based on the metadata
|
|
416
|
-
*/
|
|
417
|
-
as(alias) {
|
|
418
|
-
const qb = this.getKnexQuery();
|
|
419
|
-
if (alias.includes('.')) {
|
|
420
|
-
const [a, f] = alias.split('.');
|
|
421
|
-
const meta = this.metadata.find(a);
|
|
422
|
-
/* istanbul ignore next */
|
|
423
|
-
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
424
|
-
}
|
|
425
|
-
return qb.as(alias);
|
|
426
|
-
}
|
|
427
|
-
clone() {
|
|
428
|
-
const qb = new QueryBuilder(this.entityName, this.metadata, this.driver, this.context, this.alias, this.connectionType, this.em);
|
|
429
|
-
Object.assign(qb, this);
|
|
430
|
-
// clone array/object properties
|
|
431
|
-
const properties = [
|
|
432
|
-
'flags', '_populate', '_populateMap', '_joins', '_joinedProps', '_aliasMap', '_cond', '_data', '_orderBy',
|
|
433
|
-
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
|
|
434
|
-
];
|
|
435
|
-
properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
|
|
436
|
-
/* istanbul ignore else */
|
|
437
|
-
if (this._fields) {
|
|
438
|
-
qb._fields = [...this._fields];
|
|
439
|
-
}
|
|
440
|
-
qb.finalized = false;
|
|
441
|
-
return qb;
|
|
442
|
-
}
|
|
443
|
-
getKnex() {
|
|
444
|
-
const tableName = this.helper.getTableName(this.entityName) + (this.finalized && this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.alias}` : '');
|
|
445
|
-
const qb = this.knex(tableName);
|
|
446
|
-
if (this.context) {
|
|
447
|
-
qb.transacting(this.context);
|
|
448
|
-
}
|
|
449
|
-
return qb;
|
|
450
|
-
}
|
|
451
|
-
joinReference(field, alias, cond, type, path) {
|
|
452
|
-
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
453
|
-
const entityName = this._aliasMap[fromAlias];
|
|
454
|
-
const meta = this.metadata.get(entityName);
|
|
455
|
-
const prop = meta.properties[fromField];
|
|
456
|
-
if (!prop) {
|
|
457
|
-
throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
|
|
458
|
-
}
|
|
459
|
-
this._aliasMap[alias] = prop.type;
|
|
460
|
-
cond = core_1.QueryHelper.processWhere(cond, this.entityName, this.metadata, this.platform);
|
|
461
|
-
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
462
|
-
path ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
|
|
463
|
-
if (prop.reference === core_1.ReferenceType.ONE_TO_MANY) {
|
|
464
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
465
|
-
}
|
|
466
|
-
else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
|
|
467
|
-
let pivotAlias = alias;
|
|
468
|
-
if (type !== 'pivotJoin') {
|
|
469
|
-
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
470
|
-
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotTable);
|
|
471
|
-
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
472
|
-
}
|
|
473
|
-
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
|
|
474
|
-
Object.assign(this._joins, joins);
|
|
475
|
-
this._aliasMap[pivotAlias] = prop.pivotTable;
|
|
476
|
-
}
|
|
477
|
-
else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
|
|
478
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
479
|
-
}
|
|
480
|
-
else { // MANY_TO_ONE
|
|
481
|
-
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
|
|
482
|
-
}
|
|
483
|
-
if (!this._joins[aliasedName].path && path) {
|
|
484
|
-
this._joins[aliasedName].path = path;
|
|
485
|
-
}
|
|
486
|
-
return prop;
|
|
487
|
-
}
|
|
488
|
-
prepareFields(fields, type = 'where') {
|
|
489
|
-
const ret = [];
|
|
490
|
-
fields.forEach(field => {
|
|
491
|
-
if (!core_1.Utils.isString(field)) {
|
|
492
|
-
return ret.push(field);
|
|
493
|
-
}
|
|
494
|
-
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
495
|
-
if (join && type === 'where') {
|
|
496
|
-
return ret.push(...this.helper.mapJoinColumns(this.type, this._joins[join]));
|
|
497
|
-
}
|
|
498
|
-
const [a, f] = this.helper.splitField(field);
|
|
499
|
-
const prop = this.helper.getProperty(f, a);
|
|
500
|
-
if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
ret.push(this.helper.mapper(field, this.type));
|
|
504
|
-
});
|
|
505
|
-
const meta = this.metadata.find(this.entityName);
|
|
506
|
-
/* istanbul ignore next */
|
|
507
|
-
const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
|
|
508
|
-
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.alias}.*`)) && requiresSQLConversion.length > 0) {
|
|
509
|
-
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
510
|
-
}
|
|
511
|
-
Object.keys(this._populateMap).forEach(f => {
|
|
512
|
-
if (!fields.includes(f.replace(/#\w+$/, '')) && type === 'where') {
|
|
513
|
-
ret.push(...this.helper.mapJoinColumns(this.type, this._joins[f]));
|
|
514
|
-
}
|
|
515
|
-
if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
516
|
-
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
517
|
-
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
return ret;
|
|
522
|
-
}
|
|
523
|
-
init(type, data, cond) {
|
|
524
|
-
this.type = type;
|
|
525
|
-
this._aliasMap[this.alias] = this.entityName;
|
|
526
|
-
if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(type) && core_1.Utils.hasObjectKeys(this._cond)) {
|
|
527
|
-
throw new Error(`You are trying to call \`qb.where().${type.toLowerCase()}()\`. Calling \`qb.${type.toLowerCase()}()\` before \`qb.where()\` is required.`);
|
|
528
|
-
}
|
|
529
|
-
if (!this.helper.isTableNameAliasRequired(type)) {
|
|
530
|
-
delete this._fields;
|
|
531
|
-
}
|
|
532
|
-
if (data) {
|
|
533
|
-
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
534
|
-
}
|
|
535
|
-
if (cond) {
|
|
536
|
-
this.where(cond);
|
|
537
|
-
}
|
|
538
|
-
return this;
|
|
539
|
-
}
|
|
540
|
-
getQueryBase() {
|
|
541
|
-
const qb = this.getKnex();
|
|
542
|
-
const meta = this.metadata.find(this.entityName);
|
|
543
|
-
const metaSchema = meta?.schema && meta.schema !== '*' ? meta.schema : undefined;
|
|
544
|
-
const schema = this._schema ?? metaSchema ?? this.em?.config.get('schema');
|
|
545
|
-
if (schema) {
|
|
546
|
-
qb.withSchema(schema);
|
|
547
|
-
}
|
|
548
|
-
if (this._indexHint) {
|
|
549
|
-
const alias = this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.platform.quoteIdentifier(this.alias)}` : '';
|
|
550
|
-
const schemaQuoted = schema ? this.platform.quoteIdentifier(schema) + '.' : '';
|
|
551
|
-
const tableName = schemaQuoted + this.platform.quoteIdentifier(this.helper.getTableName(this.entityName)) + alias;
|
|
552
|
-
qb.from(this.knex.raw(`${tableName} ${this._indexHint}`));
|
|
553
|
-
}
|
|
554
|
-
switch (this.type) {
|
|
555
|
-
case enums_1.QueryType.SELECT:
|
|
556
|
-
qb.select(this.prepareFields(this._fields));
|
|
557
|
-
if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
|
|
558
|
-
qb.distinct();
|
|
559
|
-
}
|
|
560
|
-
this.helper.processJoins(qb, this._joins, schema);
|
|
561
|
-
break;
|
|
562
|
-
case enums_1.QueryType.COUNT: {
|
|
563
|
-
const m = this.flags.has(core_1.QueryFlag.DISTINCT) ? 'countDistinct' : 'count';
|
|
564
|
-
qb[m]({ count: this._fields.map(f => this.helper.mapper(f, this.type)) });
|
|
565
|
-
this.helper.processJoins(qb, this._joins, schema);
|
|
566
|
-
break;
|
|
567
|
-
}
|
|
568
|
-
case enums_1.QueryType.INSERT:
|
|
569
|
-
qb.insert(this._data);
|
|
570
|
-
break;
|
|
571
|
-
case enums_1.QueryType.UPDATE:
|
|
572
|
-
qb.update(this._data);
|
|
573
|
-
this.helper.updateVersionProperty(qb, this._data);
|
|
574
|
-
break;
|
|
575
|
-
case enums_1.QueryType.DELETE:
|
|
576
|
-
qb.delete();
|
|
577
|
-
break;
|
|
578
|
-
case enums_1.QueryType.TRUNCATE:
|
|
579
|
-
qb.truncate();
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
|
-
return qb;
|
|
583
|
-
}
|
|
584
|
-
finalize() {
|
|
585
|
-
if (this.finalized) {
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
if (!this.type) {
|
|
589
|
-
this.select('*');
|
|
590
|
-
}
|
|
591
|
-
const meta = this.metadata.find(this.entityName);
|
|
592
|
-
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
593
|
-
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
594
|
-
meta.relations
|
|
595
|
-
.filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
596
|
-
.map(prop => ({ field: prop.name }))
|
|
597
|
-
.forEach(item => this._populate.push(item));
|
|
598
|
-
}
|
|
599
|
-
this._populate.forEach(({ field }) => {
|
|
600
|
-
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
601
|
-
const aliasedField = `${fromAlias}.${fromField}`;
|
|
602
|
-
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
603
|
-
if (this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
604
|
-
return this._populateMap[join] = this._joins[join].alias;
|
|
605
|
-
}
|
|
606
|
-
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
607
|
-
this.autoJoinPivotTable(field);
|
|
608
|
-
}
|
|
609
|
-
else if (meta && this.helper.isOneToOneInverse(fromField)) {
|
|
610
|
-
const prop = meta.properties[fromField];
|
|
611
|
-
const alias = this.getNextAlias(prop.pivotTable ?? prop.type);
|
|
612
|
-
this._joins[join] = this.helper.joinOneToReference(prop, this.alias, alias, 'leftJoin');
|
|
613
|
-
this._populateMap[join] = this._joins[join].alias;
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.alias}.*`))) {
|
|
617
|
-
meta.props
|
|
618
|
-
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(core_1.QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
619
|
-
.map(prop => {
|
|
620
|
-
const alias = this.knex.ref(this.alias).toString();
|
|
621
|
-
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
622
|
-
return `${prop.formula(alias)} as ${aliased}`;
|
|
623
|
-
})
|
|
624
|
-
.filter(field => !this._fields.includes(field))
|
|
625
|
-
.forEach(field => this.addSelect(field));
|
|
626
|
-
}
|
|
627
|
-
core_1.QueryHelper.processObjectParams(this._data);
|
|
628
|
-
core_1.QueryHelper.processObjectParams(this._cond);
|
|
629
|
-
core_1.QueryHelper.processObjectParams(this._having);
|
|
630
|
-
this.finalized = true;
|
|
631
|
-
if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && this._limit > 0) {
|
|
632
|
-
this.wrapPaginateSubQuery(meta);
|
|
633
|
-
}
|
|
634
|
-
if (meta && (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY))) {
|
|
635
|
-
this.wrapModifySubQuery(meta);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
wrapPaginateSubQuery(meta) {
|
|
639
|
-
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
640
|
-
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
641
|
-
if (this._offset) {
|
|
642
|
-
subQuery.offset(this._offset);
|
|
643
|
-
}
|
|
644
|
-
if (this._orderBy.length > 0) {
|
|
645
|
-
const orderBy = [];
|
|
646
|
-
for (const orderMap of this._orderBy) {
|
|
647
|
-
for (const [field, direction] of Object.entries(orderMap)) {
|
|
648
|
-
orderBy.push({
|
|
649
|
-
[`min(${this.ref(field)})`]: direction,
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
subQuery.orderBy(orderBy);
|
|
654
|
-
}
|
|
655
|
-
subQuery.finalized = true;
|
|
656
|
-
const knexQuery = subQuery.as(this.alias).clearSelect().select(pks);
|
|
657
|
-
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
658
|
-
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
659
|
-
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
660
|
-
this._limit = undefined;
|
|
661
|
-
this._offset = undefined;
|
|
662
|
-
this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
|
|
663
|
-
}
|
|
664
|
-
wrapModifySubQuery(meta) {
|
|
665
|
-
const subQuery = this.clone();
|
|
666
|
-
subQuery.finalized = true;
|
|
667
|
-
// wrap one more time to get around MySQL limitations
|
|
668
|
-
// https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
|
|
669
|
-
const subSubQuery = this.getKnex().select(this.prepareFields(meta.primaryKeys)).from(subQuery.as(this.alias));
|
|
670
|
-
const method = this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
|
|
671
|
-
this._cond = {}; // otherwise we would trigger validation error
|
|
672
|
-
this[method](this._data).where({
|
|
673
|
-
[core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
autoJoinPivotTable(field) {
|
|
677
|
-
const pivotMeta = this.metadata.find(field);
|
|
678
|
-
const owner = pivotMeta.props.find(prop => prop.reference === core_1.ReferenceType.MANY_TO_ONE && prop.owner);
|
|
679
|
-
const inverse = pivotMeta.props.find(prop => prop.reference === core_1.ReferenceType.MANY_TO_ONE && !prop.owner);
|
|
680
|
-
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
681
|
-
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
682
|
-
this._joins[field] = this.helper.joinPivotTable(field, prop, this.alias, pivotAlias, 'leftJoin');
|
|
683
|
-
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
684
|
-
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
685
|
-
this._populateMap[field] = this._joins[field].alias;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
exports.QueryBuilder = QueryBuilder;
|