@mikro-orm/sql 7.0.0-dev.112 → 7.0.0-dev.114
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/AbstractSqlDriver.js +34 -28
- package/package.json +2 -2
- package/query/CriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.js +9 -11
- package/query/CriteriaNodeFactory.d.ts +4 -4
- package/query/CriteriaNodeFactory.js +18 -16
- package/query/NativeQueryBuilder.js +1 -2
- package/query/ObjectCriteriaNode.js +23 -23
- package/query/QueryBuilder.d.ts +3 -9
- package/query/QueryBuilder.js +23 -43
- package/query/QueryBuilderHelper.d.ts +6 -5
- package/query/QueryBuilderHelper.js +52 -32
- package/schema/DatabaseSchema.js +3 -7
- package/schema/SchemaHelper.d.ts +2 -2
- package/schema/SchemaHelper.js +2 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +1 -1
package/AbstractSqlDriver.js
CHANGED
|
@@ -167,7 +167,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
167
167
|
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
|
|
168
168
|
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
169
169
|
qb.setFlag(QueryFlag.DISABLE_PAGINATE);
|
|
170
|
-
const isCursorPagination = [
|
|
170
|
+
const isCursorPagination = [
|
|
171
|
+
options.first,
|
|
172
|
+
options.last,
|
|
173
|
+
options.before,
|
|
174
|
+
options.after,
|
|
175
|
+
].some(v => v != null);
|
|
171
176
|
const native = qb.getNativeQuery(false);
|
|
172
177
|
if (type === QueryType.COUNT) {
|
|
173
178
|
native
|
|
@@ -830,7 +835,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
830
835
|
populateWhere: undefined,
|
|
831
836
|
// @ts-ignore
|
|
832
837
|
_populateWhere: 'infer',
|
|
833
|
-
populateFilter: !Utils.isEmpty(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
|
|
838
|
+
populateFilter: !Utils.isEmpty(options?.populateFilter) || RawQueryFragment.hasObjectFragments(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
|
|
834
839
|
});
|
|
835
840
|
const map = {};
|
|
836
841
|
for (const owner of owners) {
|
|
@@ -844,7 +849,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
844
849
|
return map;
|
|
845
850
|
}
|
|
846
851
|
getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
|
|
847
|
-
if (!Utils.isEmpty(orderBy)) {
|
|
852
|
+
if (!Utils.isEmpty(orderBy) || RawQueryFragment.hasObjectFragments(orderBy)) {
|
|
848
853
|
return Utils.asArray(orderBy).map(o => ({ [pivotProp.name]: o }));
|
|
849
854
|
}
|
|
850
855
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && Utils.asArray(parentOrderBy).some(o => o[prop.name])) {
|
|
@@ -852,7 +857,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
852
857
|
.filter(o => o[prop.name])
|
|
853
858
|
.map(o => ({ [pivotProp.name]: o[prop.name] }));
|
|
854
859
|
}
|
|
855
|
-
if (!Utils.isEmpty(prop.orderBy)) {
|
|
860
|
+
if (!Utils.isEmpty(prop.orderBy) || RawQueryFragment.hasObjectFragments(prop.orderBy)) {
|
|
856
861
|
return Utils.asArray(prop.orderBy).map(o => ({ [pivotProp.name]: o }));
|
|
857
862
|
}
|
|
858
863
|
if (prop.fixedOrder) {
|
|
@@ -1152,21 +1157,21 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1152
1157
|
for (const hint of joinedProps) {
|
|
1153
1158
|
const [propName] = hint.field.split(':', 2);
|
|
1154
1159
|
const prop = meta.properties[propName];
|
|
1155
|
-
if (!Utils.isEmpty(prop.where)) {
|
|
1160
|
+
if (!Utils.isEmpty(prop.where) || RawQueryFragment.hasObjectFragments(prop.where)) {
|
|
1156
1161
|
where[prop.name] = Utils.copy(prop.where);
|
|
1157
1162
|
}
|
|
1158
1163
|
if (hint.children) {
|
|
1159
1164
|
const inner = this.buildPopulateWhere(prop.targetMeta, hint.children, {});
|
|
1160
|
-
if (!Utils.isEmpty(inner)) {
|
|
1165
|
+
if (!Utils.isEmpty(inner) || RawQueryFragment.hasObjectFragments(inner)) {
|
|
1161
1166
|
where[prop.name] ??= {};
|
|
1162
1167
|
Object.assign(where[prop.name], inner);
|
|
1163
1168
|
}
|
|
1164
1169
|
}
|
|
1165
1170
|
}
|
|
1166
|
-
if (Utils.isEmpty(options.populateWhere)) {
|
|
1171
|
+
if (Utils.isEmpty(options.populateWhere) && !RawQueryFragment.hasObjectFragments(options.populateWhere)) {
|
|
1167
1172
|
return where;
|
|
1168
1173
|
}
|
|
1169
|
-
if (Utils.isEmpty(where)) {
|
|
1174
|
+
if (Utils.isEmpty(where) && !RawQueryFragment.hasObjectFragments(where)) {
|
|
1170
1175
|
return options.populateWhere;
|
|
1171
1176
|
}
|
|
1172
1177
|
/* v8 ignore next */
|
|
@@ -1178,31 +1183,31 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1178
1183
|
// as `options.populateWhere` will be always recomputed to respect filters
|
|
1179
1184
|
const populateWhereAll = options._populateWhere !== 'infer' && !Utils.isEmpty(options._populateWhere);
|
|
1180
1185
|
const path = (populateWhereAll ? '[populate]' : '') + meta.className;
|
|
1186
|
+
const optionsOrderBy = Utils.asArray(options.orderBy);
|
|
1181
1187
|
const populateOrderBy = this.buildPopulateOrderBy(qb, meta, Utils.asArray(options.populateOrderBy ?? options.orderBy), path, !!options.populateOrderBy);
|
|
1182
1188
|
const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(qb, meta, joinedProps, options, path);
|
|
1183
|
-
return [...
|
|
1189
|
+
return [...optionsOrderBy, ...populateOrderBy, ...joinedPropsOrderBy];
|
|
1184
1190
|
}
|
|
1185
1191
|
buildPopulateOrderBy(qb, meta, populateOrderBy, parentPath, explicit, parentAlias = qb.alias) {
|
|
1186
1192
|
const orderBy = [];
|
|
1187
1193
|
for (let i = 0; i < populateOrderBy.length; i++) {
|
|
1188
1194
|
const orderHint = populateOrderBy[i];
|
|
1189
|
-
for (const
|
|
1190
|
-
const
|
|
1191
|
-
if (
|
|
1192
|
-
const sql =
|
|
1193
|
-
const
|
|
1194
|
-
orderBy.push({ [
|
|
1195
|
+
for (const field of Utils.getObjectQueryKeys(orderHint)) {
|
|
1196
|
+
const childOrder = orderHint[field];
|
|
1197
|
+
if (RawQueryFragment.isKnownFragmentSymbol(field)) {
|
|
1198
|
+
const { sql, params } = RawQueryFragment.getKnownFragment(field);
|
|
1199
|
+
const key = raw(sql.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), parentAlias), params);
|
|
1200
|
+
orderBy.push({ [key]: childOrder });
|
|
1195
1201
|
continue;
|
|
1196
1202
|
}
|
|
1197
|
-
const prop = meta.properties[
|
|
1203
|
+
const prop = meta.properties[field];
|
|
1198
1204
|
if (!prop) {
|
|
1199
|
-
throw new Error(`Trying to order by not existing property ${meta.className}.${
|
|
1205
|
+
throw new Error(`Trying to order by not existing property ${meta.className}.${field}`);
|
|
1200
1206
|
}
|
|
1201
1207
|
let path = parentPath;
|
|
1202
1208
|
const meta2 = this.metadata.find(prop.type);
|
|
1203
|
-
const childOrder = orderHint[prop.name];
|
|
1204
1209
|
if (prop.kind !== ReferenceKind.SCALAR && (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder))) {
|
|
1205
|
-
path += `.${
|
|
1210
|
+
path += `.${field}`;
|
|
1206
1211
|
}
|
|
1207
1212
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
|
|
1208
1213
|
path += '[pivot]';
|
|
@@ -1228,9 +1233,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1228
1233
|
}
|
|
1229
1234
|
continue;
|
|
1230
1235
|
}
|
|
1231
|
-
const order = typeof childOrder === 'object' ? childOrder[
|
|
1236
|
+
const order = typeof childOrder === 'object' ? childOrder[field] : childOrder;
|
|
1232
1237
|
if (order) {
|
|
1233
|
-
orderBy.push({ [`${propAlias}.${
|
|
1238
|
+
orderBy.push({ [`${propAlias}.${field}`]: order });
|
|
1234
1239
|
}
|
|
1235
1240
|
}
|
|
1236
1241
|
}
|
|
@@ -1256,15 +1261,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1256
1261
|
}
|
|
1257
1262
|
if (propOrderBy) {
|
|
1258
1263
|
for (const item of Utils.asArray(propOrderBy)) {
|
|
1259
|
-
for (const field of Utils.
|
|
1260
|
-
const
|
|
1261
|
-
if (
|
|
1262
|
-
const sql
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1264
|
+
for (const field of Utils.getObjectQueryKeys(item)) {
|
|
1265
|
+
const order = item[field];
|
|
1266
|
+
if (RawQueryFragment.isKnownFragmentSymbol(field)) {
|
|
1267
|
+
const { sql, params } = RawQueryFragment.getKnownFragment(field);
|
|
1268
|
+
const sql2 = propAlias ? sql.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), propAlias) : sql;
|
|
1269
|
+
const key = raw(sql2, params);
|
|
1270
|
+
orderBy.push({ [key]: order });
|
|
1265
1271
|
continue;
|
|
1266
1272
|
}
|
|
1267
|
-
orderBy.push({ [`${propAlias}.${field}`]:
|
|
1273
|
+
orderBy.push({ [`${propAlias}.${field}`]: order });
|
|
1268
1274
|
}
|
|
1269
1275
|
}
|
|
1270
1276
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.0.0-dev.
|
|
3
|
+
"version": "7.0.0-dev.114",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"@mikro-orm/core": "^6.6.2"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@mikro-orm/core": "7.0.0-dev.
|
|
59
|
+
"@mikro-orm/core": "7.0.0-dev.114"
|
|
60
60
|
}
|
|
61
61
|
}
|
package/query/CriteriaNode.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EntityKey, type EntityProperty, type MetadataStorage } from '@mikro-orm/core';
|
|
1
|
+
import { type EntityKey, type EntityProperty, type MetadataStorage, type RawQueryFragmentSymbol } from '@mikro-orm/core';
|
|
2
2
|
import type { ICriteriaNode, ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
|
|
3
3
|
/**
|
|
4
4
|
* Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
|
|
@@ -9,12 +9,12 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
|
|
|
9
9
|
protected readonly metadata: MetadataStorage;
|
|
10
10
|
readonly entityName: string;
|
|
11
11
|
readonly parent?: ICriteriaNode<T> | undefined;
|
|
12
|
-
readonly key?: EntityKey<T> | undefined;
|
|
12
|
+
readonly key?: (EntityKey<T> | RawQueryFragmentSymbol) | undefined;
|
|
13
13
|
readonly strict: boolean;
|
|
14
14
|
payload: any;
|
|
15
15
|
prop?: EntityProperty<T>;
|
|
16
16
|
index?: number;
|
|
17
|
-
constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean, strict?: boolean);
|
|
17
|
+
constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: (EntityKey<T> | RawQueryFragmentSymbol) | undefined, validate?: boolean, strict?: boolean);
|
|
18
18
|
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
19
19
|
unwrap(): any;
|
|
20
20
|
shouldInline(payload: any): boolean;
|
package/query/CriteriaNode.js
CHANGED
|
@@ -20,7 +20,7 @@ export class CriteriaNode {
|
|
|
20
20
|
this.key = key;
|
|
21
21
|
this.strict = strict;
|
|
22
22
|
const meta = parent && metadata.find(parent.entityName);
|
|
23
|
-
if (meta && key) {
|
|
23
|
+
if (meta && key && !RawQueryFragment.isKnownFragmentSymbol(key)) {
|
|
24
24
|
const pks = Utils.splitPrimaryKeys(key);
|
|
25
25
|
if (pks.length > 1) {
|
|
26
26
|
return;
|
|
@@ -29,7 +29,7 @@ export class CriteriaNode {
|
|
|
29
29
|
this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false));
|
|
30
30
|
const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k));
|
|
31
31
|
// do not validate if the key is prefixed or type casted (e.g. `k::text`)
|
|
32
|
-
if (validate && !isProp && !k.includes('.') && !k.includes('::') && !Utils.isOperator(k)
|
|
32
|
+
if (validate && !isProp && !k.includes('.') && !k.includes('::') && !Utils.isOperator(k)) {
|
|
33
33
|
throw new Error(`Trying to query by not existing property ${entityName}.${k}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -50,11 +50,9 @@ export class CriteriaNode {
|
|
|
50
50
|
shouldRename(payload) {
|
|
51
51
|
const type = this.prop ? this.prop.kind : null;
|
|
52
52
|
const composite = this.prop?.joinColumns ? this.prop.joinColumns.length > 1 : false;
|
|
53
|
-
const
|
|
54
|
-
const scalar = payload === null || Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date ||
|
|
55
|
-
const
|
|
56
|
-
const keys = plainObject ? Object.keys(payload) : [];
|
|
57
|
-
const operator = plainObject && keys.every(k => Utils.isOperator(k, false));
|
|
53
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
54
|
+
const scalar = payload === null || Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
|
|
55
|
+
const operator = Utils.isPlainObject(payload) && Utils.getObjectQueryKeys(payload).every(k => Utils.isOperator(k, false));
|
|
58
56
|
if (composite) {
|
|
59
57
|
return true;
|
|
60
58
|
}
|
|
@@ -84,7 +82,7 @@ export class CriteriaNode {
|
|
|
84
82
|
const parentPath = this.parent?.getPath(addParentIndex) ?? this.entityName;
|
|
85
83
|
const index = addIndex && this.index != null ? `[${this.index}]` : '';
|
|
86
84
|
// ignore group operators to allow easier mapping (e.g. for orderBy)
|
|
87
|
-
const key = this.key && !['$and', '$or', '$not'].includes(this.key) ? '.' + this.key : '';
|
|
85
|
+
const key = this.key && !RawQueryFragment.isKnownFragmentSymbol(this.key) && !['$and', '$or', '$not'].includes(this.key) ? '.' + this.key : '';
|
|
88
86
|
const ret = parentPath + index + key;
|
|
89
87
|
if (this.isPivotJoin()) {
|
|
90
88
|
// distinguish pivot table join from target entity join
|
|
@@ -96,9 +94,9 @@ export class CriteriaNode {
|
|
|
96
94
|
if (!this.key || !this.prop) {
|
|
97
95
|
return false;
|
|
98
96
|
}
|
|
99
|
-
const
|
|
100
|
-
const scalar = this.payload === null || Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date ||
|
|
101
|
-
const operator = Utils.isObject(this.payload) &&
|
|
97
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
98
|
+
const scalar = this.payload === null || Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
|
|
99
|
+
const operator = Utils.isObject(this.payload) && Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
|
|
102
100
|
return this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator);
|
|
103
101
|
}
|
|
104
102
|
getPivotPath(path) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type Dictionary, type EntityKey, type EntityMetadata, type MetadataStorage } from '@mikro-orm/core';
|
|
1
|
+
import { type Dictionary, type EntityKey, type EntityMetadata, type MetadataStorage, type RawQueryFragmentSymbol } from '@mikro-orm/core';
|
|
2
2
|
import type { ICriteriaNode } from '../typings.js';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
export declare class CriteriaNodeFactory {
|
|
7
|
-
static createNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
|
|
8
|
-
static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
|
|
7
|
+
static createNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol): ICriteriaNode<T>;
|
|
8
|
+
static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol): ICriteriaNode<T>;
|
|
9
9
|
static createArrayNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any[], parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
|
|
10
10
|
static createObjectNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: Dictionary, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
|
|
11
|
-
static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: string, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T
|
|
11
|
+
static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: string, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T> | RawQueryFragmentSymbol, meta?: EntityMetadata<T>): ICriteriaNode<T>;
|
|
12
12
|
}
|
|
@@ -7,8 +7,8 @@ import { ScalarCriteriaNode } from './ScalarCriteriaNode.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export class CriteriaNodeFactory {
|
|
9
9
|
static createNode(metadata, entityName, payload, parent, key) {
|
|
10
|
-
const
|
|
11
|
-
const scalar = Utils.isPrimaryKey(payload) || isRaw(payload) || payload instanceof RegExp || payload instanceof Date ||
|
|
10
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
|
|
11
|
+
const scalar = Utils.isPrimaryKey(payload) || isRaw(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
|
|
12
12
|
if (Array.isArray(payload) && !scalar) {
|
|
13
13
|
return this.createArrayNode(metadata, entityName, payload, parent, key);
|
|
14
14
|
}
|
|
@@ -38,25 +38,27 @@ export class CriteriaNodeFactory {
|
|
|
38
38
|
const meta = metadata.find(entityName);
|
|
39
39
|
const node = new ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
|
|
40
40
|
node.payload = {};
|
|
41
|
-
for (const
|
|
42
|
-
node.payload[
|
|
41
|
+
for (const k of Utils.getObjectQueryKeys(payload)) {
|
|
42
|
+
node.payload[k] = this.createObjectItemNode(metadata, entityName, node, payload, k, meta);
|
|
43
43
|
}
|
|
44
44
|
return node;
|
|
45
45
|
}
|
|
46
46
|
static createObjectItemNode(metadata, entityName, node, payload, key, meta) {
|
|
47
|
-
const
|
|
47
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
|
|
48
|
+
const prop = rawField ? null : meta?.properties[key];
|
|
48
49
|
const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.type : entityName;
|
|
49
|
-
const isNotEmbedded = prop?.kind !== ReferenceKind.EMBEDDED;
|
|
50
|
+
const isNotEmbedded = rawField || prop?.kind !== ReferenceKind.EMBEDDED;
|
|
51
|
+
const val = payload[key];
|
|
50
52
|
if (isNotEmbedded && prop?.customType instanceof JsonType) {
|
|
51
|
-
return this.createScalarNode(metadata, childEntity,
|
|
53
|
+
return this.createScalarNode(metadata, childEntity, val, node, key);
|
|
52
54
|
}
|
|
53
|
-
if (prop?.kind === ReferenceKind.SCALAR &&
|
|
55
|
+
if (prop?.kind === ReferenceKind.SCALAR && val != null && Object.keys(val).some(f => f in GroupOperator)) {
|
|
54
56
|
throw ValidationError.cannotUseGroupOperatorsInsideScalars(entityName, prop.name, payload);
|
|
55
57
|
}
|
|
56
58
|
if (isNotEmbedded) {
|
|
57
|
-
return this.createNode(metadata, childEntity,
|
|
59
|
+
return this.createNode(metadata, childEntity, val, node, key);
|
|
58
60
|
}
|
|
59
|
-
if (
|
|
61
|
+
if (val == null) {
|
|
60
62
|
const map = Object.keys(prop.embeddedProps).reduce((oo, k) => {
|
|
61
63
|
oo[prop.embeddedProps[k].name] = null;
|
|
62
64
|
return oo;
|
|
@@ -65,23 +67,23 @@ export class CriteriaNodeFactory {
|
|
|
65
67
|
}
|
|
66
68
|
// array operators can be used on embedded properties
|
|
67
69
|
const allowedOperators = ['$contains', '$contained', '$overlap'];
|
|
68
|
-
const operator = Object.keys(
|
|
70
|
+
const operator = Object.keys(val).some(f => Utils.isOperator(f) && !allowedOperators.includes(f));
|
|
69
71
|
if (operator) {
|
|
70
72
|
throw ValidationError.cannotUseOperatorsInsideEmbeddables(entityName, prop.name, payload);
|
|
71
73
|
}
|
|
72
|
-
const map = Object.keys(
|
|
74
|
+
const map = Object.keys(val).reduce((oo, k) => {
|
|
73
75
|
const embeddedProp = prop.embeddedProps[k] ?? Object.values(prop.embeddedProps).find(p => p.name === k);
|
|
74
76
|
if (!embeddedProp && !allowedOperators.includes(k)) {
|
|
75
77
|
throw ValidationError.invalidEmbeddableQuery(entityName, k, prop.type);
|
|
76
78
|
}
|
|
77
79
|
if (embeddedProp) {
|
|
78
|
-
oo[embeddedProp.name] =
|
|
80
|
+
oo[embeddedProp.name] = val[k];
|
|
79
81
|
}
|
|
80
|
-
else if (typeof
|
|
81
|
-
oo[k] = JSON.stringify(
|
|
82
|
+
else if (typeof val[k] === 'object') {
|
|
83
|
+
oo[k] = JSON.stringify(val[k]);
|
|
82
84
|
}
|
|
83
85
|
else {
|
|
84
|
-
oo[k] =
|
|
86
|
+
oo[k] = val[k];
|
|
85
87
|
}
|
|
86
88
|
return oo;
|
|
87
89
|
}, {});
|
|
@@ -27,8 +27,7 @@ export class NativeQueryBuilder {
|
|
|
27
27
|
}
|
|
28
28
|
from(tableName, options) {
|
|
29
29
|
if (tableName instanceof NativeQueryBuilder) {
|
|
30
|
-
|
|
31
|
-
tableName = raw(sql, params);
|
|
30
|
+
tableName = tableName.toRaw();
|
|
32
31
|
}
|
|
33
32
|
if (typeof tableName === 'string') {
|
|
34
33
|
const alias = options?.alias ? ` as ${this.platform.quoteIdentifier(options.alias)}` : '';
|
|
@@ -9,7 +9,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
9
9
|
const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
|
|
10
10
|
const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
|
|
11
11
|
const ownerAlias = options?.alias || qb.alias;
|
|
12
|
-
const keys =
|
|
12
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
13
13
|
let alias = options?.alias;
|
|
14
14
|
if (nestedAlias) {
|
|
15
15
|
alias = nestedAlias;
|
|
@@ -31,7 +31,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
31
31
|
return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
|
|
32
32
|
});
|
|
33
33
|
for (const key of keys) {
|
|
34
|
-
if (!['$some', '$none', '$every'].includes(key)) {
|
|
34
|
+
if (typeof key !== 'string' || !['$some', '$none', '$every'].includes(key)) {
|
|
35
35
|
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
36
36
|
}
|
|
37
37
|
const payload = this.payload[key].unwrap();
|
|
@@ -78,7 +78,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
78
78
|
const childNode = this.payload[field];
|
|
79
79
|
const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
|
|
80
80
|
const operator = Utils.isOperator(field);
|
|
81
|
-
const isRawField = RawQueryFragment.
|
|
81
|
+
const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
|
|
82
82
|
// we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
|
|
83
83
|
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
|
|
84
84
|
// if key is missing, we are inside group operator and we need to prefix with alias
|
|
@@ -106,12 +106,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
106
106
|
}, {});
|
|
107
107
|
}
|
|
108
108
|
isStrict() {
|
|
109
|
-
return this.strict ||
|
|
109
|
+
return this.strict || Utils.getObjectQueryKeys(this.payload).some(key => {
|
|
110
110
|
return this.payload[key].isStrict();
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
unwrap() {
|
|
114
|
-
return
|
|
114
|
+
return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
|
|
115
115
|
o[field] = this.payload[field].unwrap();
|
|
116
116
|
return o;
|
|
117
117
|
}, {});
|
|
@@ -119,7 +119,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
119
119
|
willAutoJoin(qb, alias, options) {
|
|
120
120
|
const nestedAlias = qb.getAliasForJoinPath(this.getPath(), options);
|
|
121
121
|
const ownerAlias = alias || qb.alias;
|
|
122
|
-
const keys =
|
|
122
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
123
123
|
if (nestedAlias) {
|
|
124
124
|
alias = nestedAlias;
|
|
125
125
|
}
|
|
@@ -132,9 +132,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
shouldInline(payload) {
|
|
135
|
-
const
|
|
136
|
-
const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date ||
|
|
137
|
-
const operator = Utils.isObject(payload) &&
|
|
135
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
136
|
+
const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
|
|
137
|
+
const operator = Utils.isObject(payload) && Utils.getObjectQueryKeys(payload).every(k => Utils.isOperator(k, false));
|
|
138
138
|
return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
|
|
139
139
|
}
|
|
140
140
|
getChildKey(k, prop, childAlias, alias) {
|
|
@@ -145,8 +145,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
145
145
|
}
|
|
146
146
|
inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
|
|
147
147
|
const key = this.getChildKey(k, prop, childAlias);
|
|
148
|
-
const value = payload.map((child) =>
|
|
149
|
-
const key = (this.isPrefixed(childKey) || Utils.isOperator(childKey)) ? childKey : this.aliased(childKey, childAlias);
|
|
148
|
+
const value = payload.map((child) => Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
|
|
149
|
+
const key = (RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)) ? childKey : this.aliased(childKey, childAlias);
|
|
150
150
|
inner[key] = child[childKey];
|
|
151
151
|
return inner;
|
|
152
152
|
}, {}));
|
|
@@ -154,8 +154,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
154
154
|
}
|
|
155
155
|
inlineChildPayload(o, payload, field, alias, childAlias) {
|
|
156
156
|
const prop = this.metadata.find(this.entityName).properties[field];
|
|
157
|
-
for (const k of
|
|
158
|
-
if (
|
|
157
|
+
for (const k of Utils.getObjectQueryKeys(payload)) {
|
|
158
|
+
if (RawQueryFragment.isKnownFragmentSymbol(k)) {
|
|
159
|
+
o[k] = payload[k];
|
|
160
|
+
}
|
|
161
|
+
else if (Utils.isOperator(k, false)) {
|
|
159
162
|
const tmp = payload[k];
|
|
160
163
|
delete payload[k];
|
|
161
164
|
o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
|
|
@@ -167,9 +170,6 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
167
170
|
const key = this.getChildKey(k, prop, childAlias, alias);
|
|
168
171
|
this.inlineCondition(key, o, payload[k]);
|
|
169
172
|
}
|
|
170
|
-
else if (RawQueryFragment.isKnownFragment(k)) {
|
|
171
|
-
o[k] = payload[k];
|
|
172
|
-
}
|
|
173
173
|
else {
|
|
174
174
|
o[this.aliased(k, childAlias)] = payload[k];
|
|
175
175
|
}
|
|
@@ -194,8 +194,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
194
194
|
if (!this.prop || !this.parent) {
|
|
195
195
|
return false;
|
|
196
196
|
}
|
|
197
|
-
const keys =
|
|
198
|
-
if (keys.every(k => k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
197
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
198
|
+
if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
199
199
|
return false;
|
|
200
200
|
}
|
|
201
201
|
if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
|
|
@@ -206,21 +206,21 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
206
206
|
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
207
207
|
const operatorKeys = knownKey && keys.every(key => Utils.isOperator(key, false));
|
|
208
208
|
const primaryKeys = knownKey && keys.every(key => {
|
|
209
|
-
if (!meta.primaryKeys.includes(key)) {
|
|
209
|
+
if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
|
|
210
210
|
return false;
|
|
211
211
|
}
|
|
212
212
|
if (!Utils.isPlainObject(this.payload[key].payload) || ![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
|
|
213
213
|
return true;
|
|
214
214
|
}
|
|
215
|
-
return
|
|
215
|
+
return Utils.getObjectQueryKeys(this.payload[key].payload).every(k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k));
|
|
216
216
|
});
|
|
217
217
|
return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
|
|
218
218
|
}
|
|
219
219
|
autoJoin(qb, alias, options) {
|
|
220
220
|
const nestedAlias = qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
|
|
221
|
-
const
|
|
222
|
-
const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date ||
|
|
223
|
-
const operator = Utils.isPlainObject(this.payload) &&
|
|
221
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
222
|
+
const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
|
|
223
|
+
const operator = Utils.isPlainObject(this.payload) && Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
|
|
224
224
|
const field = `${alias}.${this.prop.name}`;
|
|
225
225
|
const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
|
|
226
226
|
const path = this.getPath();
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -98,8 +98,6 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
98
98
|
__populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
99
99
|
/** @internal */
|
|
100
100
|
_populateMap: Dictionary<string>;
|
|
101
|
-
/** @internal */
|
|
102
|
-
readonly rawFragments: Set<string>;
|
|
103
101
|
protected aliasCounter: number;
|
|
104
102
|
protected flags: Set<QueryFlag>;
|
|
105
103
|
protected finalized: boolean;
|
|
@@ -175,11 +173,11 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
175
173
|
applyJoinedFilters(em: EntityManager, filterOptions: FilterOptions | undefined): Promise<void>;
|
|
176
174
|
withSubQuery(subQuery: RawQueryFragment | NativeQueryBuilder, alias: string): this;
|
|
177
175
|
where(cond: QBFilterQuery<Entity>, operator?: keyof typeof GroupOperator): this;
|
|
178
|
-
where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
|
|
176
|
+
where(cond: string | RawQueryFragment, params?: any[], operator?: keyof typeof GroupOperator): this;
|
|
179
177
|
andWhere(cond: QBFilterQuery<Entity>): this;
|
|
180
|
-
andWhere(cond: string, params?: any[]): this;
|
|
178
|
+
andWhere(cond: string | RawQueryFragment, params?: any[]): this;
|
|
181
179
|
orWhere(cond: QBFilterQuery<Entity>): this;
|
|
182
|
-
orWhere(cond: string, params?: any[]): this;
|
|
180
|
+
orWhere(cond: string | RawQueryFragment, params?: any[]): this;
|
|
183
181
|
orderBy(orderBy: QBQueryOrderMap<Entity> | QBQueryOrderMap<Entity>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
|
|
184
182
|
andOrderBy(orderBy: QBQueryOrderMap<Entity> | QBQueryOrderMap<Entity>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
|
|
185
183
|
private processOrderBy;
|
|
@@ -225,10 +223,6 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
225
223
|
from<Entity extends AnyEntity<Entity> = AnyEntity>(target: QueryBuilder<Entity>, aliasName?: string): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
|
|
226
224
|
from<Entity extends AnyEntity<Entity> = AnyEntity>(target: EntityName<Entity>): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
|
|
227
225
|
getNativeQuery(processVirtualEntity?: boolean): NativeQueryBuilder;
|
|
228
|
-
/**
|
|
229
|
-
* @internal
|
|
230
|
-
*/
|
|
231
|
-
clearRawFragmentsCache(): void;
|
|
232
226
|
/**
|
|
233
227
|
* Returns the query with parameters as wildcards.
|
|
234
228
|
*/
|