@mikro-orm/core 7.0.0-dev.113 → 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/EntityManager.js +7 -9
- package/drivers/DatabaseDriver.js +1 -1
- package/drivers/IDatabaseDriver.d.ts +2 -2
- package/entity/Collection.js +2 -1
- package/entity/EntityLoader.js +5 -19
- package/entity/validators.js +2 -2
- package/hydration/ObjectHydrator.js +2 -2
- package/metadata/MetadataDiscovery.js +2 -2
- package/package.json +1 -1
- package/platforms/Platform.js +2 -2
- package/typings.d.ts +3 -3
- package/utils/Cursor.js +3 -3
- package/utils/EntityComparator.js +8 -3
- package/utils/QueryHelper.d.ts +1 -1
- package/utils/QueryHelper.js +11 -10
- package/utils/RawQueryFragment.d.ts +11 -12
- package/utils/RawQueryFragment.js +28 -55
- package/utils/Utils.d.ts +2 -0
- package/utils/Utils.js +10 -1
- package/utils/clone.js +7 -21
- package/utils/upsert-utils.d.ts +4 -4
package/EntityManager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Cursor } from './utils/Cursor.js';
|
|
|
4
4
|
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
5
5
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
6
6
|
import { TransactionContext } from './utils/TransactionContext.js';
|
|
7
|
-
import { isRaw,
|
|
7
|
+
import { isRaw, Raw } from './utils/RawQueryFragment.js';
|
|
8
8
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
9
9
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
10
10
|
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
@@ -457,7 +457,7 @@ export class EntityManager {
|
|
|
457
457
|
}
|
|
458
458
|
ret.push(cond);
|
|
459
459
|
}
|
|
460
|
-
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
460
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
461
461
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
462
462
|
}
|
|
463
463
|
/**
|
|
@@ -468,12 +468,10 @@ export class EntityManager {
|
|
|
468
468
|
const em = this.getContext(false);
|
|
469
469
|
await em.tryFlush(entityName, options);
|
|
470
470
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
471
|
-
return
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
]);
|
|
476
|
-
});
|
|
471
|
+
return Promise.all([
|
|
472
|
+
em.find(entityName, where, options),
|
|
473
|
+
em.count(entityName, where, options),
|
|
474
|
+
]);
|
|
477
475
|
}
|
|
478
476
|
/**
|
|
479
477
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
@@ -536,7 +534,7 @@ export class EntityManager {
|
|
|
536
534
|
entityName = Utils.className(entityName);
|
|
537
535
|
options.overfetch ??= true;
|
|
538
536
|
options.where ??= {};
|
|
539
|
-
if (Utils.isEmpty(options.orderBy)) {
|
|
537
|
+
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
540
538
|
throw new Error('Explicit `orderBy` option required');
|
|
541
539
|
}
|
|
542
540
|
const [entities, count] = options.includeCount !== false
|
|
@@ -152,7 +152,7 @@ export class DatabaseDriver {
|
|
|
152
152
|
}
|
|
153
153
|
const createOrderBy = (prop, direction) => {
|
|
154
154
|
if (Utils.isPlainObject(direction)) {
|
|
155
|
-
const value = Utils.
|
|
155
|
+
const value = Utils.getObjectQueryKeys(direction).reduce((o, key) => {
|
|
156
156
|
Object.assign(o, createOrderBy(key, direction[key]));
|
|
157
157
|
return o;
|
|
158
158
|
}, {});
|
|
@@ -8,7 +8,7 @@ import type { EntityManager } from '../EntityManager.js';
|
|
|
8
8
|
import type { DriverException } from '../exceptions.js';
|
|
9
9
|
import type { Configuration } from '../utils/Configuration.js';
|
|
10
10
|
import type { LoggingOptions, LogContext } from '../logging/Logger.js';
|
|
11
|
-
import type {
|
|
11
|
+
import type { Raw } from '../utils/RawQueryFragment.js';
|
|
12
12
|
export declare const EntityManagerType: unique symbol;
|
|
13
13
|
export interface IDatabaseDriver<C extends Connection = Connection> {
|
|
14
14
|
[EntityManagerType]: EntityManager<this>;
|
|
@@ -189,7 +189,7 @@ export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOpti
|
|
|
189
189
|
processCollections?: boolean;
|
|
190
190
|
}
|
|
191
191
|
export interface UpsertOptions<Entity, Fields extends string = never> extends Omit<NativeInsertUpdateOptions<Entity>, 'upsert'> {
|
|
192
|
-
onConflictFields?: (keyof Entity)[] |
|
|
192
|
+
onConflictFields?: (keyof Entity)[] | Raw;
|
|
193
193
|
onConflictAction?: 'ignore' | 'merge';
|
|
194
194
|
onConflictMergeFields?: AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
|
|
195
195
|
onConflictExcludeFields?: AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
|
package/entity/Collection.js
CHANGED
|
@@ -4,6 +4,7 @@ import { DataloaderType, ReferenceKind } from '../enums.js';
|
|
|
4
4
|
import { Reference } from './Reference.js';
|
|
5
5
|
import { helper, wrap } from './wrap.js';
|
|
6
6
|
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
7
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
7
8
|
import { inspect } from '../logging/inspect.js';
|
|
8
9
|
export class Collection {
|
|
9
10
|
owner;
|
|
@@ -298,7 +299,7 @@ export class Collection {
|
|
|
298
299
|
return cond;
|
|
299
300
|
}
|
|
300
301
|
createOrderBy(orderBy = []) {
|
|
301
|
-
if (Utils.isEmpty(orderBy) && this.property.orderBy) {
|
|
302
|
+
if (Utils.isEmpty(orderBy) && !Raw.hasObjectFragments(orderBy) && this.property.orderBy) {
|
|
302
303
|
orderBy = this.property.orderBy;
|
|
303
304
|
}
|
|
304
305
|
return Utils.asArray(orderBy);
|
package/entity/EntityLoader.js
CHANGED
|
@@ -4,8 +4,8 @@ import { ValidationError } from '../errors.js';
|
|
|
4
4
|
import { LoadStrategy, PopulatePath, ReferenceKind, } from '../enums.js';
|
|
5
5
|
import { Reference } from './Reference.js';
|
|
6
6
|
import { helper } from './wrap.js';
|
|
7
|
-
import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
8
7
|
import { expandDotPaths } from './utils.js';
|
|
8
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
9
9
|
export class EntityLoader {
|
|
10
10
|
em;
|
|
11
11
|
metadata;
|
|
@@ -237,26 +237,12 @@ export class EntityLoader {
|
|
|
237
237
|
if (typeof populateWhere === 'object') {
|
|
238
238
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
239
239
|
}
|
|
240
|
-
if (!Utils.isEmpty(prop.where)) {
|
|
240
|
+
if (!Utils.isEmpty(prop.where) || Raw.hasObjectFragments(prop.where)) {
|
|
241
241
|
where = { $and: [where, prop.where] };
|
|
242
242
|
}
|
|
243
|
-
const
|
|
244
|
-
if (prop.orderBy) {
|
|
245
|
-
for (const item of Utils.asArray(prop.orderBy)) {
|
|
246
|
-
for (const field of Utils.keys(item)) {
|
|
247
|
-
const rawField = RawQueryFragment.getKnownFragment(field, false);
|
|
248
|
-
if (rawField) {
|
|
249
|
-
const raw2 = raw(rawField.sql, rawField.params);
|
|
250
|
-
propOrderBy.push({ [raw2.toString()]: item[field] });
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
propOrderBy.push({ [field]: item[field] });
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const orderBy = [...Utils.asArray(options.orderBy), ...propOrderBy].filter((order, idx, array) => {
|
|
243
|
+
const orderBy = [...Utils.asArray(options.orderBy), ...Utils.asArray(prop.orderBy)].filter((order, idx, array) => {
|
|
258
244
|
// skip consecutive ordering with the same key to get around mongo issues
|
|
259
|
-
return idx === 0 || !Utils.equals(
|
|
245
|
+
return idx === 0 || !Utils.equals(Utils.getObjectQueryKeys(array[idx - 1]), Utils.getObjectQueryKeys(order));
|
|
260
246
|
});
|
|
261
247
|
const items = await this.em.find(prop.type, where, {
|
|
262
248
|
filters, convertCustomTypes, lockMode, populateWhere, logging,
|
|
@@ -424,7 +410,7 @@ export class EntityLoader {
|
|
|
424
410
|
if (where[op]) {
|
|
425
411
|
const child = where[op]
|
|
426
412
|
.map((cond) => cond[prop.name])
|
|
427
|
-
.filter((sub) => sub != null && !(Utils.isPlainObject(sub) &&
|
|
413
|
+
.filter((sub) => sub != null && !(Utils.isPlainObject(sub) && Utils.getObjectQueryKeys(sub).every(key => Utils.isOperator(key, false))))
|
|
428
414
|
.map((cond) => {
|
|
429
415
|
if (Utils.isPrimaryKey(cond)) {
|
|
430
416
|
return { [pk]: cond };
|
package/entity/validators.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
2
|
import { ValidationError } from '../errors.js';
|
|
3
|
-
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
3
|
+
import { isRaw, Raw } from '../utils/RawQueryFragment.js';
|
|
4
4
|
import { SCALAR_TYPES } from '../enums.js';
|
|
5
5
|
/** @internal */
|
|
6
6
|
export function validateProperty(prop, givenValue, entity) {
|
|
@@ -59,7 +59,7 @@ export function validatePrimaryKey(entity, meta) {
|
|
|
59
59
|
}
|
|
60
60
|
/** @internal */
|
|
61
61
|
export function validateEmptyWhere(where) {
|
|
62
|
-
if (Utils.isEmpty(where)) {
|
|
62
|
+
if (Utils.isEmpty(where) && !Raw.hasObjectFragments(where)) {
|
|
63
63
|
throw new Error(`You cannot call 'EntityManager.findOne()' with empty 'where' parameter`);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -3,7 +3,7 @@ import { Collection } from '../entity/Collection.js';
|
|
|
3
3
|
import { Reference, ScalarReference } from '../entity/Reference.js';
|
|
4
4
|
import { parseJsonSafe, Utils } from '../utils/Utils.js';
|
|
5
5
|
import { ReferenceKind } from '../enums.js';
|
|
6
|
-
import {
|
|
6
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
7
7
|
export class ObjectHydrator extends Hydrator {
|
|
8
8
|
hydrators = {
|
|
9
9
|
'full~true': new Map(),
|
|
@@ -52,7 +52,7 @@ export class ObjectHydrator extends Hydrator {
|
|
|
52
52
|
const registerCustomType = (prop, convertorKey, method, context) => {
|
|
53
53
|
context.set(`${method}_${convertorKey}`, (val) => {
|
|
54
54
|
/* v8 ignore next */
|
|
55
|
-
if (
|
|
55
|
+
if (Raw.isKnownFragment(val)) {
|
|
56
56
|
return val;
|
|
57
57
|
}
|
|
58
58
|
return prop.customType[method](val, this.platform, { mode: 'serialization' });
|
|
@@ -7,7 +7,7 @@ import { Cascade, ReferenceKind } from '../enums.js';
|
|
|
7
7
|
import { MetadataError } from '../errors.js';
|
|
8
8
|
import { t, Type } from '../types/index.js';
|
|
9
9
|
import { colors } from '../logging/colors.js';
|
|
10
|
-
import { raw,
|
|
10
|
+
import { raw, Raw } from '../utils/RawQueryFragment.js';
|
|
11
11
|
export class MetadataDiscovery {
|
|
12
12
|
metadata;
|
|
13
13
|
platform;
|
|
@@ -1025,7 +1025,7 @@ export class MetadataDiscovery {
|
|
|
1025
1025
|
return;
|
|
1026
1026
|
}
|
|
1027
1027
|
let val = prop.default;
|
|
1028
|
-
const raw =
|
|
1028
|
+
const raw = Raw.getKnownFragment(val);
|
|
1029
1029
|
if (raw) {
|
|
1030
1030
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1031
1031
|
return;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.0.0-dev.
|
|
4
|
+
"version": "7.0.0-dev.114",
|
|
5
5
|
"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.",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./package.json": "./package.json",
|
package/platforms/Platform.js
CHANGED
|
@@ -5,7 +5,7 @@ import { ExceptionConverter } from './ExceptionConverter.js';
|
|
|
5
5
|
import { ArrayType, BigIntType, BlobType, BooleanType, CharacterType, DateTimeType, DateType, DecimalType, DoubleType, EnumType, FloatType, IntegerType, IntervalType, JsonType, MediumIntType, SmallIntType, StringType, TextType, TimeType, TinyIntType, Type, Uint8ArrayType, UnknownType, UuidType, } from '../types/index.js';
|
|
6
6
|
import { parseJsonSafe } from '../utils/Utils.js';
|
|
7
7
|
import { ReferenceKind } from '../enums.js';
|
|
8
|
-
import {
|
|
8
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
9
9
|
export const JsonProperty = Symbol('JsonProperty');
|
|
10
10
|
export class Platform {
|
|
11
11
|
exceptionConverter = new ExceptionConverter();
|
|
@@ -330,7 +330,7 @@ export class Platform {
|
|
|
330
330
|
return value;
|
|
331
331
|
}
|
|
332
332
|
quoteIdentifier(id, quote = '`') {
|
|
333
|
-
const raw =
|
|
333
|
+
const raw = Raw.getKnownFragment(id);
|
|
334
334
|
if (raw) {
|
|
335
335
|
return this.formatQuery(raw.sql, raw.params);
|
|
336
336
|
}
|
package/typings.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { EntitySchema } from './metadata/EntitySchema.js';
|
|
|
14
14
|
import type { Type, types } from './types/index.js';
|
|
15
15
|
import type { Platform } from './platforms/Platform.js';
|
|
16
16
|
import type { Configuration } from './utils/Configuration.js';
|
|
17
|
-
import type {
|
|
17
|
+
import type { Raw } from './utils/RawQueryFragment.js';
|
|
18
18
|
import type { EntityManager } from './EntityManager.js';
|
|
19
19
|
import type { EventSubscriber } from './events/EventSubscriber.js';
|
|
20
20
|
import type { FilterOptions, FindOneOptions, FindOptions, LoadHint } from './drivers/IDatabaseDriver.js';
|
|
@@ -314,7 +314,7 @@ type TableName = {
|
|
|
314
314
|
schema?: string;
|
|
315
315
|
toString: () => string;
|
|
316
316
|
};
|
|
317
|
-
export type IndexCallback<T> = (table: TableName, columns: Record<PropertyName<T>, string>, indexName: string) => string |
|
|
317
|
+
export type IndexCallback<T> = (table: TableName, columns: Record<PropertyName<T>, string>, indexName: string) => string | Raw;
|
|
318
318
|
export type CheckCallback<T> = (columns: Record<PropertyName<T>, string>) => string;
|
|
319
319
|
export type GeneratedColumnCallback<T> = (columns: Record<keyof T, string>) => string;
|
|
320
320
|
export interface CheckConstraint<T = any> {
|
|
@@ -443,7 +443,7 @@ export interface EntityMetadata<T = any> {
|
|
|
443
443
|
schema?: string;
|
|
444
444
|
pivotTable?: boolean;
|
|
445
445
|
virtual?: boolean;
|
|
446
|
-
expression?: string | ((em: any, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>, stream?: boolean) => MaybePromise<
|
|
446
|
+
expression?: string | ((em: any, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>, stream?: boolean) => MaybePromise<Raw | object | string>);
|
|
447
447
|
discriminatorColumn?: EntityKey<T> | AnyString;
|
|
448
448
|
discriminatorValue?: number | string;
|
|
449
449
|
discriminatorMap?: Dictionary<string>;
|
package/utils/Cursor.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Utils } from './Utils.js';
|
|
|
2
2
|
import { ReferenceKind } from '../enums.js';
|
|
3
3
|
import { Reference } from '../entity/Reference.js';
|
|
4
4
|
import { helper } from '../entity/wrap.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
6
6
|
import { CursorError } from '../errors.js';
|
|
7
7
|
import { inspect } from '../logging/inspect.js';
|
|
8
8
|
/**
|
|
@@ -147,8 +147,8 @@ export class Cursor {
|
|
|
147
147
|
static getDefinition(meta, orderBy) {
|
|
148
148
|
return Utils.asArray(orderBy).flatMap(order => {
|
|
149
149
|
const ret = [];
|
|
150
|
-
for (const key of Utils.
|
|
151
|
-
if (
|
|
150
|
+
for (const key of Utils.getObjectQueryKeys(order)) {
|
|
151
|
+
if (Raw.isKnownFragmentSymbol(key)) {
|
|
152
152
|
ret.push([key, order[key]]);
|
|
153
153
|
continue;
|
|
154
154
|
}
|
|
@@ -2,7 +2,7 @@ import { clone } from './clone.js';
|
|
|
2
2
|
import { ReferenceKind } from '../enums.js';
|
|
3
3
|
import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals, parseJsonSafe, Utils } from './Utils.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Raw } from './RawQueryFragment.js';
|
|
6
6
|
export class EntityComparator {
|
|
7
7
|
metadata;
|
|
8
8
|
platform;
|
|
@@ -482,7 +482,7 @@ export class EntityComparator {
|
|
|
482
482
|
const convertorKey = this.safeKey(prop.name);
|
|
483
483
|
context.set(`convertToDatabaseValue_${convertorKey}`, (val) => {
|
|
484
484
|
/* v8 ignore next */
|
|
485
|
-
if (
|
|
485
|
+
if (Raw.isKnownFragment(val)) {
|
|
486
486
|
return val;
|
|
487
487
|
}
|
|
488
488
|
return prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' });
|
|
@@ -597,7 +597,12 @@ export class EntityComparator {
|
|
|
597
597
|
if (prop.customType) {
|
|
598
598
|
if (prop.customType.compareValues) {
|
|
599
599
|
const idx = this.tmpIndex++;
|
|
600
|
-
context.set(`compareValues_${idx}`, (a, b) =>
|
|
600
|
+
context.set(`compareValues_${idx}`, (a, b) => {
|
|
601
|
+
if (Raw.isKnownFragment(a) || Raw.isKnownFragment(b)) {
|
|
602
|
+
return Raw.getKnownFragment(a) === Raw.getKnownFragment(b);
|
|
603
|
+
}
|
|
604
|
+
return prop.customType.compareValues(a, b);
|
|
605
|
+
});
|
|
601
606
|
return this.getGenericComparator(this.wrap(prop.name), `!compareValues_${idx}(last${this.wrap(prop.name)}, current${this.wrap(prop.name)})`);
|
|
602
607
|
}
|
|
603
608
|
type = prop.customType.compareAsType().toLowerCase();
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
|
6
6
|
export declare class QueryHelper {
|
|
7
7
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
8
8
|
static processParams(params: unknown): any;
|
|
9
|
-
static processObjectParams<T extends
|
|
9
|
+
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
10
10
|
/**
|
|
11
11
|
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
12
12
|
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
package/utils/QueryHelper.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Utils } from './Utils.js';
|
|
|
3
3
|
import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, ReferenceKind } from '../enums.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { helper } from '../entity/wrap.js';
|
|
6
|
-
import { isRaw,
|
|
6
|
+
import { isRaw, Raw } from './RawQueryFragment.js';
|
|
7
7
|
/** @internal */
|
|
8
8
|
export class QueryHelper {
|
|
9
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
@@ -29,7 +29,7 @@ export class QueryHelper {
|
|
|
29
29
|
return params;
|
|
30
30
|
}
|
|
31
31
|
static processObjectParams(params = {}) {
|
|
32
|
-
Utils.
|
|
32
|
+
Utils.getObjectQueryKeys(params).forEach(k => {
|
|
33
33
|
params[k] = QueryHelper.processParams(params[k]);
|
|
34
34
|
});
|
|
35
35
|
return params;
|
|
@@ -138,12 +138,10 @@ export class QueryHelper {
|
|
|
138
138
|
if (!Utils.isPlainObject(where)) {
|
|
139
139
|
return where;
|
|
140
140
|
}
|
|
141
|
-
return
|
|
141
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
142
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
143
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
144
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
145
|
o[key] = value;
|
|
148
146
|
return o;
|
|
149
147
|
}
|
|
@@ -157,6 +155,9 @@ export class QueryHelper {
|
|
|
157
155
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
156
|
return o;
|
|
159
157
|
}
|
|
158
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
159
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
160
|
+
const composite = keys > 1;
|
|
160
161
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
162
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
163
|
}
|
|
@@ -164,7 +165,7 @@ export class QueryHelper {
|
|
|
164
165
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
166
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
167
|
}
|
|
167
|
-
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !
|
|
168
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
169
|
// comparing single composite key - use $eq instead of $in
|
|
169
170
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
171
|
o[key] = { [op]: value };
|
|
@@ -231,8 +232,8 @@ export class QueryHelper {
|
|
|
231
232
|
}
|
|
232
233
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
233
234
|
if (Utils.isPlainObject(cond)) {
|
|
234
|
-
return Utils.
|
|
235
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
235
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
236
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
236
237
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
237
238
|
}
|
|
238
239
|
else {
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import type { AnyString, Dictionary, EntityKey } from '../typings.js';
|
|
2
|
+
declare const rawFragmentSymbolBrand: unique symbol;
|
|
3
|
+
export type RawQueryFragmentSymbol = symbol & {
|
|
4
|
+
readonly [rawFragmentSymbolBrand]: true;
|
|
5
|
+
};
|
|
2
6
|
export declare class RawQueryFragment {
|
|
3
7
|
#private;
|
|
4
8
|
readonly sql: string;
|
|
5
9
|
readonly params: unknown[];
|
|
6
|
-
static cloneRegistry?: Set<string>;
|
|
7
10
|
constructor(sql: string, params?: unknown[]);
|
|
11
|
+
get key(): RawQueryFragmentSymbol;
|
|
8
12
|
as(alias: string): RawQueryFragment;
|
|
9
|
-
|
|
13
|
+
[Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
|
|
14
|
+
get [Symbol.toStringTag](): string;
|
|
10
15
|
toJSON(): string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
static
|
|
14
|
-
|
|
15
|
-
* @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
|
|
16
|
-
*/
|
|
17
|
-
static checkCacheSize(): number;
|
|
18
|
-
static isKnownFragment(key: string | RawQueryFragment): boolean;
|
|
19
|
-
static getKnownFragment(key: string | RawQueryFragment, cleanup?: boolean): RawQueryFragment | undefined;
|
|
20
|
-
static remove(key: string): void;
|
|
16
|
+
static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
|
|
17
|
+
static hasObjectFragments(object: unknown): boolean;
|
|
18
|
+
static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
|
|
19
|
+
static getKnownFragment(key: unknown): RawQueryFragment | undefined;
|
|
21
20
|
}
|
|
22
21
|
export { RawQueryFragment as Raw };
|
|
23
22
|
export declare function isRaw(value: unknown): value is RawQueryFragment;
|
|
@@ -1,81 +1,58 @@
|
|
|
1
1
|
import { Utils } from './Utils.js';
|
|
2
|
-
import { createAsyncContext } from './AsyncContext.js';
|
|
3
2
|
export class RawQueryFragment {
|
|
4
3
|
sql;
|
|
5
4
|
params;
|
|
6
|
-
|
|
7
|
-
static #
|
|
8
|
-
static #index = 0n;
|
|
9
|
-
static cloneRegistry;
|
|
10
|
-
#used = 0;
|
|
5
|
+
// holds a weak reference to fragments used as object keys
|
|
6
|
+
static #rawQueryReferences = new WeakMap();
|
|
11
7
|
#key;
|
|
12
8
|
constructor(sql, params = []) {
|
|
13
9
|
this.sql = sql;
|
|
14
10
|
this.params = params;
|
|
15
|
-
|
|
11
|
+
}
|
|
12
|
+
get key() {
|
|
13
|
+
if (!this.#key) {
|
|
14
|
+
this.#key = Symbol(this.toJSON());
|
|
15
|
+
RawQueryFragment.#rawQueryReferences.set(this.#key, this);
|
|
16
|
+
}
|
|
17
|
+
return this.#key;
|
|
16
18
|
}
|
|
17
19
|
as(alias) {
|
|
18
20
|
return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
[Symbol.toPrimitive](hint) {
|
|
23
|
+
// if a fragment is converted to string (used as an object key), return a unique symbol
|
|
24
|
+
// and save a weak reference to map so we can retrieve it when compiling the query
|
|
25
|
+
if (hint === 'string') {
|
|
26
|
+
return this.key;
|
|
27
|
+
}
|
|
21
28
|
throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
|
|
22
29
|
}
|
|
23
|
-
|
|
24
|
-
return this
|
|
25
|
-
}
|
|
26
|
-
toString() {
|
|
27
|
-
RawQueryFragment.#rawQueryCache.set(this.#key, this);
|
|
28
|
-
this.#used++;
|
|
29
|
-
return this.#key;
|
|
30
|
+
get [Symbol.toStringTag]() {
|
|
31
|
+
return this.toJSON();
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return new RawQueryFragment(this.sql, this.params);
|
|
33
|
+
toJSON() {
|
|
34
|
+
return `raw('${this.sql}')`;
|
|
34
35
|
}
|
|
35
|
-
static
|
|
36
|
-
|
|
37
|
-
const res = await this.#storage.run(removeStack, cb);
|
|
38
|
-
removeStack.forEach(key => RawQueryFragment.remove(key));
|
|
39
|
-
removeStack.clear();
|
|
40
|
-
return res;
|
|
36
|
+
static isKnownFragmentSymbol(key) {
|
|
37
|
+
return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
|
|
41
38
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
static checkCacheSize() {
|
|
46
|
-
return this.#rawQueryCache.size;
|
|
39
|
+
static hasObjectFragments(object) {
|
|
40
|
+
return Utils.isPlainObject(object) && Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol));
|
|
47
41
|
}
|
|
48
42
|
static isKnownFragment(key) {
|
|
49
43
|
if (key instanceof RawQueryFragment) {
|
|
50
44
|
return true;
|
|
51
45
|
}
|
|
52
|
-
return this
|
|
46
|
+
return this.isKnownFragmentSymbol(key);
|
|
53
47
|
}
|
|
54
|
-
static getKnownFragment(key
|
|
48
|
+
static getKnownFragment(key) {
|
|
55
49
|
if (key instanceof RawQueryFragment) {
|
|
56
50
|
return key;
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
if (raw && cleanup) {
|
|
60
|
-
this.remove(key);
|
|
61
|
-
}
|
|
62
|
-
return raw;
|
|
63
|
-
}
|
|
64
|
-
static remove(key) {
|
|
65
|
-
const raw = this.#rawQueryCache.get(key);
|
|
66
|
-
if (!raw) {
|
|
52
|
+
if (typeof key !== 'symbol') {
|
|
67
53
|
return;
|
|
68
54
|
}
|
|
69
|
-
|
|
70
|
-
if (raw.#used <= 0) {
|
|
71
|
-
const removeStack = this.#storage.getStore();
|
|
72
|
-
if (removeStack) {
|
|
73
|
-
removeStack.add(key);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
this.#rawQueryCache.delete(key);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
55
|
+
return this.#rawQueryReferences.get(key);
|
|
79
56
|
}
|
|
80
57
|
/** @ignore */
|
|
81
58
|
/* v8 ignore next */
|
|
@@ -193,11 +170,7 @@ export function raw(sql, params) {
|
|
|
193
170
|
* ```
|
|
194
171
|
*/
|
|
195
172
|
export function sql(sql, ...values) {
|
|
196
|
-
return raw(sql.
|
|
197
|
-
const valueExists = i < values.length;
|
|
198
|
-
const text = query + queryPart;
|
|
199
|
-
return valueExists ? text + '?' : text;
|
|
200
|
-
}, ''), values);
|
|
173
|
+
return raw(sql.join('?'), values);
|
|
201
174
|
}
|
|
202
175
|
export function createSqlFunction(func, key) {
|
|
203
176
|
if (typeof key === 'string') {
|
package/utils/Utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadat
|
|
|
2
2
|
import type { Collection } from '../entity/Collection.js';
|
|
3
3
|
import type { Platform } from '../platforms/Platform.js';
|
|
4
4
|
import type { ScalarReference } from '../entity/Reference.js';
|
|
5
|
+
import { type RawQueryFragmentSymbol } from './RawQueryFragment.js';
|
|
5
6
|
export declare function compareObjects(a: any, b: any): boolean;
|
|
6
7
|
export declare function compareArrays(a: any[] | string, b: any[] | string): boolean;
|
|
7
8
|
export declare function compareBooleans(a: unknown, b: unknown): boolean;
|
|
@@ -155,4 +156,5 @@ export declare class Utils {
|
|
|
155
156
|
static values<T extends object>(obj: T): T[keyof T][];
|
|
156
157
|
static entries<T extends object>(obj: T): [keyof T, T[keyof T]][];
|
|
157
158
|
static primaryKeyToObject<T>(meta: EntityMetadata<T>, primaryKey: Primary<T> | T, visible?: (keyof T)[]): T;
|
|
159
|
+
static getObjectQueryKeys<T extends Dictionary, K extends string = Extract<keyof T, string>>(obj: T): (K | RawQueryFragmentSymbol)[];
|
|
158
160
|
}
|
package/utils/Utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { clone } from './clone.js';
|
|
2
2
|
import { GroupOperator, PlainObject, QueryOperator, ReferenceKind } from '../enums.js';
|
|
3
3
|
import { helper } from '../entity/wrap.js';
|
|
4
|
+
import { Raw } from './RawQueryFragment.js';
|
|
4
5
|
function compareConstructors(a, b) {
|
|
5
6
|
if (a.constructor === b.constructor) {
|
|
6
7
|
return true;
|
|
@@ -123,7 +124,7 @@ export function parseJsonSafe(value) {
|
|
|
123
124
|
}
|
|
124
125
|
export class Utils {
|
|
125
126
|
static PK_SEPARATOR = '~~~';
|
|
126
|
-
static #ORM_VERSION = '7.0.0-dev.
|
|
127
|
+
static #ORM_VERSION = '7.0.0-dev.114';
|
|
127
128
|
/**
|
|
128
129
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
129
130
|
*/
|
|
@@ -824,4 +825,12 @@ export class Utils {
|
|
|
824
825
|
return o;
|
|
825
826
|
}, {});
|
|
826
827
|
}
|
|
828
|
+
static getObjectQueryKeys(obj) {
|
|
829
|
+
return Reflect.ownKeys(obj).filter(key => {
|
|
830
|
+
if (!Object.prototype.propertyIsEnumerable.call(obj, key)) {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
return typeof key === 'string' || Raw.isKnownFragmentSymbol(key);
|
|
834
|
+
});
|
|
835
|
+
}
|
|
827
836
|
}
|
package/utils/clone.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* clone `EventEmitter`s to get around https://github.com/mikro-orm/mikro-orm/issues/2748
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
|
-
import { RawQueryFragment } from './RawQueryFragment.js';
|
|
7
6
|
/**
|
|
8
7
|
* Get the property descriptor of a property on an object or its prototype chain.
|
|
9
8
|
*
|
|
@@ -29,10 +28,6 @@ export function clone(parent, respectCustomCloneMethod = true) {
|
|
|
29
28
|
if (parent === null) {
|
|
30
29
|
return null;
|
|
31
30
|
}
|
|
32
|
-
const raw = RawQueryFragment.getKnownFragment(parent, false);
|
|
33
|
-
if (raw && respectCustomCloneMethod) {
|
|
34
|
-
return raw.clone();
|
|
35
|
-
}
|
|
36
31
|
if (typeof parent !== 'object') {
|
|
37
32
|
return parent;
|
|
38
33
|
}
|
|
@@ -113,25 +108,16 @@ export function clone(parent, respectCustomCloneMethod = true) {
|
|
|
113
108
|
if (attrs && typeof attrs.get === 'function' && attrs.set == null) {
|
|
114
109
|
continue;
|
|
115
110
|
}
|
|
116
|
-
const raw = RawQueryFragment.getKnownFragment(i, false);
|
|
117
|
-
if (raw && respectCustomCloneMethod) {
|
|
118
|
-
const i2 = raw.clone().toString();
|
|
119
|
-
child[i2] = _clone(parent[i]);
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
111
|
child[i] = _clone(parent[i]);
|
|
123
112
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (descriptor && !descriptor.enumerable) {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
child[symbol] = _clone(parent[symbol]);
|
|
113
|
+
const symbols = Object.getOwnPropertySymbols(parent);
|
|
114
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
115
|
+
const symbol = symbols[i];
|
|
116
|
+
const descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
|
|
117
|
+
if (descriptor && !descriptor.enumerable) {
|
|
118
|
+
continue;
|
|
134
119
|
}
|
|
120
|
+
child[symbol] = _clone(parent[symbol]);
|
|
135
121
|
}
|
|
136
122
|
return child;
|
|
137
123
|
}
|
package/utils/upsert-utils.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { EntityData, EntityMetadata, FilterQuery } from '../typings.js';
|
|
2
2
|
import type { UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type Raw } from '../utils/RawQueryFragment.js';
|
|
4
4
|
/** @internal */
|
|
5
|
-
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] |
|
|
5
|
+
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T>): (keyof T)[];
|
|
6
6
|
/** @internal */
|
|
7
|
-
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] |
|
|
7
|
+
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T, P>): (keyof T)[] | '*';
|
|
8
8
|
/** @internal */
|
|
9
|
-
export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] |
|
|
9
|
+
export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] | Raw | undefined, data: EntityData<T>, where: FilterQuery<T>): {
|
|
10
10
|
where: FilterQuery<T>;
|
|
11
11
|
propIndex: number | false;
|
|
12
12
|
};
|