@mikro-orm/core 7.2.7-dev.5 → 7.2.7-dev.7
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 +6 -1
- package/entity/EntityLoader.js +10 -2
- package/package.json +1 -1
- package/platforms/Platform.d.ts +2 -0
- package/platforms/Platform.js +4 -0
- package/typings.d.ts +6 -3
- package/unit-of-work/UnitOfWork.js +13 -5
- package/utils/Utils.js +1 -1
package/EntityManager.js
CHANGED
|
@@ -743,10 +743,15 @@ export class EntityManager {
|
|
|
743
743
|
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
744
744
|
throw new Error('Explicit `orderBy` option required');
|
|
745
745
|
}
|
|
746
|
+
const meta = this.metadata.get(entityName);
|
|
747
|
+
// same merge as em.find, so the cursor and the query see the same orderBy
|
|
748
|
+
if (meta.orderBy) {
|
|
749
|
+
options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
|
|
750
|
+
}
|
|
746
751
|
const [entities, count] = options.includeCount !== false
|
|
747
752
|
? await em.findAndCount(entityName, options.where, options)
|
|
748
753
|
: [await em.find(entityName, options.where, options)];
|
|
749
|
-
return new Cursor(entities, count, options,
|
|
754
|
+
return new Cursor(entities, count, options, meta);
|
|
750
755
|
}
|
|
751
756
|
/**
|
|
752
757
|
* Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
|
package/entity/EntityLoader.js
CHANGED
|
@@ -679,12 +679,20 @@ export class EntityLoader {
|
|
|
679
679
|
const subCond = Utils.isPlainObject(where[prop.name]) ? { ...where[prop.name] } : {};
|
|
680
680
|
const meta2 = prop.targetMeta;
|
|
681
681
|
const pk = Utils.getPrimaryKeyHash(meta2.primaryKeys);
|
|
682
|
+
// `$not` wrapping entity conditions stays on the target entity, only operators on the relation itself map to its PK
|
|
683
|
+
const isRelationOperator = (key, value) => {
|
|
684
|
+
if (key === '$not' && Utils.isPlainObject(value)) {
|
|
685
|
+
return Utils.getObjectQueryKeys(value).every(k => Utils.isOperator(k, false));
|
|
686
|
+
}
|
|
687
|
+
return Utils.isOperator(key, false);
|
|
688
|
+
};
|
|
682
689
|
['$and', '$or'].forEach(op => {
|
|
683
690
|
if (where[op]) {
|
|
684
691
|
const child = where[op]
|
|
685
692
|
.map((cond) => cond[prop.name])
|
|
686
693
|
.filter((sub) => sub != null &&
|
|
687
|
-
!(Utils.isPlainObject(sub) &&
|
|
694
|
+
!(Utils.isPlainObject(sub) &&
|
|
695
|
+
Utils.getObjectQueryKeys(sub).every(key => isRelationOperator(key, sub[key]))))
|
|
688
696
|
.map((cond) => {
|
|
689
697
|
if (Utils.isPrimaryKey(cond)) {
|
|
690
698
|
return { [pk]: cond };
|
|
@@ -697,7 +705,7 @@ export class EntityLoader {
|
|
|
697
705
|
}
|
|
698
706
|
}
|
|
699
707
|
});
|
|
700
|
-
const operators = Object.keys(subCond).filter(key =>
|
|
708
|
+
const operators = Object.keys(subCond).filter(key => isRelationOperator(key, subCond[key]));
|
|
701
709
|
if (operators.length > 0) {
|
|
702
710
|
subCond[pk] = Utils.isPlainObject(subCond[pk]) ? { ...subCond[pk] } : (subCond[pk] ?? {});
|
|
703
711
|
operators.forEach(op => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.2.7-dev.
|
|
3
|
+
"version": "7.2.7-dev.7",
|
|
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
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -204,6 +204,8 @@ export declare abstract class Platform {
|
|
|
204
204
|
supportsUnionWhere(): boolean;
|
|
205
205
|
/** Returns the SQL type declaration used for array storage. */
|
|
206
206
|
getArrayDeclarationSQL(): string;
|
|
207
|
+
/** Whether array columns support the `&&` (overlap) operator. */
|
|
208
|
+
supportsArrayOverlap(): boolean;
|
|
207
209
|
/** Serializes a string array into its database storage format. */
|
|
208
210
|
marshallArray(values: string[]): string;
|
|
209
211
|
/** Deserializes a database-stored array string back into a string array. */
|
package/platforms/Platform.js
CHANGED
|
@@ -360,6 +360,10 @@ export class Platform {
|
|
|
360
360
|
getArrayDeclarationSQL() {
|
|
361
361
|
return 'text';
|
|
362
362
|
}
|
|
363
|
+
/** Whether array columns support the `&&` (overlap) operator. */
|
|
364
|
+
supportsArrayOverlap() {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
363
367
|
/** Serializes a string array into its database storage format. */
|
|
364
368
|
marshallArray(values) {
|
|
365
369
|
return values.map(v => (/[,",\\]/.test(v) ? JSON.stringify(v) : v)).join(',');
|
package/typings.d.ts
CHANGED
|
@@ -356,12 +356,15 @@ type ElemMatchCondition<T extends Record<string, any>> = {
|
|
|
356
356
|
$and?: ElemMatchCondition<T>[];
|
|
357
357
|
$not?: ElemMatchCondition<T>;
|
|
358
358
|
};
|
|
359
|
-
type
|
|
359
|
+
type ArrayFilter<T> = T extends readonly (infer E)[] ? [E] extends [Scalar] ? {
|
|
360
|
+
$in?: readonly (readonly ExpandScalar<E>[])[];
|
|
361
|
+
$nin?: readonly (readonly ExpandScalar<E>[])[];
|
|
362
|
+
} : E extends Record<string, any> ? {
|
|
360
363
|
$elemMatch: ElemMatchCondition<E>;
|
|
361
364
|
} : never : never;
|
|
362
365
|
/** Object form of a filter query, mapping entity keys to their filter conditions. */
|
|
363
366
|
export type FilterObject<T> = {
|
|
364
|
-
-readonly [K in EntityKey<T>]?: ExpandQuery<ExpandProperty<FilterObjectProp<T, K>>> | ExpandQueryMerged<ExpandProperty<FilterObjectProp<T, K>>> | FilterValue<ExpandProperty<FilterObjectProp<T, K>>> |
|
|
367
|
+
-readonly [K in EntityKey<T>]?: ExpandQuery<ExpandProperty<FilterObjectProp<T, K>>> | ExpandQueryMerged<ExpandProperty<FilterObjectProp<T, K>>> | FilterValue<ExpandProperty<FilterObjectProp<T, K>>> | ArrayFilter<FilterObjectProp<T, K>> | null;
|
|
365
368
|
};
|
|
366
369
|
/** Recursively expands a type into its `FilterQuery` form for nested object filtering. */
|
|
367
370
|
export type ExpandQuery<T> = T extends object ? (T extends Scalar ? never : FilterQuery<T>) : FilterValue<T>;
|
|
@@ -381,7 +384,7 @@ export type FilterQuery<T> = ObjectQuery<T> | NonNullable<ExpandScalar<Primary<T
|
|
|
381
384
|
* Used when `using` option is set in `FindOptions` to enforce type-safe index usage.
|
|
382
385
|
*/
|
|
383
386
|
export type IndexFilterQuery<T, Using extends string> = [Using] extends [never] ? FilterQuery<T> : (OperatorMap<T> & {
|
|
384
|
-
-readonly [K in Extract<EntityKey<T>, IndexColumns<T, Using>>]?: ExpandQuery<ExpandProperty<FilterObjectProp<T, K>>> | ExpandQueryMerged<ExpandProperty<FilterObjectProp<T, K>>> | FilterValue<ExpandProperty<FilterObjectProp<T, K>>> |
|
|
387
|
+
-readonly [K in Extract<EntityKey<T>, IndexColumns<T, Using>>]?: ExpandQuery<ExpandProperty<FilterObjectProp<T, K>>> | ExpandQueryMerged<ExpandProperty<FilterObjectProp<T, K>>> | FilterValue<ExpandProperty<FilterObjectProp<T, K>>> | ArrayFilter<FilterObjectProp<T, K>> | null;
|
|
385
388
|
}) | NonNullable<ExpandScalar<Primary<T>>> | IndexFilterQuery<T, Using>[];
|
|
386
389
|
/** Replaces `where` and `using` on an options type with index-aware variants when `Using` is specified. */
|
|
387
390
|
export type WithUsingOptions<Opts, Entity, Using extends string> = Omit<Opts, 'where' | 'using'> & {
|
|
@@ -600,19 +600,27 @@ export class UnitOfWork {
|
|
|
600
600
|
this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
|
+
// hashes of the inserts' PKs and unique values per type, built lazily, so matching a removed entity is a set lookup
|
|
604
|
+
const insertHashes = {};
|
|
603
605
|
for (const entity of this.#removeStack) {
|
|
604
606
|
const wrapped = helper(entity);
|
|
605
607
|
/* v8 ignore next */
|
|
606
608
|
if (wrapped.__processing) {
|
|
607
609
|
continue;
|
|
608
610
|
}
|
|
609
|
-
const
|
|
610
|
-
let
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
611
|
+
const uniqueName = wrapped.__meta.uniqueName;
|
|
612
|
+
let hashes = insertHashes[uniqueName];
|
|
613
|
+
if (!hashes) {
|
|
614
|
+
hashes = insertHashes[uniqueName] = new Set();
|
|
615
|
+
for (const cs of inserts[uniqueName] ?? []) {
|
|
616
|
+
hashes.add(cs.getSerializedPrimaryKey());
|
|
617
|
+
for (const hash of this.expandUniqueProps(cs.entity)) {
|
|
618
|
+
hashes.add(hash);
|
|
619
|
+
}
|
|
614
620
|
}
|
|
615
621
|
}
|
|
622
|
+
const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
|
|
623
|
+
const type = deletePkHash.some(hash => hashes.has(hash)) ? ChangeSetType.DELETE_EARLY : ChangeSetType.DELETE;
|
|
616
624
|
this.computeChangeSet(entity, type);
|
|
617
625
|
}
|
|
618
626
|
}
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.2.7-dev.
|
|
156
|
+
static #ORM_VERSION = '7.2.7-dev.7';
|
|
157
157
|
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
158
158
|
static getRlsSettingName(filterName, argName) {
|
|
159
159
|
return `mikro.${filterName}.${argName}`;
|