@mikro-orm/core 7.0.0-dev.22 → 7.0.0-dev.24
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.d.ts +12 -2
- package/EntityManager.js +40 -53
- package/README.md +1 -2
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Entity.d.ts +15 -0
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +2 -0
- package/decorators/OneToOne.d.ts +2 -0
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/drivers/IDatabaseDriver.d.ts +4 -0
- package/entity/ArrayCollection.d.ts +3 -1
- package/entity/ArrayCollection.js +7 -2
- package/entity/Collection.js +3 -2
- package/entity/EntityFactory.d.ts +6 -0
- package/entity/EntityFactory.js +17 -6
- package/entity/EntityHelper.js +5 -1
- package/entity/EntityLoader.js +27 -19
- package/entity/Reference.d.ts +5 -0
- package/entity/Reference.js +16 -0
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +537 -0
- package/entity/defineEntity.js +690 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +15 -2
- package/enums.js +13 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/hydration/ObjectHydrator.js +1 -1
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +10 -2
- package/metadata/MetadataDiscovery.d.ts +0 -1
- package/metadata/MetadataDiscovery.js +27 -18
- package/package.json +3 -3
- package/platforms/Platform.d.ts +3 -1
- package/serialization/SerializationContext.js +13 -10
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/typings.d.ts +32 -10
- package/typings.js +21 -4
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +14 -10
- package/unit-of-work/UnitOfWork.d.ts +2 -1
- package/unit-of-work/UnitOfWork.js +36 -13
- package/utils/Configuration.d.ts +7 -1
- package/utils/Configuration.js +1 -0
- package/utils/ConfigurationLoader.js +2 -2
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +6 -1
- package/utils/DataloaderUtils.js +37 -20
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +29 -8
- package/utils/QueryHelper.d.ts +6 -0
- package/utils/QueryHelper.js +47 -4
- package/utils/RawQueryFragment.d.ts +34 -0
- package/utils/RawQueryFragment.js +35 -1
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +199 -0
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +31 -7
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.js +9 -1
package/entity/EntityLoader.js
CHANGED
|
@@ -38,7 +38,7 @@ export class EntityLoader {
|
|
|
38
38
|
options.refresh ??= false;
|
|
39
39
|
options.convertCustomTypes ??= true;
|
|
40
40
|
if (references.length > 0) {
|
|
41
|
-
await this.populateScalar(meta, references, options);
|
|
41
|
+
await this.populateScalar(meta, references, { ...options, populateWhere: undefined });
|
|
42
42
|
}
|
|
43
43
|
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup);
|
|
44
44
|
const invalid = populate.find(({ field }) => !this.em.canPopulate(entityName, field));
|
|
@@ -140,18 +140,22 @@ export class EntityLoader {
|
|
|
140
140
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
141
141
|
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils.isObject(orderBy[prop.name]))
|
|
142
142
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
143
|
+
const where = await this.extractChildCondition(options, prop);
|
|
143
144
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
|
|
144
145
|
const res = await this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
|
|
145
146
|
return Utils.flatten(res);
|
|
146
147
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
149
|
+
...options,
|
|
150
|
+
where,
|
|
151
|
+
orderBy: innerOrderBy,
|
|
152
|
+
}, !!(ref || prop.mapToPk));
|
|
153
|
+
this.initializeCollections(filtered, prop, field, items, innerOrderBy.length > 0, partial);
|
|
154
|
+
return items;
|
|
151
155
|
}
|
|
152
156
|
async populateScalar(meta, filtered, options) {
|
|
153
157
|
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
154
|
-
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta
|
|
158
|
+
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta, true)));
|
|
155
159
|
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.metadata, this.driver.getPlatform());
|
|
156
160
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
157
161
|
await this.em.find(meta.className, where, {
|
|
@@ -160,15 +164,15 @@ export class EntityLoader {
|
|
|
160
164
|
populate: [],
|
|
161
165
|
});
|
|
162
166
|
}
|
|
163
|
-
initializeCollections(filtered, prop, field, children, customOrder) {
|
|
167
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
164
168
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
165
|
-
this.initializeOneToMany(filtered, children, prop, field);
|
|
169
|
+
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
166
170
|
}
|
|
167
171
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.driver.getPlatform().usesPivotTable()) {
|
|
168
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder);
|
|
172
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
|
-
initializeOneToMany(filtered, children, prop, field) {
|
|
175
|
+
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
172
176
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
173
177
|
const map = {};
|
|
174
178
|
for (const entity of filtered) {
|
|
@@ -184,14 +188,14 @@ export class EntityLoader {
|
|
|
184
188
|
}
|
|
185
189
|
for (const entity of filtered) {
|
|
186
190
|
const key = helper(entity).getSerializedPrimaryKey();
|
|
187
|
-
entity[field].hydrate(map[key]);
|
|
191
|
+
entity[field].hydrate(map[key], undefined, partial);
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
|
-
initializeManyToMany(filtered, children, prop, field, customOrder) {
|
|
194
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
191
195
|
if (prop.mappedBy) {
|
|
192
196
|
for (const entity of filtered) {
|
|
193
197
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
194
|
-
entity[field].hydrate(items, true);
|
|
198
|
+
entity[field].hydrate(items, true, partial);
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
else { // owning side of M:N without pivot table needs to be reordered
|
|
@@ -201,7 +205,7 @@ export class EntityLoader {
|
|
|
201
205
|
if (!customOrder) {
|
|
202
206
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
203
207
|
}
|
|
204
|
-
entity[field].hydrate(items, true);
|
|
208
|
+
entity[field].hydrate(items, true, partial);
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
}
|
|
@@ -210,6 +214,7 @@ export class EntityLoader {
|
|
|
210
214
|
const meta = prop.targetMeta;
|
|
211
215
|
let fk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
212
216
|
let schema = options.schema;
|
|
217
|
+
const partial = !Utils.isEmpty(prop.where) || !Utils.isEmpty(options.where);
|
|
213
218
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.owner)) {
|
|
214
219
|
fk = meta.properties[prop.mappedBy].name;
|
|
215
220
|
}
|
|
@@ -219,7 +224,7 @@ export class EntityLoader {
|
|
|
219
224
|
children.push(...this.filterByReferences(entities, prop.name, options.refresh));
|
|
220
225
|
}
|
|
221
226
|
if (children.length === 0) {
|
|
222
|
-
return [];
|
|
227
|
+
return { items: [], partial };
|
|
223
228
|
}
|
|
224
229
|
if (!schema && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
225
230
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
@@ -228,7 +233,7 @@ export class EntityLoader {
|
|
|
228
233
|
let where = this.mergePrimaryCondition(ids, fk, options, meta, this.metadata, this.driver.getPlatform());
|
|
229
234
|
const fields = this.buildFields(options.fields, prop, ref);
|
|
230
235
|
/* eslint-disable prefer-const */
|
|
231
|
-
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, } = options;
|
|
236
|
+
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere = 'infer', connectionType, logging, } = options;
|
|
232
237
|
/* eslint-enable prefer-const */
|
|
233
238
|
if (typeof populateWhere === 'object') {
|
|
234
239
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
@@ -272,7 +277,7 @@ export class EntityLoader {
|
|
|
272
277
|
this.em.getUnitOfWork()['loadedEntities'].delete(item);
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
|
-
return items;
|
|
280
|
+
return { items, partial };
|
|
276
281
|
}
|
|
277
282
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
278
283
|
const cond1 = QueryHelper.processWhere({ where: { [pk]: { $in: ids } }, entityName: meta.className, metadata, platform, convertCustomTypes: !options.convertCustomTypes });
|
|
@@ -321,11 +326,12 @@ export class EntityLoader {
|
|
|
321
326
|
for (const entity of entities) {
|
|
322
327
|
visited.delete(entity);
|
|
323
328
|
}
|
|
324
|
-
const
|
|
329
|
+
const unique = Utils.unique(children);
|
|
330
|
+
const filtered = unique.filter(e => !visited.has(e));
|
|
325
331
|
for (const entity of entities) {
|
|
326
332
|
visited.add(entity);
|
|
327
333
|
}
|
|
328
|
-
await this.populate(prop.type,
|
|
334
|
+
await this.populate(prop.type, unique, populate.children ?? populate.all, {
|
|
329
335
|
where: await this.extractChildCondition(options, prop, false),
|
|
330
336
|
orderBy: innerOrderBy,
|
|
331
337
|
fields,
|
|
@@ -342,6 +348,8 @@ export class EntityLoader {
|
|
|
342
348
|
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
343
349
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
344
350
|
visited: options.visited,
|
|
351
|
+
// @ts-ignore not a public option
|
|
352
|
+
filtered,
|
|
345
353
|
});
|
|
346
354
|
}
|
|
347
355
|
/** @internal */
|
package/entity/Reference.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export declare class ScalarReference<Value> {
|
|
|
56
56
|
* Returns either the whole entity, or the requested property.
|
|
57
57
|
*/
|
|
58
58
|
load(options?: Omit<LoadReferenceOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
61
|
+
* Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
|
|
62
|
+
*/
|
|
63
|
+
loadOrFail(options?: Omit<LoadReferenceOrFailOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value>;
|
|
59
64
|
set(value: Value): void;
|
|
60
65
|
bind<Entity extends object>(entity: Entity, property: EntityKey<Entity>): void;
|
|
61
66
|
unwrap(): Value | undefined;
|
package/entity/Reference.js
CHANGED
|
@@ -2,6 +2,7 @@ import { inspect } from 'node:util';
|
|
|
2
2
|
import { DataloaderType } from '../enums.js';
|
|
3
3
|
import { helper, wrap } from './wrap.js';
|
|
4
4
|
import { Utils } from '../utils/Utils.js';
|
|
5
|
+
import { NotFoundError } from '../errors.js';
|
|
5
6
|
export class Reference {
|
|
6
7
|
entity;
|
|
7
8
|
constructor(entity) {
|
|
@@ -171,6 +172,21 @@ export class ScalarReference {
|
|
|
171
172
|
}
|
|
172
173
|
return this.value;
|
|
173
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
177
|
+
* Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
|
|
178
|
+
*/
|
|
179
|
+
async loadOrFail(options = {}) {
|
|
180
|
+
const ret = await this.load(options);
|
|
181
|
+
if (!ret) {
|
|
182
|
+
const wrapped = helper(this.entity);
|
|
183
|
+
options.failHandler ??= wrapped.__em.config.get('findOneOrFailHandler');
|
|
184
|
+
const entityName = this.entity.constructor.name;
|
|
185
|
+
const where = wrapped.getPrimaryKey();
|
|
186
|
+
throw new NotFoundError(`${entityName} (${where}) failed to load property '${this.property}'`);
|
|
187
|
+
}
|
|
188
|
+
return ret;
|
|
189
|
+
}
|
|
174
190
|
set(value) {
|
|
175
191
|
this.value = value;
|
|
176
192
|
this.initialized = true;
|
package/entity/WrappedEntity.js
CHANGED
|
@@ -150,7 +150,7 @@ export class WrappedEntity {
|
|
|
150
150
|
return this.__em?.config ?? this.entity.__config;
|
|
151
151
|
}
|
|
152
152
|
get __primaryKeys() {
|
|
153
|
-
return Utils.getPrimaryKeyValues(this.entity, this.__meta
|
|
153
|
+
return Utils.getPrimaryKeyValues(this.entity, this.__meta);
|
|
154
154
|
}
|
|
155
155
|
/** @ignore */
|
|
156
156
|
[inspect.custom]() {
|