@mikro-orm/core 7.1.0-dev.3 → 7.1.0-dev.31
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 +44 -11
- package/EntityManager.js +139 -31
- package/README.md +2 -1
- package/drivers/IDatabaseDriver.d.ts +39 -1
- package/entity/BaseEntity.d.ts +68 -1
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +6 -3
- package/entity/Collection.js +15 -4
- package/entity/EntityFactory.js +20 -1
- package/entity/EntityLoader.d.ts +3 -1
- package/entity/EntityLoader.js +39 -16
- package/entity/EntityRepository.d.ts +27 -9
- package/entity/EntityRepository.js +12 -0
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/defineEntity.d.ts +105 -23
- package/entity/defineEntity.js +17 -6
- package/entity/utils.js +4 -5
- package/errors.d.ts +2 -0
- package/errors.js +4 -0
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/metadata/EntitySchema.js +3 -0
- package/metadata/MetadataDiscovery.d.ts +12 -0
- package/metadata/MetadataDiscovery.js +185 -16
- package/metadata/MetadataValidator.d.ts +24 -0
- package/metadata/MetadataValidator.js +202 -1
- package/metadata/types.d.ts +77 -4
- package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
- package/naming-strategy/NamingStrategy.d.ts +1 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +8 -1
- package/platforms/Platform.js +40 -4
- package/serialization/EntitySerializer.js +2 -1
- package/typings.d.ts +176 -18
- package/typings.js +22 -5
- package/unit-of-work/UnitOfWork.js +3 -0
- package/utils/AbstractMigrator.d.ts +20 -5
- package/utils/AbstractMigrator.js +263 -28
- package/utils/AbstractSchemaGenerator.d.ts +1 -1
- package/utils/AbstractSchemaGenerator.js +4 -1
- package/utils/Configuration.d.ts +25 -0
- package/utils/Configuration.js +1 -0
- package/utils/DataloaderUtils.d.ts +10 -1
- package/utils/DataloaderUtils.js +78 -0
- package/utils/EntityComparator.js +1 -1
- package/utils/QueryHelper.d.ts +16 -0
- package/utils/QueryHelper.js +15 -0
- package/utils/Utils.js +1 -1
- package/utils/fs-utils.d.ts +2 -0
- package/utils/fs-utils.js +7 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/partition-utils.d.ts +17 -0
- package/utils/partition-utils.js +79 -0
package/entity/Collection.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare class Collection<T extends object, O extends object = object> {
|
|
|
37
37
|
/**
|
|
38
38
|
* Gets the count of collection items from database instead of counting loaded items.
|
|
39
39
|
* The value is cached (unless you use the `where` option), use `refresh: true` to force reload it.
|
|
40
|
+
* When the dataloader is enabled (globally or per-query), multiple calls are batched into a single grouped query.
|
|
40
41
|
*/
|
|
41
42
|
loadCount(options?: LoadCountOptions<T> | boolean): Promise<number>;
|
|
42
43
|
/** Queries a subset of the collection items from the database with custom filtering, ordering, and pagination. */
|
|
@@ -48,14 +49,14 @@ export declare class Collection<T extends object, O extends object = object> {
|
|
|
48
49
|
/** Serializes the collection items to plain JSON objects. Returns an empty array if not initialized. */
|
|
49
50
|
toJSON<TT extends T>(): EntityDTO<TT>[];
|
|
50
51
|
/** Adds one or more items to the collection, propagating the change to the inverse side. Returns the number of items added. */
|
|
51
|
-
add<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>>, ...entities: (
|
|
52
|
+
add<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>>, ...entities: (T | Reference<T>)[]): number;
|
|
52
53
|
/**
|
|
53
54
|
* Remove specified item(s) from the collection. Note that removing item from collection does not necessarily imply deleting the target entity,
|
|
54
55
|
* it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
|
|
55
56
|
* is not the same as `em.remove()`. If we want to delete the entity by removing it from collection, we need to enable `orphanRemoval: true`,
|
|
56
57
|
* which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
|
|
57
58
|
*/
|
|
58
|
-
remove<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>> | ((item:
|
|
59
|
+
remove<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>> | ((item: T) => boolean), ...entities: (T | Reference<T>)[]): number;
|
|
59
60
|
/** Checks whether the collection contains the given item. */
|
|
60
61
|
contains<TT extends T>(item: TT | Reference<TT>, check?: boolean): boolean;
|
|
61
62
|
/** Returns the number of items in the collection. Throws if the collection is not initialized. */
|
|
@@ -93,7 +94,7 @@ export declare class Collection<T extends object, O extends object = object> {
|
|
|
93
94
|
/**
|
|
94
95
|
* @internal
|
|
95
96
|
*/
|
|
96
|
-
hydrate(items: T[], forcePropagate?: boolean, partial?: boolean): void;
|
|
97
|
+
hydrate(items: T[], forcePropagate?: boolean, partial?: boolean, readonly?: boolean): void;
|
|
97
98
|
/**
|
|
98
99
|
* Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
|
|
99
100
|
* it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
|
|
@@ -193,4 +194,6 @@ export interface LoadCountOptions<T extends object> extends CountOptions<T, '*'>
|
|
|
193
194
|
refresh?: boolean;
|
|
194
195
|
/** Additional filtering conditions for the count query. */
|
|
195
196
|
where?: FilterQuery<T>;
|
|
197
|
+
/** Whether to use the dataloader for batching count operations. */
|
|
198
|
+
dataloader?: boolean;
|
|
196
199
|
}
|
package/entity/Collection.js
CHANGED
|
@@ -79,10 +79,11 @@ export class Collection {
|
|
|
79
79
|
/**
|
|
80
80
|
* Gets the count of collection items from database instead of counting loaded items.
|
|
81
81
|
* The value is cached (unless you use the `where` option), use `refresh: true` to force reload it.
|
|
82
|
+
* When the dataloader is enabled (globally or per-query), multiple calls are batched into a single grouped query.
|
|
82
83
|
*/
|
|
83
84
|
async loadCount(options = {}) {
|
|
84
85
|
options = typeof options === 'boolean' ? { refresh: options } : options;
|
|
85
|
-
const { refresh, where, ...countOptions } = options;
|
|
86
|
+
const { refresh, where, dataloader, ...countOptions } = options;
|
|
86
87
|
if (!refresh && !where && this.#count != null) {
|
|
87
88
|
return this.#count;
|
|
88
89
|
}
|
|
@@ -92,8 +93,15 @@ export class Collection {
|
|
|
92
93
|
this.property.owner) {
|
|
93
94
|
return (this.#count = this.length);
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
let count;
|
|
97
|
+
if (dataloader ?? [DataloaderType.ALL, DataloaderType.COLLECTION].includes(em.config.getDataloaderType())) {
|
|
98
|
+
const loader = await em.getDataLoader('count');
|
|
99
|
+
count = await loader.load([this, { where, ...countOptions }]);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const cond = this.createLoadCountCondition(where ?? {});
|
|
103
|
+
count = await em.count(this.property.targetMeta.class, cond, countOptions);
|
|
104
|
+
}
|
|
97
105
|
if (!where) {
|
|
98
106
|
this.#count = count;
|
|
99
107
|
}
|
|
@@ -462,7 +470,7 @@ export class Collection {
|
|
|
462
470
|
/**
|
|
463
471
|
* @internal
|
|
464
472
|
*/
|
|
465
|
-
hydrate(items, forcePropagate, partial) {
|
|
473
|
+
hydrate(items, forcePropagate, partial, readonly) {
|
|
466
474
|
for (let i = 0; i < this.#items.size; i++) {
|
|
467
475
|
delete this[i];
|
|
468
476
|
}
|
|
@@ -472,6 +480,9 @@ export class Collection {
|
|
|
472
480
|
this.#count = 0;
|
|
473
481
|
this.add(items);
|
|
474
482
|
this.takeSnapshot(forcePropagate);
|
|
483
|
+
if (readonly) {
|
|
484
|
+
this.#readonly = true;
|
|
485
|
+
}
|
|
475
486
|
}
|
|
476
487
|
/**
|
|
477
488
|
* Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
|
package/entity/EntityFactory.js
CHANGED
|
@@ -316,11 +316,30 @@ export class EntityFactory {
|
|
|
316
316
|
else if (!onCreateOnly && prop.default != null && !isRaw(prop.default) && entity[prop.name] === undefined) {
|
|
317
317
|
entity[prop.name] = prop.default;
|
|
318
318
|
}
|
|
319
|
+
else if (!onCreateOnly &&
|
|
320
|
+
this.#config.get('initNullableProperties') &&
|
|
321
|
+
prop.nullable &&
|
|
322
|
+
prop.default == null &&
|
|
323
|
+
!prop.defaultRaw &&
|
|
324
|
+
entity[prop.name] === undefined) {
|
|
325
|
+
entity[prop.name] = (this.#config.get('forceUndefined') ? undefined : null);
|
|
326
|
+
}
|
|
319
327
|
if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
|
|
320
328
|
const items = prop.array ? entity[prop.name] : [entity[prop.name]];
|
|
321
329
|
for (const item of items) {
|
|
322
330
|
// Embedded sub-properties need all defaults since the DB can't apply them within JSON columns.
|
|
323
|
-
|
|
331
|
+
// For polymorphic embeddables, resolve the actual subtype to avoid setting
|
|
332
|
+
// properties from other subtypes (e.g. Cat's canMeow on a Dog instance).
|
|
333
|
+
let targetMeta = prop.targetMeta;
|
|
334
|
+
if (targetMeta.polymorphs && targetMeta.discriminatorColumn) {
|
|
335
|
+
const discValue = item[targetMeta.discriminatorColumn];
|
|
336
|
+
// eslint-disable-next-line eqeqeq
|
|
337
|
+
const resolved = targetMeta.polymorphs.find(m => m.discriminatorValue == discValue);
|
|
338
|
+
if (resolved) {
|
|
339
|
+
targetMeta = resolved;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
this.assignDefaultValues(item, targetMeta);
|
|
324
343
|
}
|
|
325
344
|
}
|
|
326
345
|
}
|
package/entity/EntityLoader.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyEntity, AutoPath, ConnectionType, EntityName, EntityProperty, FilterQuery, PopulateOptions } from '../typings.js';
|
|
1
|
+
import type { AnyEntity, AutoPath, ConnectionType, EntityName, EntityProperty, FilterQuery, PopulateHintOptions, PopulateOptions } from '../typings.js';
|
|
2
2
|
import type { EntityManager } from '../EntityManager.js';
|
|
3
3
|
import { LoadStrategy, type LockMode, type PopulateHint, PopulatePath, type QueryOrderMap } from '../enums.js';
|
|
4
4
|
import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
@@ -37,6 +37,8 @@ export interface EntityLoaderOptions<Entity, Fields extends string = never, Excl
|
|
|
37
37
|
connectionType?: ConnectionType;
|
|
38
38
|
/** Logging options for the query. */
|
|
39
39
|
logging?: LoggingOptions;
|
|
40
|
+
/** Per-relation populate overrides (limit, offset, orderBy). */
|
|
41
|
+
populateHints?: Record<string, PopulateHintOptions>;
|
|
40
42
|
}
|
|
41
43
|
/** Responsible for batch-loading entity relations using either select-in or joined loading strategies. */
|
|
42
44
|
export declare class EntityLoader {
|
package/entity/EntityLoader.js
CHANGED
|
@@ -177,8 +177,9 @@ export class EntityLoader {
|
|
|
177
177
|
Utils.isObject(orderBy[prop.name]))
|
|
178
178
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
179
179
|
const where = await this.extractChildCondition(options, prop);
|
|
180
|
+
const mergedOrderBy = populate.orderBy ? Utils.asArray(populate.orderBy) : innerOrderBy;
|
|
180
181
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.#driver.getPlatform().usesPivotTable()) {
|
|
181
|
-
const pivotOrderBy = QueryHelper.mergeOrderBy(
|
|
182
|
+
const pivotOrderBy = QueryHelper.mergeOrderBy(mergedOrderBy, prop.orderBy, prop.targetMeta?.orderBy);
|
|
182
183
|
const res = await this.findChildrenFromPivotTable(filtered, prop, options, pivotOrderBy, populate, !!ref);
|
|
183
184
|
return Utils.flatten(res);
|
|
184
185
|
}
|
|
@@ -188,10 +189,12 @@ export class EntityLoader {
|
|
|
188
189
|
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
189
190
|
...options,
|
|
190
191
|
where,
|
|
191
|
-
orderBy:
|
|
192
|
+
orderBy: mergedOrderBy,
|
|
192
193
|
}, !!(ref || prop.mapToPk));
|
|
193
|
-
const
|
|
194
|
-
|
|
194
|
+
const hasLimit = populate.limit != null;
|
|
195
|
+
const isPartial = partial || hasLimit;
|
|
196
|
+
const customOrder = mergedOrderBy.length > 0 || !!prop.orderBy || !!prop.targetMeta?.orderBy;
|
|
197
|
+
this.initializeCollections(filtered, prop, field, items, customOrder, isPartial, hasLimit);
|
|
195
198
|
return items;
|
|
196
199
|
}
|
|
197
200
|
async populateScalar(meta, filtered, options) {
|
|
@@ -269,15 +272,15 @@ export class EntityLoader {
|
|
|
269
272
|
}));
|
|
270
273
|
return allItems;
|
|
271
274
|
}
|
|
272
|
-
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
275
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial, readonly) {
|
|
273
276
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
274
|
-
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
277
|
+
this.initializeOneToMany(filtered, children, prop, field, partial, readonly);
|
|
275
278
|
}
|
|
276
279
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.#driver.getPlatform().usesPivotTable()) {
|
|
277
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
280
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial, readonly);
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
|
-
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
283
|
+
initializeOneToMany(filtered, children, prop, field, partial, readonly) {
|
|
281
284
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
282
285
|
const map = {};
|
|
283
286
|
for (const entity of filtered) {
|
|
@@ -293,14 +296,14 @@ export class EntityLoader {
|
|
|
293
296
|
}
|
|
294
297
|
for (const entity of filtered) {
|
|
295
298
|
const key = helper(entity).getSerializedPrimaryKey();
|
|
296
|
-
entity[field].hydrate(map[key], undefined, partial);
|
|
299
|
+
entity[field].hydrate(map[key], undefined, partial, readonly);
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
|
-
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
302
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial, readonly) {
|
|
300
303
|
if (prop.mappedBy) {
|
|
301
304
|
for (const entity of filtered) {
|
|
302
305
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
303
|
-
entity[field].hydrate(items, true, partial);
|
|
306
|
+
entity[field].hydrate(items, true, partial, readonly);
|
|
304
307
|
}
|
|
305
308
|
}
|
|
306
309
|
else {
|
|
@@ -311,7 +314,7 @@ export class EntityLoader {
|
|
|
311
314
|
if (!customOrder) {
|
|
312
315
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
313
316
|
}
|
|
314
|
-
entity[field].hydrate(items, true, partial);
|
|
317
|
+
entity[field].hydrate(items, true, partial, readonly);
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
320
|
}
|
|
@@ -375,7 +378,7 @@ export class EntityLoader {
|
|
|
375
378
|
where = { $and: [where, prop.where] };
|
|
376
379
|
}
|
|
377
380
|
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, prop.orderBy);
|
|
378
|
-
const
|
|
381
|
+
const findOptions = {
|
|
379
382
|
filters,
|
|
380
383
|
convertCustomTypes,
|
|
381
384
|
lockMode,
|
|
@@ -394,7 +397,15 @@ export class EntityLoader {
|
|
|
394
397
|
refresh: refresh && !children.every(item => options.visited.has(item)),
|
|
395
398
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
396
399
|
visited: options.visited,
|
|
397
|
-
}
|
|
400
|
+
};
|
|
401
|
+
if (populate.limit != null) {
|
|
402
|
+
findOptions._partitionLimit = {
|
|
403
|
+
partitionBy: fk,
|
|
404
|
+
limit: populate.limit,
|
|
405
|
+
offset: populate.offset,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const items = await this.#em.find(meta.class, where, findOptions);
|
|
398
409
|
// For targetKey relations, wire up loaded entities to parent references
|
|
399
410
|
// This is needed because the references were created under alternate key,
|
|
400
411
|
// but loaded entities are stored under PK, so they don't automatically merge
|
|
@@ -582,11 +593,18 @@ export class EntityLoader {
|
|
|
582
593
|
const options2 = { ...options, fields, exclude, populateFilter };
|
|
583
594
|
['limit', 'offset', 'first', 'last', 'before', 'after', 'overfetch'].forEach(prop => delete options2[prop]);
|
|
584
595
|
options2.populate = populate?.children ?? [];
|
|
596
|
+
if (populate?.limit != null) {
|
|
597
|
+
options2._partitionLimit = {
|
|
598
|
+
limit: populate.limit,
|
|
599
|
+
offset: populate.offset,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
585
602
|
if (!Utils.isEmpty(prop.where)) {
|
|
586
603
|
where = { $and: [where, prop.where] };
|
|
587
604
|
}
|
|
588
605
|
const map = await this.#driver.loadFromPivotTable(prop, ids, where, orderBy, this.#em.getTransactionContext(), options2, pivotJoin);
|
|
589
606
|
const children = [];
|
|
607
|
+
const isUnionTargetMN = QueryHelper.isUnionTargetPolymorphic(prop);
|
|
590
608
|
for (let i = 0; i < filtered.length; i++) {
|
|
591
609
|
const entity = filtered[i];
|
|
592
610
|
const items = map[Utils.getPrimaryKeyHash(ids[i])].map(item => {
|
|
@@ -596,7 +614,11 @@ export class EntityLoader {
|
|
|
596
614
|
schema: options.schema ?? this.#em.config.get('schema'),
|
|
597
615
|
});
|
|
598
616
|
}
|
|
599
|
-
|
|
617
|
+
// Union-target items carry their concrete class via `constructor` — dispatch to the right factory call.
|
|
618
|
+
const targetClass = isUnionTargetMN && item.constructor !== Object
|
|
619
|
+
? item.constructor
|
|
620
|
+
: prop.targetMeta.class;
|
|
621
|
+
const entity = this.#em.getEntityFactory().create(targetClass, item, {
|
|
600
622
|
refresh,
|
|
601
623
|
merge: true,
|
|
602
624
|
convertCustomTypes: true,
|
|
@@ -604,7 +626,8 @@ export class EntityLoader {
|
|
|
604
626
|
});
|
|
605
627
|
return this.#em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
|
|
606
628
|
});
|
|
607
|
-
|
|
629
|
+
const hasLimit = populate?.limit != null;
|
|
630
|
+
entity[prop.name].hydrate(items, true, hasLimit, hasLimit);
|
|
608
631
|
children.push(items);
|
|
609
632
|
}
|
|
610
633
|
return children;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PopulatePath } from '../enums.js';
|
|
2
2
|
import type { CreateOptions, EntityManager, MergeOptions } from '../EntityManager.js';
|
|
3
3
|
import type { AssignOptions } from './EntityAssigner.js';
|
|
4
|
-
import type { EntityData,
|
|
5
|
-
import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
|
+
import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityName, FilterQuery, Loaded, Primary, AutoPath, RequiredEntityData, Ref, EntityType, EntityDTO, MergeSelected, FromEntityType, IsSubset, MergeLoaded, ArrayElement, IndexFilterQuery, WithUsingOptions } from '../typings.js';
|
|
5
|
+
import type { CountByOptions, CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
6
6
|
import type { EntityLoaderOptions } from './EntityLoader.js';
|
|
7
7
|
import type { Cursor } from '../utils/Cursor.js';
|
|
8
8
|
/** Repository class providing a type-safe API for querying and persisting a specific entity type. */
|
|
@@ -13,13 +13,17 @@ export declare class EntityRepository<Entity extends object> {
|
|
|
13
13
|
/**
|
|
14
14
|
* Finds first entity matching your `where` query.
|
|
15
15
|
*/
|
|
16
|
-
findOne<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(where: FilterQuery<Entity
|
|
16
|
+
findOne<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(where: [Using] extends [never] ? FilterQuery<Entity> : IndexFilterQuery<Entity, Using>, options?: FindOneOptions<Entity, Hint, Fields, Excludes> & {
|
|
17
|
+
using?: Using | Using[];
|
|
18
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
|
|
17
19
|
/**
|
|
18
20
|
* Finds first entity matching your `where` query. If nothing is found, it will throw an error.
|
|
19
21
|
* You can override the factory for creating this method via `options.failHandler` locally
|
|
20
22
|
* or via `Configuration.findOneOrFailHandler` globally.
|
|
21
23
|
*/
|
|
22
|
-
findOneOrFail<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(where: FilterQuery<Entity>, options?: FindOneOrFailOptions<Entity, Hint, Fields, Excludes>
|
|
24
|
+
findOneOrFail<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(where: [Using] extends [never] ? FilterQuery<Entity> : IndexFilterQuery<Entity, Using>, options?: FindOneOrFailOptions<Entity, Hint, Fields, Excludes> & {
|
|
25
|
+
using?: Using | Using[];
|
|
26
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
23
27
|
/**
|
|
24
28
|
* Creates or updates the entity, based on whether it is already present in the database.
|
|
25
29
|
* This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
|
|
@@ -72,24 +76,28 @@ export declare class EntityRepository<Entity extends object> {
|
|
|
72
76
|
/**
|
|
73
77
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
74
78
|
*/
|
|
75
|
-
find<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(where: FilterQuery<Entity>, options?: FindOptions<Entity, Hint, Fields, Excludes>
|
|
79
|
+
find<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(where: [Using] extends [never] ? FilterQuery<Entity> : IndexFilterQuery<Entity, Using>, options?: FindOptions<Entity, Hint, Fields, Excludes> & {
|
|
80
|
+
using?: Using | Using[];
|
|
81
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
76
82
|
/**
|
|
77
83
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
|
|
78
84
|
* where first element is the array of entities, and the second is the count.
|
|
79
85
|
*/
|
|
80
|
-
findAndCount<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(where: FilterQuery<Entity
|
|
86
|
+
findAndCount<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(where: [Using] extends [never] ? FilterQuery<Entity> : IndexFilterQuery<Entity, Using>, options?: FindOptions<Entity, Hint, Fields, Excludes> & {
|
|
87
|
+
using?: Using | Using[];
|
|
88
|
+
}): Promise<[Loaded<Entity, Hint, Fields, Excludes>[], number]>;
|
|
81
89
|
/**
|
|
82
90
|
* @inheritDoc EntityManager.findByCursor
|
|
83
91
|
*/
|
|
84
|
-
findByCursor<Hint extends string = never, Fields extends string = never, Excludes extends string = never, IncludeCount extends boolean = true>(options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
92
|
+
findByCursor<Hint extends string = never, Fields extends string = never, Excludes extends string = never, IncludeCount extends boolean = true, Using extends string = never>(options: WithUsingOptions<FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>, Entity, Using>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
85
93
|
/**
|
|
86
94
|
* Finds all entities of given type. You can pass additional options via the `options` parameter.
|
|
87
95
|
*/
|
|
88
|
-
findAll<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(options?: FindAllOptions<Entity, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
96
|
+
findAll<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(options?: WithUsingOptions<FindAllOptions<Entity, Hint, Fields, Excludes>, Entity, Using>): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
89
97
|
/**
|
|
90
98
|
* @inheritDoc EntityManager.stream
|
|
91
99
|
*/
|
|
92
|
-
stream<Hint extends string = never, Fields extends string = never, Excludes extends string = never>(options?: StreamOptions<Entity, Hint, Fields, Excludes>): AsyncIterableIterator<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
100
|
+
stream<Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(options?: WithUsingOptions<StreamOptions<Entity, Hint, Fields, Excludes>, Entity, Using>): AsyncIterableIterator<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
93
101
|
/**
|
|
94
102
|
* @inheritDoc EntityManager.insert
|
|
95
103
|
*/
|
|
@@ -199,6 +207,16 @@ export declare class EntityRepository<Entity extends object> {
|
|
|
199
207
|
* Returns total number of entities matching your `where` query.
|
|
200
208
|
*/
|
|
201
209
|
count<Hint extends string = never>(where?: FilterQuery<Entity>, options?: CountOptions<Entity, Hint>): Promise<number>;
|
|
210
|
+
/**
|
|
211
|
+
* Counts entities grouped by one or more properties.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const counts = await repo.countBy('status');
|
|
216
|
+
* // { 'active': 5, 'inactive': 2 }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
countBy(groupBy: EntityKey<Entity> | readonly EntityKey<Entity>[], options?: CountByOptions<Entity>): Promise<Dictionary<number>>;
|
|
202
220
|
/** Returns the entity class name associated with this repository. */
|
|
203
221
|
getEntityName(): string;
|
|
204
222
|
/**
|
|
@@ -194,6 +194,18 @@ export class EntityRepository {
|
|
|
194
194
|
async count(where = {}, options = {}) {
|
|
195
195
|
return this.getEntityManager().count(this.entityName, where, options);
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Counts entities grouped by one or more properties.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* const counts = await repo.countBy('status');
|
|
203
|
+
* // { 'active': 5, 'inactive': 2 }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
async countBy(groupBy, options) {
|
|
207
|
+
return this.getEntityManager().countBy(this.entityName, groupBy, options);
|
|
208
|
+
}
|
|
197
209
|
/** Returns the entity class name associated with this repository. */
|
|
198
210
|
getEntityName() {
|
|
199
211
|
return Utils.className(this.entityName);
|
package/entity/Reference.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
1
|
+
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, LazyRef, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
2
2
|
import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
3
|
/** Wrapper around an entity that provides lazy loading capabilities and identity-preserving reference semantics. */
|
|
4
4
|
export declare class Reference<T extends object> {
|
|
@@ -98,6 +98,47 @@ export declare function ref<I extends unknown | Ref<unknown> | undefined | null,
|
|
|
98
98
|
* shortcut for `Reference.createFromPK(entityType, pk)`
|
|
99
99
|
*/
|
|
100
100
|
export declare function ref<I extends unknown | undefined | null, T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk: I): Ref<T> | AddOptional<typeof pk>;
|
|
101
|
+
/**
|
|
102
|
+
* Unwraps a reference to its underlying value, returning the entity or scalar. Works on any of:
|
|
103
|
+
*
|
|
104
|
+
* - `Ref<T>` / `Reference<T>` (entity) — calls `.unwrap()` to extract the wrapped entity, returns `T`.
|
|
105
|
+
* - `LazyRef<T>` — type-only marker; runtime value is already `T`, so returned as-is.
|
|
106
|
+
* - plain entity `T` — returned as-is.
|
|
107
|
+
* - `ScalarReference<V>` / `ScalarRef<V>` — calls `.unwrap()` to extract the scalar value, returns `V | undefined`
|
|
108
|
+
* (a scalar reference may be bound without an initial value).
|
|
109
|
+
*
|
|
110
|
+
* Inverse of the {@link ref} helper. Use as a typed escape hatch when you know a relation is populated
|
|
111
|
+
* but don't want to (or can't) thread `Loaded<T, 'relation'>` through a function signature.
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* function logAuthor(book: Book) {
|
|
115
|
+
* // `book.author` is typed as `LazyRef<Author>`, so `.name` is not directly accessible
|
|
116
|
+
* console.log(unref(book.author).name);
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* // Scalar reference:
|
|
120
|
+
* const scalar = ref('hello');
|
|
121
|
+
* unref(scalar); // 'hello' (type: string | undefined)
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* Note: `unref` is a compile-time narrowing only for entity refs. On a `LazyRef<T>` or plain entity
|
|
125
|
+
* relation that is a stub (PK only, never populated), reads of non-PK properties will still return
|
|
126
|
+
* `undefined` at runtime — same footgun as a plain non-`Ref` relation.
|
|
127
|
+
*/
|
|
128
|
+
export declare function unref<V>(value: ScalarReference<V>): V | undefined;
|
|
129
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T): T;
|
|
130
|
+
/**
|
|
131
|
+
* Unwraps a reference to its underlying value. Nullable variant — returns `null` unchanged.
|
|
132
|
+
*/
|
|
133
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null): T | null;
|
|
134
|
+
/**
|
|
135
|
+
* Unwraps a reference to its underlying value. Optional variant — returns `undefined` unchanged.
|
|
136
|
+
*/
|
|
137
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | undefined): T | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Unwraps a reference to its underlying value. Nullable variant — returns `null`/`undefined` unchanged.
|
|
140
|
+
*/
|
|
141
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null | undefined): T | null | undefined;
|
|
101
142
|
/**
|
|
102
143
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
103
144
|
*/
|
package/entity/Reference.js
CHANGED
|
@@ -303,6 +303,15 @@ export function ref(entityOrType, pk) {
|
|
|
303
303
|
}
|
|
304
304
|
return Reference.createFromPK(entityOrType, pk);
|
|
305
305
|
}
|
|
306
|
+
export function unref(value) {
|
|
307
|
+
if (value == null) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
if (ScalarReference.isScalarReference(value)) {
|
|
311
|
+
return value.unwrap();
|
|
312
|
+
}
|
|
313
|
+
return Reference.unwrapReference(value);
|
|
314
|
+
}
|
|
306
315
|
/**
|
|
307
316
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
308
317
|
*/
|