@mikro-orm/core 7.0.0-rc.2 → 7.0.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/EntityManager.d.ts +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
|
@@ -110,6 +110,22 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
|
|
|
110
110
|
* when nesting the condition. This is used for implementation of joined filters.
|
|
111
111
|
*/
|
|
112
112
|
populateFilter?: ObjectQuery<Entity>;
|
|
113
|
+
/**
|
|
114
|
+
* Index-friendly alternative to `$or` for conditions that span joined relations.
|
|
115
|
+
* Each array element becomes an independent branch combined via `UNION ALL` subquery:
|
|
116
|
+
* `WHERE pk IN (branch_1 UNION ALL branch_2 ... branch_N)`.
|
|
117
|
+
* The database plans each branch independently, enabling per-table index usage
|
|
118
|
+
* (e.g. GIN trigram indexes for fuzzy search across related entities).
|
|
119
|
+
* sql only
|
|
120
|
+
*/
|
|
121
|
+
unionWhere?: ObjectQuery<Entity>[];
|
|
122
|
+
/**
|
|
123
|
+
* Strategy for combining `unionWhere` branches.
|
|
124
|
+
* - `'union-all'` (default) — skips deduplication, faster for most use cases.
|
|
125
|
+
* - `'union'` — deduplicates rows between branches; useful when branch overlap is very high.
|
|
126
|
+
* sql only
|
|
127
|
+
*/
|
|
128
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
113
129
|
/** Used for ordering of the populate queries. If not specified, the value of `options.orderBy` is used. */
|
|
114
130
|
populateOrderBy?: OrderDefinition<Entity>;
|
|
115
131
|
/** Per-relation overrides for populate loading behavior. Keys are populate paths (same as used in `populate`). */
|
|
@@ -199,6 +215,13 @@ export interface NativeInsertUpdateOptions<T> {
|
|
|
199
215
|
/** `nativeUpdate()` only option */
|
|
200
216
|
upsert?: boolean;
|
|
201
217
|
loggerContext?: LogContext;
|
|
218
|
+
/** sql only */
|
|
219
|
+
unionWhere?: ObjectQuery<T>[];
|
|
220
|
+
/** sql only */
|
|
221
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
222
|
+
filters?: FilterOptions;
|
|
223
|
+
/** @internal */
|
|
224
|
+
em?: EntityManager;
|
|
202
225
|
}
|
|
203
226
|
export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOptions<T> {
|
|
204
227
|
processCollections?: boolean;
|
|
@@ -223,6 +246,10 @@ export interface CountOptions<T extends object, P extends string = never> {
|
|
|
223
246
|
populate?: Populate<T, P>;
|
|
224
247
|
populateWhere?: ObjectQuery<T> | PopulateHint | `${PopulateHint}`;
|
|
225
248
|
populateFilter?: ObjectQuery<T>;
|
|
249
|
+
/** @see FindOptions.unionWhere */
|
|
250
|
+
unionWhere?: ObjectQuery<T>[];
|
|
251
|
+
/** @see FindOptions.unionWhereStrategy */
|
|
252
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
226
253
|
ctx?: Transaction;
|
|
227
254
|
connectionType?: ConnectionType;
|
|
228
255
|
flushMode?: FlushMode | `${FlushMode}`;
|
|
@@ -245,12 +272,28 @@ export interface UpdateOptions<T> {
|
|
|
245
272
|
filters?: FilterOptions;
|
|
246
273
|
schema?: string;
|
|
247
274
|
ctx?: Transaction;
|
|
275
|
+
/** sql only */
|
|
276
|
+
unionWhere?: ObjectQuery<T>[];
|
|
277
|
+
/** sql only */
|
|
278
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
248
279
|
}
|
|
249
280
|
export interface DeleteOptions<T> extends DriverMethodOptions {
|
|
250
281
|
filters?: FilterOptions;
|
|
282
|
+
/** sql only */
|
|
283
|
+
unionWhere?: ObjectQuery<T>[];
|
|
284
|
+
/** sql only */
|
|
285
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
286
|
+
/** @internal */
|
|
287
|
+
em?: EntityManager;
|
|
251
288
|
}
|
|
252
289
|
export interface NativeDeleteOptions<T> extends DriverMethodOptions {
|
|
253
290
|
filters?: FilterOptions;
|
|
291
|
+
/** sql only */
|
|
292
|
+
unionWhere?: ObjectQuery<T>[];
|
|
293
|
+
/** sql only */
|
|
294
|
+
unionWhereStrategy?: 'union-all' | 'union';
|
|
295
|
+
/** @internal */
|
|
296
|
+
em?: EntityManager;
|
|
254
297
|
}
|
|
255
298
|
export interface LockOptions extends DriverMethodOptions {
|
|
256
299
|
lockMode?: LockMode;
|
package/entity/Collection.d.ts
CHANGED
|
@@ -9,17 +9,9 @@ export interface MatchingOptions<T extends object, P extends string = never> ext
|
|
|
9
9
|
ctx?: Transaction;
|
|
10
10
|
}
|
|
11
11
|
export declare class Collection<T extends object, O extends object = object> {
|
|
12
|
+
#private;
|
|
12
13
|
readonly owner: O;
|
|
13
14
|
[k: number]: T;
|
|
14
|
-
protected readonly items: Set<T>;
|
|
15
|
-
protected initialized: boolean;
|
|
16
|
-
protected dirty: boolean;
|
|
17
|
-
protected partial: boolean;
|
|
18
|
-
protected snapshot: T[] | undefined;
|
|
19
|
-
private readonly?;
|
|
20
|
-
protected _count?: number;
|
|
21
|
-
private _property?;
|
|
22
|
-
private _populated?;
|
|
23
15
|
constructor(owner: O, items?: T[], initialized?: boolean);
|
|
24
16
|
/**
|
|
25
17
|
* Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
|
package/entity/Collection.js
CHANGED
|
@@ -7,24 +7,24 @@ import { QueryHelper } from '../utils/QueryHelper.js';
|
|
|
7
7
|
import { inspect } from '../logging/inspect.js';
|
|
8
8
|
export class Collection {
|
|
9
9
|
owner;
|
|
10
|
-
items = new Set();
|
|
11
|
-
initialized = true;
|
|
12
|
-
dirty = false;
|
|
13
|
-
partial = false; // mark partially loaded collections, propagation is disabled for those
|
|
14
|
-
snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
|
|
15
|
-
readonly;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
#items = new Set();
|
|
11
|
+
#initialized = true;
|
|
12
|
+
#dirty = false;
|
|
13
|
+
#partial = false; // mark partially loaded collections, propagation is disabled for those
|
|
14
|
+
#snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
|
|
15
|
+
#readonly;
|
|
16
|
+
#count;
|
|
17
|
+
#property;
|
|
18
|
+
#populated;
|
|
19
19
|
constructor(owner, items, initialized = true) {
|
|
20
20
|
this.owner = owner;
|
|
21
21
|
/* v8 ignore next */
|
|
22
22
|
if (items) {
|
|
23
23
|
let i = 0;
|
|
24
|
-
this
|
|
25
|
-
this
|
|
24
|
+
this.#items = new Set(items);
|
|
25
|
+
this.#items.forEach(item => (this[i++] = item));
|
|
26
26
|
}
|
|
27
|
-
this
|
|
27
|
+
this.#initialized = !!items || initialized;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
|
|
@@ -44,9 +44,9 @@ export class Collection {
|
|
|
44
44
|
*/
|
|
45
45
|
async load(options = {}) {
|
|
46
46
|
if (this.isInitialized(true) && !options.refresh) {
|
|
47
|
-
const em = this.getEntityManager(this
|
|
47
|
+
const em = this.getEntityManager(this.#items, false);
|
|
48
48
|
options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
|
|
49
|
-
await em?.populate(this
|
|
49
|
+
await em?.populate(this.#items, options.populate, options);
|
|
50
50
|
this.setSerializationContext(options);
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
@@ -58,7 +58,7 @@ export class Collection {
|
|
|
58
58
|
helper(this.owner).setSerializationContext({
|
|
59
59
|
populate: Array.isArray(options.populate)
|
|
60
60
|
? options.populate.map(hint => `${this.property.name}.${hint}`)
|
|
61
|
-
: options.populate ?? [this.property.name],
|
|
61
|
+
: (options.populate ?? [this.property.name]),
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
@@ -75,17 +75,19 @@ export class Collection {
|
|
|
75
75
|
async loadCount(options = {}) {
|
|
76
76
|
options = typeof options === 'boolean' ? { refresh: options } : options;
|
|
77
77
|
const { refresh, where, ...countOptions } = options;
|
|
78
|
-
if (!refresh && !where && this
|
|
79
|
-
return this
|
|
78
|
+
if (!refresh && !where && this.#count != null) {
|
|
79
|
+
return this.#count;
|
|
80
80
|
}
|
|
81
81
|
const em = this.getEntityManager();
|
|
82
|
-
if (!em.getPlatform().usesPivotTable() &&
|
|
83
|
-
|
|
82
|
+
if (!em.getPlatform().usesPivotTable() &&
|
|
83
|
+
this.property.kind === ReferenceKind.MANY_TO_MANY &&
|
|
84
|
+
this.property.owner) {
|
|
85
|
+
return (this.#count = this.length);
|
|
84
86
|
}
|
|
85
87
|
const cond = this.createLoadCountCondition(where ?? {});
|
|
86
88
|
const count = await em.count(this.property.targetMeta.class, cond, countOptions);
|
|
87
89
|
if (!where) {
|
|
88
|
-
this
|
|
90
|
+
this.#count = count;
|
|
89
91
|
}
|
|
90
92
|
return count;
|
|
91
93
|
}
|
|
@@ -96,22 +98,24 @@ export class Collection {
|
|
|
96
98
|
if (this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
|
|
97
99
|
// M:N via pivot table bypasses em.find(), so merge all 3 levels here
|
|
98
100
|
opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
|
|
99
|
-
options.populate = await em.preparePopulate(this.property.targetMeta.class, options);
|
|
100
|
-
const cond = await em.applyFilters(this.property.targetMeta.class, where, options.filters ?? {}, 'read');
|
|
101
|
-
const map = await em
|
|
101
|
+
options.populate = (await em.preparePopulate(this.property.targetMeta.class, options));
|
|
102
|
+
const cond = (await em.applyFilters(this.property.targetMeta.class, where, options.filters ?? {}, 'read'));
|
|
103
|
+
const map = await em
|
|
104
|
+
.getDriver()
|
|
105
|
+
.loadFromPivotTable(this.property, [helper(this.owner).__primaryKeys], cond, opts.orderBy, ctx, options);
|
|
102
106
|
items = map[helper(this.owner).getSerializedPrimaryKey()].map((item) => em.merge(this.property.targetMeta.class, item, { convertCustomTypes: true }));
|
|
103
107
|
await em.populate(items, options.populate, options);
|
|
104
108
|
}
|
|
105
109
|
else {
|
|
106
110
|
// em.find() merges entity-level orderBy, so only merge runtime + relation here
|
|
107
111
|
opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy);
|
|
108
|
-
items = await em.find(this.property.targetMeta.class, this.createCondition(where), opts);
|
|
112
|
+
items = (await em.find(this.property.targetMeta.class, this.createCondition(where), opts));
|
|
109
113
|
}
|
|
110
114
|
if (options.store) {
|
|
111
115
|
this.hydrate(items, true);
|
|
112
116
|
this.setSerializationContext(options);
|
|
113
117
|
this.populated();
|
|
114
|
-
this
|
|
118
|
+
this.#readonly = true;
|
|
115
119
|
}
|
|
116
120
|
return items;
|
|
117
121
|
}
|
|
@@ -122,7 +126,7 @@ export class Collection {
|
|
|
122
126
|
if (check) {
|
|
123
127
|
this.checkInitialized();
|
|
124
128
|
}
|
|
125
|
-
return [...this
|
|
129
|
+
return [...this.#items];
|
|
126
130
|
}
|
|
127
131
|
toJSON() {
|
|
128
132
|
if (!this.isInitialized()) {
|
|
@@ -140,10 +144,10 @@ export class Collection {
|
|
|
140
144
|
const entity = Reference.unwrapReference(item);
|
|
141
145
|
if (!this.contains(entity, false)) {
|
|
142
146
|
this.incrementCount(1);
|
|
143
|
-
this[this
|
|
144
|
-
this
|
|
147
|
+
this[this.#items.size] = entity;
|
|
148
|
+
this.#items.add(entity);
|
|
145
149
|
added++;
|
|
146
|
-
this
|
|
150
|
+
this.#dirty = true;
|
|
147
151
|
this.propagate(entity, 'add');
|
|
148
152
|
}
|
|
149
153
|
}
|
|
@@ -162,7 +166,7 @@ export class Collection {
|
|
|
162
166
|
remove(entity, ...entities) {
|
|
163
167
|
if (entity instanceof Function) {
|
|
164
168
|
let removed = 0;
|
|
165
|
-
for (const item of this
|
|
169
|
+
for (const item of this.#items) {
|
|
166
170
|
if (entity(item)) {
|
|
167
171
|
removed += this.remove(item);
|
|
168
172
|
}
|
|
@@ -180,12 +184,12 @@ export class Collection {
|
|
|
180
184
|
continue;
|
|
181
185
|
}
|
|
182
186
|
const entity = Reference.unwrapReference(item);
|
|
183
|
-
if (this
|
|
187
|
+
if (this.#items.delete(entity)) {
|
|
184
188
|
this.incrementCount(-1);
|
|
185
|
-
delete this[this
|
|
189
|
+
delete this[this.#items.size]; // remove last item
|
|
186
190
|
this.propagate(entity, 'remove');
|
|
187
191
|
removed++;
|
|
188
|
-
this
|
|
192
|
+
this.#dirty = true;
|
|
189
193
|
}
|
|
190
194
|
if (this.property.orphanRemoval && em) {
|
|
191
195
|
em.getUnitOfWork().scheduleOrphanRemoval(entity);
|
|
@@ -195,7 +199,7 @@ export class Collection {
|
|
|
195
199
|
em.persist(entities);
|
|
196
200
|
}
|
|
197
201
|
if (removed > 0) {
|
|
198
|
-
Object.assign(this, [...this
|
|
202
|
+
Object.assign(this, [...this.#items]); // reassign array access
|
|
199
203
|
}
|
|
200
204
|
return removed;
|
|
201
205
|
}
|
|
@@ -204,11 +208,11 @@ export class Collection {
|
|
|
204
208
|
this.checkInitialized();
|
|
205
209
|
}
|
|
206
210
|
const entity = Reference.unwrapReference(item);
|
|
207
|
-
return this
|
|
211
|
+
return this.#items.has(entity);
|
|
208
212
|
}
|
|
209
213
|
count() {
|
|
210
214
|
this.checkInitialized();
|
|
211
|
-
return this
|
|
215
|
+
return this.#items.size;
|
|
212
216
|
}
|
|
213
217
|
isEmpty() {
|
|
214
218
|
this.checkInitialized();
|
|
@@ -218,18 +222,18 @@ export class Collection {
|
|
|
218
222
|
if (!this.isInitialized(true)) {
|
|
219
223
|
return false;
|
|
220
224
|
}
|
|
221
|
-
if (this
|
|
222
|
-
return this
|
|
225
|
+
if (this.#populated != null) {
|
|
226
|
+
return this.#populated;
|
|
223
227
|
}
|
|
224
228
|
return !!populated;
|
|
225
229
|
}
|
|
226
230
|
populated(populated = true) {
|
|
227
|
-
this
|
|
231
|
+
this.#populated = populated;
|
|
228
232
|
}
|
|
229
233
|
async init(options = {}) {
|
|
230
|
-
if (this
|
|
231
|
-
const items = [...this
|
|
232
|
-
this
|
|
234
|
+
if (this.#dirty) {
|
|
235
|
+
const items = [...this.#items];
|
|
236
|
+
this.#dirty = false;
|
|
233
237
|
await this.init(options);
|
|
234
238
|
items.forEach(i => this.add(i));
|
|
235
239
|
return this;
|
|
@@ -237,36 +241,34 @@ export class Collection {
|
|
|
237
241
|
const em = this.getEntityManager();
|
|
238
242
|
options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
|
|
239
243
|
if (options.dataloader ?? [DataloaderType.ALL, DataloaderType.COLLECTION].includes(em.config.getDataloaderType())) {
|
|
240
|
-
const order = [...this
|
|
244
|
+
const order = [...this.#items]; // copy order of references
|
|
241
245
|
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
|
|
242
246
|
const customOrder = orderBy.length > 0;
|
|
243
247
|
const pivotTable = this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable();
|
|
244
248
|
const loader = await em.getDataLoader(pivotTable ? 'm:n' : '1:m');
|
|
245
249
|
const items = await loader.load([this, { ...options, orderBy }]);
|
|
246
250
|
if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
|
|
247
|
-
this
|
|
248
|
-
this
|
|
251
|
+
this.#initialized = true;
|
|
252
|
+
this.#dirty = false;
|
|
249
253
|
if (!customOrder) {
|
|
250
254
|
this.reorderItems(items, order);
|
|
251
255
|
}
|
|
252
256
|
return this;
|
|
253
257
|
}
|
|
254
|
-
this
|
|
258
|
+
this.#items.clear();
|
|
255
259
|
let i = 0;
|
|
256
260
|
for (const item of items) {
|
|
257
|
-
this
|
|
261
|
+
this.#items.add(item);
|
|
258
262
|
this[i++] = item;
|
|
259
263
|
}
|
|
260
|
-
this
|
|
261
|
-
this
|
|
264
|
+
this.#initialized = true;
|
|
265
|
+
this.#dirty = false;
|
|
262
266
|
return this;
|
|
263
267
|
}
|
|
264
268
|
const populate = Array.isArray(options.populate)
|
|
265
|
-
? options.populate.map(f => f === '*' ? f : `${this.property.name}.${f}`)
|
|
269
|
+
? options.populate.map(f => (f === '*' ? f : `${this.property.name}.${f}`))
|
|
266
270
|
: [`${this.property.name}${options.ref ? ':ref' : ''}`];
|
|
267
|
-
const schema = this.property.targetMeta.schema === '*'
|
|
268
|
-
? helper(this.owner).__schema
|
|
269
|
-
: undefined;
|
|
271
|
+
const schema = this.property.targetMeta.schema === '*' ? helper(this.owner).__schema : undefined;
|
|
270
272
|
await em.populate(this.owner, populate, {
|
|
271
273
|
refresh: true,
|
|
272
274
|
...options,
|
|
@@ -297,7 +299,8 @@ export class Collection {
|
|
|
297
299
|
if (this.property.kind === ReferenceKind.ONE_TO_MANY) {
|
|
298
300
|
cond[this.property.mappedBy] = helper(this.owner).getPrimaryKey();
|
|
299
301
|
}
|
|
300
|
-
else {
|
|
302
|
+
else {
|
|
303
|
+
// MANY_TO_MANY
|
|
301
304
|
this.createManyToManyCondition(cond);
|
|
302
305
|
}
|
|
303
306
|
return cond;
|
|
@@ -308,7 +311,7 @@ export class Collection {
|
|
|
308
311
|
// we know there is at least one item as it was checked in load method
|
|
309
312
|
const pk = this.property.targetMeta.primaryKeys[0];
|
|
310
313
|
dict[pk] = { $in: [] };
|
|
311
|
-
this
|
|
314
|
+
this.#items.forEach(item => dict[pk].$in.push(helper(item).getPrimaryKey()));
|
|
312
315
|
}
|
|
313
316
|
else {
|
|
314
317
|
dict[this.property.mappedBy] = helper(this.owner).getPrimaryKey();
|
|
@@ -350,7 +353,7 @@ export class Collection {
|
|
|
350
353
|
}
|
|
351
354
|
}
|
|
352
355
|
validateModification(items) {
|
|
353
|
-
if (this
|
|
356
|
+
if (this.#readonly) {
|
|
354
357
|
throw ValidationError.cannotModifyReadonlyCollection(this.owner, this.property);
|
|
355
358
|
}
|
|
356
359
|
const check = (item) => {
|
|
@@ -375,7 +378,7 @@ export class Collection {
|
|
|
375
378
|
}
|
|
376
379
|
}
|
|
377
380
|
toArray() {
|
|
378
|
-
if (this
|
|
381
|
+
if (this.#items.size === 0) {
|
|
379
382
|
return [];
|
|
380
383
|
}
|
|
381
384
|
return this.map(item => wrap(item).toJSON());
|
|
@@ -386,7 +389,9 @@ export class Collection {
|
|
|
386
389
|
if (items.length === 0) {
|
|
387
390
|
return [];
|
|
388
391
|
}
|
|
389
|
-
field ??= targetMeta.compositePK
|
|
392
|
+
field ??= targetMeta.compositePK
|
|
393
|
+
? targetMeta.primaryKeys
|
|
394
|
+
: (targetMeta.serializedPrimaryKey ?? targetMeta.primaryKeys[0]);
|
|
390
395
|
const cb = (i, f) => {
|
|
391
396
|
if (Utils.isEntity(i[f], true)) {
|
|
392
397
|
return wrap(i[f], true).getPrimaryKey();
|
|
@@ -406,28 +411,28 @@ export class Collection {
|
|
|
406
411
|
addWithoutPropagation(entity) {
|
|
407
412
|
if (!this.contains(entity, false)) {
|
|
408
413
|
this.incrementCount(1);
|
|
409
|
-
this[this
|
|
410
|
-
this
|
|
411
|
-
this
|
|
414
|
+
this[this.#items.size] = entity;
|
|
415
|
+
this.#items.add(entity);
|
|
416
|
+
this.#dirty = true;
|
|
412
417
|
}
|
|
413
418
|
}
|
|
414
419
|
set(items) {
|
|
415
|
-
if (!this
|
|
416
|
-
this
|
|
417
|
-
this
|
|
420
|
+
if (!this.#initialized) {
|
|
421
|
+
this.#initialized = true;
|
|
422
|
+
this.#snapshot = undefined;
|
|
418
423
|
}
|
|
419
424
|
if (this.compare(Utils.asArray(items).map(item => Reference.unwrapReference(item)))) {
|
|
420
425
|
return;
|
|
421
426
|
}
|
|
422
|
-
this.remove(this
|
|
427
|
+
this.remove(this.#items);
|
|
423
428
|
this.add(items);
|
|
424
429
|
}
|
|
425
430
|
compare(items) {
|
|
426
|
-
if (items.length !== this
|
|
431
|
+
if (items.length !== this.#items.size) {
|
|
427
432
|
return false;
|
|
428
433
|
}
|
|
429
434
|
let idx = 0;
|
|
430
|
-
for (const item of this
|
|
435
|
+
for (const item of this.#items) {
|
|
431
436
|
if (item !== items[idx++]) {
|
|
432
437
|
return false;
|
|
433
438
|
}
|
|
@@ -438,13 +443,13 @@ export class Collection {
|
|
|
438
443
|
* @internal
|
|
439
444
|
*/
|
|
440
445
|
hydrate(items, forcePropagate, partial) {
|
|
441
|
-
for (let i = 0; i < this
|
|
446
|
+
for (let i = 0; i < this.#items.size; i++) {
|
|
442
447
|
delete this[i];
|
|
443
448
|
}
|
|
444
|
-
this
|
|
445
|
-
this
|
|
446
|
-
this
|
|
447
|
-
this
|
|
449
|
+
this.#initialized = true;
|
|
450
|
+
this.#partial = !!partial;
|
|
451
|
+
this.#items.clear();
|
|
452
|
+
this.#count = 0;
|
|
448
453
|
this.add(items);
|
|
449
454
|
this.takeSnapshot(forcePropagate);
|
|
450
455
|
}
|
|
@@ -455,24 +460,24 @@ export class Collection {
|
|
|
455
460
|
* which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
|
|
456
461
|
*/
|
|
457
462
|
removeAll() {
|
|
458
|
-
if (!this
|
|
459
|
-
this
|
|
460
|
-
this
|
|
463
|
+
if (!this.#initialized) {
|
|
464
|
+
this.#initialized = true;
|
|
465
|
+
this.#snapshot = undefined;
|
|
461
466
|
}
|
|
462
|
-
this.remove(this
|
|
463
|
-
this
|
|
467
|
+
this.remove(this.#items);
|
|
468
|
+
this.#dirty = true;
|
|
464
469
|
}
|
|
465
470
|
/**
|
|
466
471
|
* @internal
|
|
467
472
|
*/
|
|
468
473
|
removeWithoutPropagation(entity) {
|
|
469
|
-
if (!this
|
|
474
|
+
if (!this.#items.delete(entity)) {
|
|
470
475
|
return;
|
|
471
476
|
}
|
|
472
477
|
this.incrementCount(-1);
|
|
473
|
-
delete this[this
|
|
474
|
-
Object.assign(this, [...this
|
|
475
|
-
this
|
|
478
|
+
delete this[this.#items.size];
|
|
479
|
+
Object.assign(this, [...this.#items]);
|
|
480
|
+
this.#dirty = true;
|
|
476
481
|
}
|
|
477
482
|
/**
|
|
478
483
|
* Extracts a slice of the collection items starting at position start to end (exclusive) of the collection.
|
|
@@ -481,9 +486,9 @@ export class Collection {
|
|
|
481
486
|
slice(start = 0, end) {
|
|
482
487
|
this.checkInitialized();
|
|
483
488
|
let index = 0;
|
|
484
|
-
end ??= this
|
|
489
|
+
end ??= this.#items.size;
|
|
485
490
|
const items = [];
|
|
486
|
-
for (const item of this
|
|
491
|
+
for (const item of this.#items) {
|
|
487
492
|
if (index === end) {
|
|
488
493
|
break;
|
|
489
494
|
}
|
|
@@ -499,7 +504,7 @@ export class Collection {
|
|
|
499
504
|
*/
|
|
500
505
|
exists(cb) {
|
|
501
506
|
this.checkInitialized();
|
|
502
|
-
for (const item of this
|
|
507
|
+
for (const item of this.#items) {
|
|
503
508
|
if (cb(item)) {
|
|
504
509
|
return true;
|
|
505
510
|
}
|
|
@@ -512,7 +517,7 @@ export class Collection {
|
|
|
512
517
|
find(cb) {
|
|
513
518
|
this.checkInitialized();
|
|
514
519
|
let index = 0;
|
|
515
|
-
for (const item of this
|
|
520
|
+
for (const item of this.#items) {
|
|
516
521
|
if (cb(item, index++)) {
|
|
517
522
|
return item;
|
|
518
523
|
}
|
|
@@ -526,7 +531,7 @@ export class Collection {
|
|
|
526
531
|
this.checkInitialized();
|
|
527
532
|
const items = [];
|
|
528
533
|
let index = 0;
|
|
529
|
-
for (const item of this
|
|
534
|
+
for (const item of this.#items) {
|
|
530
535
|
if (cb(item, index++)) {
|
|
531
536
|
items.push(item);
|
|
532
537
|
}
|
|
@@ -540,7 +545,7 @@ export class Collection {
|
|
|
540
545
|
this.checkInitialized();
|
|
541
546
|
const items = [];
|
|
542
547
|
let index = 0;
|
|
543
|
-
for (const item of this
|
|
548
|
+
for (const item of this.#items) {
|
|
544
549
|
items.push(mapper(item, index++));
|
|
545
550
|
}
|
|
546
551
|
return items;
|
|
@@ -551,7 +556,7 @@ export class Collection {
|
|
|
551
556
|
reduce(cb, initial = {}) {
|
|
552
557
|
this.checkInitialized();
|
|
553
558
|
let index = 0;
|
|
554
|
-
for (const item of this
|
|
559
|
+
for (const item of this.#items) {
|
|
555
560
|
initial = cb(initial, item, index++);
|
|
556
561
|
}
|
|
557
562
|
return initial;
|
|
@@ -567,10 +572,10 @@ export class Collection {
|
|
|
567
572
|
}, {});
|
|
568
573
|
}
|
|
569
574
|
isInitialized(fully = false) {
|
|
570
|
-
if (!this
|
|
571
|
-
return this
|
|
575
|
+
if (!this.#initialized || !fully) {
|
|
576
|
+
return this.#initialized;
|
|
572
577
|
}
|
|
573
|
-
for (const item of this
|
|
578
|
+
for (const item of this.#items) {
|
|
574
579
|
if (!helper(item).__initialized) {
|
|
575
580
|
return false;
|
|
576
581
|
}
|
|
@@ -578,13 +583,13 @@ export class Collection {
|
|
|
578
583
|
return true;
|
|
579
584
|
}
|
|
580
585
|
isDirty() {
|
|
581
|
-
return this
|
|
586
|
+
return this.#dirty;
|
|
582
587
|
}
|
|
583
588
|
isPartial() {
|
|
584
|
-
return this
|
|
589
|
+
return this.#partial;
|
|
585
590
|
}
|
|
586
591
|
setDirty(dirty = true) {
|
|
587
|
-
this
|
|
592
|
+
this.#dirty = dirty;
|
|
588
593
|
}
|
|
589
594
|
get length() {
|
|
590
595
|
return this.count();
|
|
@@ -598,10 +603,10 @@ export class Collection {
|
|
|
598
603
|
* @internal
|
|
599
604
|
*/
|
|
600
605
|
takeSnapshot(forcePropagate) {
|
|
601
|
-
this
|
|
602
|
-
this
|
|
606
|
+
this.#snapshot = [...this.#items];
|
|
607
|
+
this.#dirty = false;
|
|
603
608
|
if (this.property.owner || forcePropagate) {
|
|
604
|
-
this
|
|
609
|
+
this.#items.forEach(item => {
|
|
605
610
|
this.propagate(item, 'takeSnapshot');
|
|
606
611
|
});
|
|
607
612
|
}
|
|
@@ -610,27 +615,29 @@ export class Collection {
|
|
|
610
615
|
* @internal
|
|
611
616
|
*/
|
|
612
617
|
getSnapshot() {
|
|
613
|
-
return this
|
|
618
|
+
return this.#snapshot;
|
|
614
619
|
}
|
|
615
620
|
/**
|
|
616
621
|
* @internal
|
|
617
622
|
*/
|
|
618
623
|
get property() {
|
|
619
|
-
|
|
624
|
+
// cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
|
|
625
|
+
if (!this.#property) {
|
|
620
626
|
const meta = wrap(this.owner, true).__meta;
|
|
621
627
|
/* v8 ignore next */
|
|
622
628
|
if (!meta) {
|
|
623
629
|
throw MetadataError.fromUnknownEntity(this.owner.constructor.name, 'Collection.property getter, maybe you just forgot to initialize the ORM?');
|
|
624
630
|
}
|
|
625
|
-
this
|
|
631
|
+
this.#property = meta.relations.find(prop => this.owner[prop.name] === this);
|
|
626
632
|
}
|
|
627
|
-
return this
|
|
633
|
+
return this.#property;
|
|
628
634
|
}
|
|
629
635
|
/**
|
|
630
636
|
* @internal
|
|
631
637
|
*/
|
|
632
638
|
set property(prop) {
|
|
633
|
-
|
|
639
|
+
// cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
|
|
640
|
+
this.#property = prop;
|
|
634
641
|
}
|
|
635
642
|
propagate(item, method) {
|
|
636
643
|
if (this.property.owner && this.property.inversedBy) {
|
|
@@ -689,22 +696,31 @@ export class Collection {
|
|
|
689
696
|
}
|
|
690
697
|
}
|
|
691
698
|
incrementCount(value) {
|
|
692
|
-
if (typeof this
|
|
693
|
-
this
|
|
699
|
+
if (typeof this.#count === 'number' && this.#initialized) {
|
|
700
|
+
this.#count += value;
|
|
694
701
|
}
|
|
695
702
|
}
|
|
696
703
|
/** @ignore */
|
|
697
704
|
[Symbol.for('nodejs.util.inspect.custom')](depth = 2) {
|
|
698
705
|
const object = { ...this };
|
|
699
|
-
|
|
700
|
-
|
|
706
|
+
delete object.owner;
|
|
707
|
+
object.initialized = this.#initialized;
|
|
708
|
+
object.dirty = this.#dirty;
|
|
701
709
|
const ret = inspect(object, { depth });
|
|
702
710
|
const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
|
|
703
711
|
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
704
712
|
}
|
|
705
713
|
}
|
|
706
714
|
Object.defineProperties(Collection.prototype, {
|
|
707
|
-
$: {
|
|
708
|
-
|
|
715
|
+
$: {
|
|
716
|
+
get() {
|
|
717
|
+
return this;
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
get: {
|
|
721
|
+
get() {
|
|
722
|
+
return () => this;
|
|
723
|
+
},
|
|
724
|
+
},
|
|
709
725
|
__collection: { value: true, enumerable: false, writable: false },
|
|
710
726
|
});
|