@mikro-orm/knex 6.5.10-dev.9 → 6.6.1-dev.0
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 +2 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +3 -3
- package/query/ArrayCriteriaNode.d.ts +1 -0
- package/query/ArrayCriteriaNode.js +3 -0
- package/query/CriteriaNode.d.ts +3 -1
- package/query/CriteriaNode.js +6 -1
- package/query/CriteriaNodeFactory.js +1 -1
- package/query/ObjectCriteriaNode.d.ts +1 -0
- package/query/ObjectCriteriaNode.js +21 -13
- package/query/QueryBuilder.d.ts +3 -3
- package/query/QueryBuilder.js +3 -2
- package/query/QueryBuilderHelper.d.ts +1 -1
- package/query/QueryBuilderHelper.js +8 -1
- package/query/index.d.ts +1 -0
- package/query/index.js +3 -0
- package/query/raw.d.ts +59 -0
- package/query/raw.js +71 -0
- package/schema/DatabaseTable.js +15 -3
- package/typings.d.ts +1 -0
package/AbstractSqlDriver.js
CHANGED
|
@@ -847,10 +847,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
847
847
|
*/
|
|
848
848
|
joinedProps(meta, populate, options) {
|
|
849
849
|
return populate.filter(hint => {
|
|
850
|
-
const [propName] = hint.field.split(':', 2);
|
|
850
|
+
const [propName, ref] = hint.field.split(':', 2);
|
|
851
851
|
const prop = meta.properties[propName] || {};
|
|
852
852
|
const strategy = (0, core_1.getLoadingStrategy)(hint.strategy || prop.strategy || options?.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
853
|
-
if (
|
|
853
|
+
if (ref && [core_1.ReferenceKind.ONE_TO_ONE, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
854
854
|
return true;
|
|
855
855
|
}
|
|
856
856
|
// skip redundant joins for 1:1 owner population hints when using `mapToPk`
|
package/index.d.ts
CHANGED
|
@@ -16,4 +16,5 @@ export { SqlEntityManager as EntityManager } from './SqlEntityManager';
|
|
|
16
16
|
export { SqlEntityRepository as EntityRepository } from './SqlEntityRepository';
|
|
17
17
|
/** @ignore */
|
|
18
18
|
export { Knex, knex } from 'knex';
|
|
19
|
+
/** @ts-ignore */
|
|
19
20
|
export * from '@mikro-orm/core';
|
package/index.js
CHANGED
|
@@ -38,4 +38,5 @@ Object.defineProperty(exports, "EntityRepository", { enumerable: true, get: func
|
|
|
38
38
|
var knex_1 = require("knex");
|
|
39
39
|
Object.defineProperty(exports, "Knex", { enumerable: true, get: function () { return knex_1.Knex; } });
|
|
40
40
|
Object.defineProperty(exports, "knex", { enumerable: true, get: function () { return knex_1.knex; } });
|
|
41
|
+
/** @ts-ignore */
|
|
41
42
|
__exportStar(require("@mikro-orm/core"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/knex",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.1-dev.0",
|
|
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
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"sqlstring": "2.3.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@mikro-orm/core": "^6.
|
|
66
|
+
"@mikro-orm/core": "^6.6.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@mikro-orm/core": "6.
|
|
69
|
+
"@mikro-orm/core": "6.6.1-dev.0",
|
|
70
70
|
"better-sqlite3": "*",
|
|
71
71
|
"libsql": "*",
|
|
72
72
|
"mariadb": "*"
|
|
@@ -7,4 +7,5 @@ export declare class ArrayCriteriaNode<T extends object> extends CriteriaNode<T>
|
|
|
7
7
|
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
8
|
unwrap(): any;
|
|
9
9
|
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): any;
|
|
10
|
+
isStrict(): boolean;
|
|
10
11
|
}
|
|
@@ -21,5 +21,8 @@ class ArrayCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
21
21
|
return node.willAutoJoin(qb, alias, options);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
isStrict() {
|
|
25
|
+
return this.strict || this.payload.some((node) => node.isStrict());
|
|
26
|
+
}
|
|
24
27
|
}
|
|
25
28
|
exports.ArrayCriteriaNode = ArrayCriteriaNode;
|
package/query/CriteriaNode.d.ts
CHANGED
|
@@ -11,10 +11,11 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
|
|
|
11
11
|
readonly entityName: string;
|
|
12
12
|
readonly parent?: ICriteriaNode<T> | undefined;
|
|
13
13
|
readonly key?: EntityKey<T> | undefined;
|
|
14
|
+
readonly strict: boolean;
|
|
14
15
|
payload: any;
|
|
15
16
|
prop?: EntityProperty<T>;
|
|
16
17
|
index?: number;
|
|
17
|
-
constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean);
|
|
18
|
+
constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean, strict?: boolean);
|
|
18
19
|
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
19
20
|
unwrap(): any;
|
|
20
21
|
shouldInline(payload: any): boolean;
|
|
@@ -25,6 +26,7 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
|
|
|
25
26
|
private isPivotJoin;
|
|
26
27
|
getPivotPath(path: string): string;
|
|
27
28
|
aliased(field: string, alias?: string): string;
|
|
29
|
+
isStrict(): boolean;
|
|
28
30
|
/** @ignore */
|
|
29
31
|
[inspect.custom](): string;
|
|
30
32
|
}
|
package/query/CriteriaNode.js
CHANGED
|
@@ -13,14 +13,16 @@ class CriteriaNode {
|
|
|
13
13
|
entityName;
|
|
14
14
|
parent;
|
|
15
15
|
key;
|
|
16
|
+
strict;
|
|
16
17
|
payload;
|
|
17
18
|
prop;
|
|
18
19
|
index;
|
|
19
|
-
constructor(metadata, entityName, parent, key, validate = true) {
|
|
20
|
+
constructor(metadata, entityName, parent, key, validate = true, strict = false) {
|
|
20
21
|
this.metadata = metadata;
|
|
21
22
|
this.entityName = entityName;
|
|
22
23
|
this.parent = parent;
|
|
23
24
|
this.key = key;
|
|
25
|
+
this.strict = strict;
|
|
24
26
|
const meta = parent && metadata.find(parent.entityName);
|
|
25
27
|
if (meta && key) {
|
|
26
28
|
const pks = core_1.Utils.splitPrimaryKeys(key);
|
|
@@ -109,6 +111,9 @@ class CriteriaNode {
|
|
|
109
111
|
aliased(field, alias) {
|
|
110
112
|
return alias ? `${alias}.${field}` : field;
|
|
111
113
|
}
|
|
114
|
+
isStrict() {
|
|
115
|
+
return this.strict;
|
|
116
|
+
}
|
|
112
117
|
/** @ignore */
|
|
113
118
|
[node_util_1.inspect.custom]() {
|
|
114
119
|
const o = {};
|
|
@@ -39,7 +39,7 @@ class CriteriaNodeFactory {
|
|
|
39
39
|
}
|
|
40
40
|
static createObjectNode(metadata, entityName, payload, parent, key) {
|
|
41
41
|
const meta = metadata.find(entityName);
|
|
42
|
-
const node = new ObjectCriteriaNode_1.ObjectCriteriaNode(metadata, entityName, parent, key);
|
|
42
|
+
const node = new ObjectCriteriaNode_1.ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
|
|
43
43
|
node.payload = Object.keys(payload).reduce((o, item) => {
|
|
44
44
|
o[item] = this.createObjectItemNode(metadata, entityName, node, payload, item, meta);
|
|
45
45
|
return o;
|
|
@@ -5,6 +5,7 @@ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
7
|
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
|
+
isStrict(): boolean;
|
|
8
9
|
unwrap(): any;
|
|
9
10
|
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
|
|
10
11
|
shouldInline(payload: any): boolean;
|
|
@@ -61,6 +61,22 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
61
61
|
}
|
|
62
62
|
alias = this.autoJoin(qb, ownerAlias, options);
|
|
63
63
|
}
|
|
64
|
+
if (this.prop && nestedAlias) {
|
|
65
|
+
const toOneProperty = [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
66
|
+
// if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
|
|
67
|
+
// with an exclusive condition on the join columns:
|
|
68
|
+
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
69
|
+
// - if the target column is not null, the row is matched, we apply the filter
|
|
70
|
+
if (toOneProperty && this.prop.nullable && this.isStrict()) {
|
|
71
|
+
const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
|
|
72
|
+
qb.andWhere({
|
|
73
|
+
$or: [
|
|
74
|
+
{ [ownerAlias + '.' + key]: null },
|
|
75
|
+
{ [nestedAlias + '.' + core_1.Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
64
80
|
return keys.reduce((o, field) => {
|
|
65
81
|
const childNode = this.payload[field];
|
|
66
82
|
const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
|
|
@@ -92,6 +108,11 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
92
108
|
return o;
|
|
93
109
|
}, {});
|
|
94
110
|
}
|
|
111
|
+
isStrict() {
|
|
112
|
+
return this.strict || Object.keys(this.payload).some(key => {
|
|
113
|
+
return this.payload[key].isStrict();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
95
116
|
unwrap() {
|
|
96
117
|
return Object.keys(this.payload).reduce((o, field) => {
|
|
97
118
|
o[field] = this.payload[field].unwrap();
|
|
@@ -216,19 +237,6 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
216
237
|
? enums_1.JoinType.innerJoin
|
|
217
238
|
: enums_1.JoinType.leftJoin;
|
|
218
239
|
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
219
|
-
// if the property is nullable, we need to use left join, so we mimic the inner join behaviour
|
|
220
|
-
// with an exclusive condition on the join columns:
|
|
221
|
-
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
222
|
-
// - if the target column is not null, the row is matched, we apply the filter
|
|
223
|
-
if (toOneProperty && this.prop.nullable && options?.filter) {
|
|
224
|
-
const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
|
|
225
|
-
qb.andWhere({
|
|
226
|
-
$or: [
|
|
227
|
-
{ [alias + '.' + key]: null },
|
|
228
|
-
{ [nestedAlias + '.' + core_1.Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
229
|
-
],
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
240
|
if (!qb.hasFlag(core_1.QueryFlag.INFER_POPULATE)) {
|
|
233
241
|
qb._fields = prev;
|
|
234
242
|
}
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inspect } from 'node:util';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityManager, type EntityMetadata, type EntityName, type EntityProperty, type ExpandProperty, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData } from '@mikro-orm/core';
|
|
3
|
+
import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityManager, type EntityMetadata, type EntityName, type EntityProperty, type ExpandProperty, type FilterOptions, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData } from '@mikro-orm/core';
|
|
4
4
|
import { JoinType, QueryType } from './enums';
|
|
5
5
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
6
6
|
import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
|
|
@@ -147,7 +147,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
147
147
|
/**
|
|
148
148
|
* Apply filters to the QB where condition.
|
|
149
149
|
*/
|
|
150
|
-
applyFilters(filterOptions?:
|
|
150
|
+
applyFilters(filterOptions?: FilterOptions): Promise<void>;
|
|
151
151
|
private readonly autoJoinedPaths;
|
|
152
152
|
/**
|
|
153
153
|
* @internal
|
|
@@ -156,7 +156,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
156
156
|
/**
|
|
157
157
|
* @internal
|
|
158
158
|
*/
|
|
159
|
-
applyJoinedFilters(em: EntityManager, filterOptions
|
|
159
|
+
applyJoinedFilters(em: EntityManager, filterOptions: FilterOptions | undefined): Promise<void>;
|
|
160
160
|
withSubQuery(subQuery: Knex.QueryBuilder, alias: string): this;
|
|
161
161
|
where(cond: QBFilterQuery<Entity>, operator?: keyof typeof GroupOperator): this;
|
|
162
162
|
where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
|
package/query/QueryBuilder.js
CHANGED
|
@@ -280,12 +280,13 @@ class QueryBuilder {
|
|
|
280
280
|
/**
|
|
281
281
|
* @internal
|
|
282
282
|
*/
|
|
283
|
-
async applyJoinedFilters(em, filterOptions
|
|
283
|
+
async applyJoinedFilters(em, filterOptions) {
|
|
284
284
|
for (const path of this.autoJoinedPaths) {
|
|
285
285
|
const join = this.getJoinForPath(path);
|
|
286
286
|
if (join.type === enums_1.JoinType.pivotJoin) {
|
|
287
287
|
continue;
|
|
288
288
|
}
|
|
289
|
+
filterOptions = core_1.QueryHelper.mergePropertyFilters(join.prop.filters, filterOptions);
|
|
289
290
|
const cond = await em.applyFilters(join.prop.type, join.cond, filterOptions, 'read');
|
|
290
291
|
if (core_1.Utils.hasObjectKeys(cond)) {
|
|
291
292
|
// remove nested filters, we only care about scalars here, nesting would require another join branch
|
|
@@ -572,7 +573,7 @@ class QueryBuilder {
|
|
|
572
573
|
return this._query.qb = this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
|
|
573
574
|
}
|
|
574
575
|
if (this.lockMode) {
|
|
575
|
-
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
576
|
+
this.helper.getLockSQL(qb, this.lockMode, this.lockTables, this._joins);
|
|
576
577
|
}
|
|
577
578
|
this.helper.finalize(type, qb, this.mainAlias.metadata, this._data, this._returning);
|
|
578
579
|
this.clearRawFragmentsCache();
|
|
@@ -51,7 +51,7 @@ export declare class QueryBuilderHelper {
|
|
|
51
51
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string[];
|
|
52
52
|
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
|
|
53
53
|
splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>, string | undefined];
|
|
54
|
-
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
|
|
54
|
+
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[], joinsMap?: Dictionary<JoinOptions>): void;
|
|
55
55
|
updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
|
|
56
56
|
private prefix;
|
|
57
57
|
private appendGroupCondition;
|
|
@@ -629,11 +629,18 @@ class QueryBuilderHelper {
|
|
|
629
629
|
const fromField = parts.join('.');
|
|
630
630
|
return [fromAlias, fromField, ref];
|
|
631
631
|
}
|
|
632
|
-
getLockSQL(qb, lockMode, lockTables = []) {
|
|
632
|
+
getLockSQL(qb, lockMode, lockTables = [], joinsMap) {
|
|
633
633
|
const meta = this.metadata.find(this.entityName);
|
|
634
634
|
if (lockMode === core_1.LockMode.OPTIMISTIC && meta && !meta.versionProperty) {
|
|
635
635
|
throw core_1.OptimisticLockError.lockFailed(this.entityName);
|
|
636
636
|
}
|
|
637
|
+
if (lockMode !== core_1.LockMode.OPTIMISTIC && lockTables.length === 0 && joinsMap) {
|
|
638
|
+
const joins = Object.values(joinsMap);
|
|
639
|
+
const innerJoins = joins.filter(join => [enums_1.JoinType.innerJoin, enums_1.JoinType.innerJoinLateral, enums_1.JoinType.nestedInnerJoin].includes(join.type));
|
|
640
|
+
if (joins.length > innerJoins.length) {
|
|
641
|
+
lockTables.push(this.alias, ...innerJoins.map(join => join.alias));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
637
644
|
switch (lockMode) {
|
|
638
645
|
case core_1.LockMode.PESSIMISTIC_READ: return void qb.forShare(...lockTables);
|
|
639
646
|
case core_1.LockMode.PESSIMISTIC_WRITE: return void qb.forUpdate(...lockTables);
|
package/query/index.d.ts
CHANGED
package/query/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.raw = void 0;
|
|
17
18
|
__exportStar(require("./enums"), exports);
|
|
18
19
|
__exportStar(require("./QueryBuilderHelper"), exports);
|
|
19
20
|
__exportStar(require("./QueryBuilder"), exports);
|
|
@@ -22,3 +23,5 @@ __exportStar(require("./ArrayCriteriaNode"), exports);
|
|
|
22
23
|
__exportStar(require("./ObjectCriteriaNode"), exports);
|
|
23
24
|
__exportStar(require("./ScalarCriteriaNode"), exports);
|
|
24
25
|
__exportStar(require("./CriteriaNodeFactory"), exports);
|
|
26
|
+
var raw_1 = require("./raw");
|
|
27
|
+
Object.defineProperty(exports, "raw", { enumerable: true, get: function () { return raw_1.rawKnex; } });
|
package/query/raw.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type AnyString, type Dictionary, type EntityKey, type RawQueryFragment } from '@mikro-orm/core';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
import { QueryBuilder } from './QueryBuilder';
|
|
4
|
+
/**
|
|
5
|
+
* Creates raw SQL query fragment that can be assigned to a property or part of a filter. This fragment is represented
|
|
6
|
+
* by `RawQueryFragment` class instance that can be serialized to a string, so it can be used both as an object value
|
|
7
|
+
* and key. When serialized, the fragment key gets cached and only such cached key will be recognized by the ORM.
|
|
8
|
+
* This adds a runtime safety to the raw query fragments.
|
|
9
|
+
*
|
|
10
|
+
* > **`raw()` helper is required since v6 to use a raw fragment in your query, both through EntityManager and QueryBuilder.**
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* // as a value
|
|
14
|
+
* await em.find(User, { time: raw('now()') });
|
|
15
|
+
*
|
|
16
|
+
* // as a key
|
|
17
|
+
* await em.find(User, { [raw('lower(name)')]: name.toLowerCase() });
|
|
18
|
+
*
|
|
19
|
+
* // value can be empty array
|
|
20
|
+
* await em.find(User, { [raw('(select 1 = 1)')]: [] });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The `raw` helper supports several signatures, you can pass in a callback that receives the current property alias:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* await em.find(User, { [raw(alias => `lower(${alias}.name)`)]: name.toLowerCase() });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* You can also use the `sql` tagged template function, which works the same, but supports only the simple string signature:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* await em.find(User, { [sql`lower(name)`]: name.toLowerCase() });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* When using inside filters, you might have to use a callback signature to create new raw instance for every filter usage.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
45
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
46
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
47
|
+
* @Entity({ schema: 'library' })
|
|
48
|
+
* export class Author { ... }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
55
|
+
* @Entity({ schema: 'library' })
|
|
56
|
+
* export class Author { ... }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function rawKnex<T extends object = any, R = any>(sql: Knex.QueryBuilder | Knex.Raw | QueryBuilder<T> | EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): NoInfer<R>;
|
package/query/raw.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rawKnex = rawKnex;
|
|
4
|
+
const core_1 = require("@mikro-orm/core");
|
|
5
|
+
const QueryBuilder_1 = require("./QueryBuilder");
|
|
6
|
+
/**
|
|
7
|
+
* Creates raw SQL query fragment that can be assigned to a property or part of a filter. This fragment is represented
|
|
8
|
+
* by `RawQueryFragment` class instance that can be serialized to a string, so it can be used both as an object value
|
|
9
|
+
* and key. When serialized, the fragment key gets cached and only such cached key will be recognized by the ORM.
|
|
10
|
+
* This adds a runtime safety to the raw query fragments.
|
|
11
|
+
*
|
|
12
|
+
* > **`raw()` helper is required since v6 to use a raw fragment in your query, both through EntityManager and QueryBuilder.**
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* // as a value
|
|
16
|
+
* await em.find(User, { time: raw('now()') });
|
|
17
|
+
*
|
|
18
|
+
* // as a key
|
|
19
|
+
* await em.find(User, { [raw('lower(name)')]: name.toLowerCase() });
|
|
20
|
+
*
|
|
21
|
+
* // value can be empty array
|
|
22
|
+
* await em.find(User, { [raw('(select 1 = 1)')]: [] });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The `raw` helper supports several signatures, you can pass in a callback that receives the current property alias:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* await em.find(User, { [raw(alias => `lower(${alias}.name)`)]: name.toLowerCase() });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* You can also use the `sql` tagged template function, which works the same, but supports only the simple string signature:
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* await em.find(User, { [sql`lower(name)`]: name.toLowerCase() });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* When using inside filters, you might have to use a callback signature to create new raw instance for every filter usage.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
47
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
48
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
49
|
+
* @Entity({ schema: 'library' })
|
|
50
|
+
* export class Author { ... }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
57
|
+
* @Entity({ schema: 'library' })
|
|
58
|
+
* export class Author { ... }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
function rawKnex(sql, params) {
|
|
62
|
+
if (core_1.Utils.isObject(sql) && 'toSQL' in sql) {
|
|
63
|
+
const query = sql.toSQL();
|
|
64
|
+
return (0, core_1.raw)(query.sql, query.bindings);
|
|
65
|
+
}
|
|
66
|
+
if (sql instanceof QueryBuilder_1.QueryBuilder) {
|
|
67
|
+
const query = sql.toQuery()._sql;
|
|
68
|
+
return (0, core_1.raw)(query.sql, query.bindings);
|
|
69
|
+
}
|
|
70
|
+
return (0, core_1.raw)(sql, params);
|
|
71
|
+
}
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -614,7 +614,7 @@ class DatabaseTable {
|
|
|
614
614
|
fkOptions.deferMode = fk.deferMode;
|
|
615
615
|
fkOptions.columnTypes = fk.columnNames.map(col => this.getColumn(col).type);
|
|
616
616
|
}
|
|
617
|
-
|
|
617
|
+
const ret = {
|
|
618
618
|
name: prop,
|
|
619
619
|
type,
|
|
620
620
|
runtimeType,
|
|
@@ -642,6 +642,11 @@ class DatabaseTable {
|
|
|
642
642
|
persist,
|
|
643
643
|
...fkOptions,
|
|
644
644
|
};
|
|
645
|
+
const nativeEnumName = Object.keys(this.nativeEnums).find(name => name === column.type);
|
|
646
|
+
if (nativeEnumName) {
|
|
647
|
+
ret.nativeEnumName = nativeEnumName;
|
|
648
|
+
}
|
|
649
|
+
return ret;
|
|
645
650
|
}
|
|
646
651
|
getReferenceKind(fk, unique) {
|
|
647
652
|
if (fk && unique) {
|
|
@@ -675,10 +680,17 @@ class DatabaseTable {
|
|
|
675
680
|
if (fk) {
|
|
676
681
|
return this.getPropertyTypeForForeignKey(namingStrategy, fk);
|
|
677
682
|
}
|
|
683
|
+
const enumMode = this.platform.getConfig().get('entityGenerator').enumMode;
|
|
678
684
|
// If this column is using an enum.
|
|
679
685
|
if (column.enumItems?.length) {
|
|
680
|
-
|
|
681
|
-
|
|
686
|
+
const name = column.nativeEnumName ?? column.name;
|
|
687
|
+
const tableName = column.nativeEnumName ? undefined : this.name;
|
|
688
|
+
if (enumMode === 'ts-enum') {
|
|
689
|
+
// We will create a new enum name for this type and set it as the property type as well.
|
|
690
|
+
return namingStrategy.getEnumClassName(name, tableName, this.schema);
|
|
691
|
+
}
|
|
692
|
+
// With other enum strategies, we need to use the type name.
|
|
693
|
+
return namingStrategy.getEnumTypeName(name, tableName, this.schema);
|
|
682
694
|
}
|
|
683
695
|
return column.mappedType?.runtimeType ?? 'unknown';
|
|
684
696
|
}
|
package/typings.d.ts
CHANGED
|
@@ -178,6 +178,7 @@ export interface ICriteriaNode<T extends object> {
|
|
|
178
178
|
readonly entityName: string;
|
|
179
179
|
readonly parent?: ICriteriaNode<T> | undefined;
|
|
180
180
|
readonly key?: string | undefined;
|
|
181
|
+
readonly strict?: boolean;
|
|
181
182
|
payload: any;
|
|
182
183
|
prop?: EntityProperty;
|
|
183
184
|
index?: number;
|