• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 14870110473

06 May 2025 09:30PM UTC coverage: 76.301% (-0.05%) from 76.346%
14870110473

push

github

web-flow
fix: update/delete/softDelete by criteria of condition objects (#10910)

Co-authored-by: maxbronnikov10 <maxbronnikov2004@gmail.com>

9180 of 12736 branches covered (72.08%)

Branch coverage included in aggregate %.

48 of 51 new or added lines in 2 files covered. (94.12%)

12 existing lines in 4 files now uncovered.

18818 of 23958 relevant lines covered (78.55%)

197423.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.4
/src/entity-manager/EntityManager.ts
1
import { DataSource } from "../data-source/DataSource"
2
import { FindManyOptions } from "../find-options/FindManyOptions"
3
import { EntityTarget } from "../common/EntityTarget"
4
import { ObjectType } from "../common/ObjectType"
5
import { EntityNotFoundError } from "../error/EntityNotFoundError"
40✔
6
import { QueryRunnerProviderAlreadyReleasedError } from "../error/QueryRunnerProviderAlreadyReleasedError"
40✔
7
import { FindOneOptions } from "../find-options/FindOneOptions"
8
import { DeepPartial } from "../common/DeepPartial"
9
import { RemoveOptions } from "../repository/RemoveOptions"
10
import { SaveOptions } from "../repository/SaveOptions"
11
import { NoNeedToReleaseEntityManagerError } from "../error/NoNeedToReleaseEntityManagerError"
40✔
12
import { MongoRepository } from "../repository/MongoRepository"
40✔
13
import { TreeRepository } from "../repository/TreeRepository"
40✔
14
import { Repository } from "../repository/Repository"
40✔
15
import { FindOptionsUtils } from "../find-options/FindOptionsUtils"
40✔
16
import { PlainObjectToNewEntityTransformer } from "../query-builder/transformer/PlainObjectToNewEntityTransformer"
40✔
17
import { PlainObjectToDatabaseEntityTransformer } from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer"
40✔
18
import {
40✔
19
    CustomRepositoryCannotInheritRepositoryError,
20
    CustomRepositoryNotFoundError,
21
    TreeRepositoryNotSupportedError,
22
    TypeORMError,
23
} from "../error"
24
import { AbstractRepository } from "../repository/AbstractRepository"
40✔
25
import { QueryRunner } from "../query-runner/QueryRunner"
26
import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder"
27
import { QueryDeepPartialEntity } from "../query-builder/QueryPartialEntity"
28
import { EntityPersistExecutor } from "../persistence/EntityPersistExecutor"
40✔
29
import { ObjectId } from "../driver/mongodb/typings"
30
import { InsertResult } from "../query-builder/result/InsertResult"
31
import { UpdateResult } from "../query-builder/result/UpdateResult"
32
import { DeleteResult } from "../query-builder/result/DeleteResult"
33
import { FindOptionsWhere } from "../find-options/FindOptionsWhere"
34
import { IsolationLevel } from "../driver/types/IsolationLevel"
35
import { ObjectUtils } from "../util/ObjectUtils"
40✔
36
import { getMetadataArgsStorage } from "../globals"
40✔
37
import { UpsertOptions } from "../repository/UpsertOptions"
38
import { InstanceChecker } from "../util/InstanceChecker"
40✔
39
import { ObjectLiteral } from "../common/ObjectLiteral"
40
import { PickKeysByType } from "../common/PickKeysByType"
41
import { OrmUtils } from "../util/OrmUtils"
40✔
42

43
/**
44
 * Entity manager supposed to work with any entity, automatically find its repository and call its methods,
45
 * whatever entity type are you passing.
46
 */
47
export class EntityManager {
40✔
48
    readonly "@instanceof" = Symbol.for("EntityManager")
489,086✔
49

50
    // -------------------------------------------------------------------------
51
    // Public Properties
52
    // -------------------------------------------------------------------------
53

54
    /**
55
     * Connection used by this entity manager.
56
     */
57
    readonly connection: DataSource
58

59
    /**
60
     * Custom query runner to be used for operations in this entity manager.
61
     * Used only in non-global entity manager.
62
     */
63
    readonly queryRunner?: QueryRunner
64

65
    // -------------------------------------------------------------------------
66
    // Protected Properties
67
    // -------------------------------------------------------------------------
68

69
    /**
70
     * Once created and then reused by repositories.
71
     * Created as a future replacement for the #repositories to provide a bit more perf optimization.
72
     */
73
    protected repositories = new Map<EntityTarget<any>, Repository<any>>()
489,086✔
74

75
    /**
76
     * Once created and then reused by repositories.
77
     */
78
    protected treeRepositories: TreeRepository<any>[] = []
489,086✔
79

80
    /**
81
     * Plain to object transformer used in create and merge operations.
82
     */
83
    protected plainObjectToEntityTransformer =
489,086✔
84
        new PlainObjectToNewEntityTransformer()
85

86
    // -------------------------------------------------------------------------
87
    // Constructor
88
    // -------------------------------------------------------------------------
89

90
    constructor(connection: DataSource, queryRunner?: QueryRunner) {
91
        this.connection = connection
489,086✔
92
        if (queryRunner) {
489,086✔
93
            this.queryRunner = queryRunner
467,537✔
94
            // dynamic: this.queryRunner = manager;
95
            ObjectUtils.assign(this.queryRunner, { manager: this })
467,537✔
96
        }
97
    }
98

99
    // -------------------------------------------------------------------------
100
    // Public Methods
101
    // -------------------------------------------------------------------------
102

103
    /**
104
     * Wraps given function execution (and all operations made there) in a transaction.
105
     * All database operations must be executed using provided entity manager.
106
     */
107
    async transaction<T>(
108
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
109
    ): Promise<T>
110

111
    /**
112
     * Wraps given function execution (and all operations made there) in a transaction.
113
     * All database operations must be executed using provided entity manager.
114
     */
115
    async transaction<T>(
116
        isolationLevel: IsolationLevel,
117
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
118
    ): Promise<T>
119

120
    /**
121
     * Wraps given function execution (and all operations made there) in a transaction.
122
     * All database operations must be executed using provided entity manager.
123
     */
124
    async transaction<T>(
125
        isolationOrRunInTransaction:
126
            | IsolationLevel
127
            | ((entityManager: EntityManager) => Promise<T>),
128
        runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
129
    ): Promise<T> {
130
        const isolation =
131
            typeof isolationOrRunInTransaction === "string"
1,412✔
132
                ? isolationOrRunInTransaction
133
                : undefined
134
        const runInTransaction =
135
            typeof isolationOrRunInTransaction === "function"
1,412✔
136
                ? isolationOrRunInTransaction
137
                : runInTransactionParam
138

139
        if (!runInTransaction) {
1,412!
140
            throw new TypeORMError(
×
141
                `Transaction method requires callback in second parameter if isolation level is supplied.`,
142
            )
143
        }
144

145
        if (this.queryRunner && this.queryRunner.isReleased)
1,412!
146
            throw new QueryRunnerProviderAlreadyReleasedError()
×
147

148
        // if query runner is already defined in this class, it means this entity manager was already created for a single connection
149
        // if its not defined we create a new query runner - single connection where we'll execute all our operations
150
        const queryRunner =
151
            this.queryRunner || this.connection.createQueryRunner()
1,412✔
152

153
        try {
1,412✔
154
            await queryRunner.startTransaction(isolation)
1,412✔
155
            const result = await runInTransaction(queryRunner.manager)
1,406✔
156
            await queryRunner.commitTransaction()
987✔
157
            return result
985✔
158
        } catch (err) {
159
            try {
427✔
160
                // we throw original error even if rollback thrown an error
161
                await queryRunner.rollbackTransaction()
427✔
162
            } catch (rollbackError) {}
163
            throw err
427✔
164
        } finally {
165
            if (!this.queryRunner)
1,412✔
166
                // if we used a new query runner provider then release it
167
                await queryRunner.release()
893✔
168
        }
169
    }
170

171
    /**
172
     * Executes raw SQL query and returns raw database results.
173
     *
174
     * @see [Official docs](https://typeorm.io/entity-manager-api) for examples.
175
     */
176
    async query<T = any>(query: string, parameters?: any[]): Promise<T> {
177
        return this.connection.query(query, parameters, this.queryRunner)
279✔
178
    }
179

180
    /**
181
     * Creates a new query builder that can be used to build a SQL query.
182
     */
183
    createQueryBuilder<Entity extends ObjectLiteral>(
184
        entityClass: EntityTarget<Entity>,
185
        alias: string,
186
        queryRunner?: QueryRunner,
187
    ): SelectQueryBuilder<Entity>
188

189
    /**
190
     * Creates a new query builder that can be used to build a SQL query.
191
     */
192
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
193

194
    /**
195
     * Creates a new query builder that can be used to build a SQL query.
196
     */
197
    createQueryBuilder<Entity extends ObjectLiteral>(
198
        entityClass?: EntityTarget<Entity> | QueryRunner,
199
        alias?: string,
200
        queryRunner?: QueryRunner,
201
    ): SelectQueryBuilder<Entity> {
202
        if (alias) {
1,061,860✔
203
            return this.connection.createQueryBuilder(
691,498✔
204
                entityClass as EntityTarget<Entity>,
205
                alias,
206
                queryRunner || this.queryRunner,
1,215,249✔
207
            )
208
        } else {
209
            return this.connection.createQueryBuilder(
370,362✔
210
                (entityClass as QueryRunner | undefined) ||
1,110,180✔
211
                    queryRunner ||
212
                    this.queryRunner,
213
            )
214
        }
215
    }
216

217
    /**
218
     * Checks if entity has an id.
219
     */
220
    hasId(entity: any): boolean
221

222
    /**
223
     * Checks if entity of given schema name has an id.
224
     */
225
    hasId(target: Function | string, entity: any): boolean
226

227
    /**
228
     * Checks if entity has an id by its Function type or schema name.
229
     */
230
    hasId(targetOrEntity: any | Function | string, maybeEntity?: any): boolean {
231
        const target =
232
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
523!
233
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
523!
234
        const metadata = this.connection.getMetadata(target)
523✔
235
        return metadata.hasId(entity)
523✔
236
    }
237

238
    /**
239
     * Gets entity mixed id.
240
     */
241
    getId(entity: any): any
242

243
    /**
244
     * Gets entity mixed id.
245
     */
246
    getId(target: EntityTarget<any>, entity: any): any
247

248
    /**
249
     * Gets entity mixed id.
250
     */
251
    getId(targetOrEntity: any | EntityTarget<any>, maybeEntity?: any): any {
252
        const target =
253
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
120!
254
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
120!
255
        const metadata = this.connection.getMetadata(target)
120✔
256
        return metadata.getEntityIdMixedMap(entity)
120✔
257
    }
258

259
    /**
260
     * Creates a new entity instance and copies all entity properties from this object into a new entity.
261
     * Note that it copies only properties that present in entity schema.
262
     */
263
    create<Entity, EntityLike extends DeepPartial<Entity>>(
264
        entityClass: EntityTarget<Entity>,
265
        plainObject?: EntityLike,
266
    ): Entity
267

268
    /**
269
     * Creates a new entities and copies all entity properties from given objects into their new entities.
270
     * Note that it copies only properties that present in entity schema.
271
     */
272
    create<Entity, EntityLike extends DeepPartial<Entity>>(
273
        entityClass: EntityTarget<Entity>,
274
        plainObjects?: EntityLike[],
275
    ): Entity[]
276

277
    /**
278
     * Creates a new entity instance or instances.
279
     * Can copy properties from the given object into new entities.
280
     */
281
    create<Entity, EntityLike extends DeepPartial<Entity>>(
282
        entityClass: EntityTarget<Entity>,
283
        plainObjectOrObjects?: EntityLike | EntityLike[],
284
    ): Entity | Entity[] {
285
        const metadata = this.connection.getMetadata(entityClass)
5,925✔
286

287
        if (!plainObjectOrObjects) return metadata.create(this.queryRunner)
5,925✔
288

289
        if (Array.isArray(plainObjectOrObjects))
3,382✔
290
            return (plainObjectOrObjects as EntityLike[]).map(
160✔
291
                (plainEntityLike) => this.create(entityClass, plainEntityLike),
400✔
292
            )
293

294
        const mergeIntoEntity = metadata.create(this.queryRunner)
3,222✔
295
        this.plainObjectToEntityTransformer.transform(
3,222✔
296
            mergeIntoEntity,
297
            plainObjectOrObjects,
298
            metadata,
299
            true,
300
        )
301
        return mergeIntoEntity
3,222✔
302
    }
303

304
    /**
305
     * Merges two entities into one new entity.
306
     */
307
    merge<Entity extends ObjectLiteral>(
308
        entityClass: EntityTarget<Entity>,
309
        mergeIntoEntity: Entity,
310
        ...entityLikes: DeepPartial<Entity>[]
311
    ): Entity {
312
        // todo: throw exception if entity manager is released
313
        const metadata = this.connection.getMetadata(entityClass)
1,635,372✔
314
        entityLikes.forEach((object) =>
1,635,372✔
315
            this.plainObjectToEntityTransformer.transform(
1,635,541✔
316
                mergeIntoEntity,
317
                object,
318
                metadata,
319
            ),
320
        )
321
        return mergeIntoEntity
1,635,372✔
322
    }
323

324
    /**
325
     * Creates a new entity from the given plain javascript object. If entity already exist in the database, then
326
     * it loads it (and everything related to it), replaces all values with the new ones from the given object
327
     * and returns this new entity. This new entity is actually a loaded from the db entity with all properties
328
     * replaced from the new object.
329
     */
330
    async preload<Entity extends ObjectLiteral>(
331
        entityClass: EntityTarget<Entity>,
332
        entityLike: DeepPartial<Entity>,
333
    ): Promise<Entity | undefined> {
334
        const metadata = this.connection.getMetadata(entityClass)
126✔
335
        const plainObjectToDatabaseEntityTransformer =
336
            new PlainObjectToDatabaseEntityTransformer(this.connection.manager)
126✔
337
        const transformedEntity =
338
            await plainObjectToDatabaseEntityTransformer.transform(
126✔
339
                entityLike,
340
                metadata,
341
            )
342
        if (transformedEntity)
126✔
343
            return this.merge(
126✔
344
                entityClass as any,
345
                transformedEntity as Entity,
346
                entityLike,
347
            )
348

349
        return undefined
×
350
    }
351

352
    /**
353
     * Saves all given entities in the database.
354
     * If entities do not exist in the database then inserts, otherwise updates.
355
     */
356
    save<Entity>(entities: Entity[], options?: SaveOptions): Promise<Entity[]>
357

358
    /**
359
     * Saves all given entities in the database.
360
     * If entities do not exist in the database then inserts, otherwise updates.
361
     */
362
    save<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
363

364
    /**
365
     * Saves all given entities in the database.
366
     * If entities do not exist in the database then inserts, otherwise updates.
367
     */
368
    save<Entity, T extends DeepPartial<Entity>>(
369
        targetOrEntity: EntityTarget<Entity>,
370
        entities: T[],
371
        options: SaveOptions & { reload: false },
372
    ): Promise<T[]>
373

374
    /**
375
     * Saves all given entities in the database.
376
     * If entities do not exist in the database then inserts, otherwise updates.
377
     */
378
    save<Entity, T extends DeepPartial<Entity>>(
379
        targetOrEntity: EntityTarget<Entity>,
380
        entities: T[],
381
        options?: SaveOptions,
382
    ): Promise<(T & Entity)[]>
383

384
    /**
385
     * Saves a given entity in the database.
386
     * If entity does not exist in the database then inserts, otherwise updates.
387
     */
388
    save<Entity, T extends DeepPartial<Entity>>(
389
        targetOrEntity: EntityTarget<Entity>,
390
        entity: T,
391
        options: SaveOptions & { reload: false },
392
    ): Promise<T>
393

394
    /**
395
     * Saves a given entity in the database.
396
     * If entity does not exist in the database then inserts, otherwise updates.
397
     */
398
    save<Entity, T extends DeepPartial<Entity>>(
399
        targetOrEntity: EntityTarget<Entity>,
400
        entity: T,
401
        options?: SaveOptions,
402
    ): Promise<T & Entity>
403

404
    /**
405
     * Saves a given entity in the database.
406
     */
407
    save<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
408
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
409
        maybeEntityOrOptions?: T | T[],
410
        maybeOptions?: SaveOptions,
411
    ): Promise<T | T[]> {
412
        // normalize mixed parameters
413
        let target =
414
            arguments.length > 1 &&
251,533✔
415
            (typeof targetOrEntity === "function" ||
416
                InstanceChecker.isEntitySchema(targetOrEntity) ||
417
                typeof targetOrEntity === "string")
418
                ? (targetOrEntity as Function | string)
419
                : undefined
420
        const entity: T | T[] = target
251,533✔
421
            ? (maybeEntityOrOptions as T | T[])
422
            : (targetOrEntity as T | T[])
423
        const options = target
251,533✔
424
            ? maybeOptions
425
            : (maybeEntityOrOptions as SaveOptions)
426

427
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
251,533✔
428

429
        // if user passed empty array of entities then we don't need to do anything
430
        if (Array.isArray(entity) && entity.length === 0)
251,533✔
431
            return Promise.resolve(entity)
43✔
432

433
        // execute save operation
434
        return new EntityPersistExecutor(
251,490✔
435
            this.connection,
436
            this.queryRunner,
437
            "save",
438
            target,
439
            entity,
440
            options,
441
        )
442
            .execute()
443
            .then(() => entity)
250,989✔
444
    }
445

446
    /**
447
     * Removes a given entity from the database.
448
     */
449
    remove<Entity>(entity: Entity, options?: RemoveOptions): Promise<Entity>
450

451
    /**
452
     * Removes a given entity from the database.
453
     */
454
    remove<Entity>(
455
        targetOrEntity: EntityTarget<Entity>,
456
        entity: Entity,
457
        options?: RemoveOptions,
458
    ): Promise<Entity>
459

460
    /**
461
     * Removes a given entity from the database.
462
     */
463
    remove<Entity>(entity: Entity[], options?: RemoveOptions): Promise<Entity>
464

465
    /**
466
     * Removes a given entity from the database.
467
     */
468
    remove<Entity>(
469
        targetOrEntity: EntityTarget<Entity>,
470
        entity: Entity[],
471
        options?: RemoveOptions,
472
    ): Promise<Entity[]>
473

474
    /**
475
     * Removes a given entity from the database.
476
     */
477
    remove<Entity extends ObjectLiteral>(
478
        targetOrEntity: (Entity | Entity[]) | EntityTarget<Entity>,
479
        maybeEntityOrOptions?: Entity | Entity[],
480
        maybeOptions?: RemoveOptions,
481
    ): Promise<Entity | Entity[]> {
482
        // normalize mixed parameters
483
        const target =
484
            arguments.length > 1 &&
2,036✔
485
            (typeof targetOrEntity === "function" ||
486
                InstanceChecker.isEntitySchema(targetOrEntity) ||
487
                typeof targetOrEntity === "string")
488
                ? (targetOrEntity as Function | string)
489
                : undefined
490
        const entity: Entity | Entity[] = target
2,036✔
491
            ? (maybeEntityOrOptions as Entity | Entity[])
492
            : (targetOrEntity as Entity | Entity[])
493
        const options = target
2,036✔
494
            ? maybeOptions
495
            : (maybeEntityOrOptions as SaveOptions)
496

497
        // if user passed empty array of entities then we don't need to do anything
498
        if (Array.isArray(entity) && entity.length === 0)
2,036!
499
            return Promise.resolve(entity)
×
500

501
        // execute save operation
502
        return new EntityPersistExecutor(
2,036✔
503
            this.connection,
504
            this.queryRunner,
505
            "remove",
506
            target,
507
            entity,
508
            options,
509
        )
510
            .execute()
511
            .then(() => entity)
1,796✔
512
    }
513

514
    /**
515
     * Records the delete date of all given entities.
516
     */
517
    softRemove<Entity>(
518
        entities: Entity[],
519
        options?: SaveOptions,
520
    ): Promise<Entity[]>
521

522
    /**
523
     * Records the delete date of a given entity.
524
     */
525
    softRemove<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
526

527
    /**
528
     * Records the delete date of all given entities.
529
     */
530
    softRemove<Entity, T extends DeepPartial<Entity>>(
531
        targetOrEntity: EntityTarget<Entity>,
532
        entities: T[],
533
        options?: SaveOptions,
534
    ): Promise<T[]>
535

536
    /**
537
     * Records the delete date of a given entity.
538
     */
539
    softRemove<Entity, T extends DeepPartial<Entity>>(
540
        targetOrEntity: EntityTarget<Entity>,
541
        entity: T,
542
        options?: SaveOptions,
543
    ): Promise<T>
544

545
    /**
546
     * Records the delete date of one or many given entities.
547
     */
548
    softRemove<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
549
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
550
        maybeEntityOrOptions?: T | T[],
551
        maybeOptions?: SaveOptions,
552
    ): Promise<T | T[]> {
553
        // normalize mixed parameters
554
        let target =
555
            arguments.length > 1 &&
649✔
556
            (typeof targetOrEntity === "function" ||
557
                InstanceChecker.isEntitySchema(targetOrEntity) ||
558
                typeof targetOrEntity === "string")
559
                ? (targetOrEntity as Function | string)
560
                : undefined
561
        const entity: T | T[] = target
649✔
562
            ? (maybeEntityOrOptions as T | T[])
563
            : (targetOrEntity as T | T[])
564
        const options = target
649✔
565
            ? maybeOptions
566
            : (maybeEntityOrOptions as SaveOptions)
567

568
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
649!
569

570
        // if user passed empty array of entities then we don't need to do anything
571
        if (Array.isArray(entity) && entity.length === 0)
649!
572
            return Promise.resolve(entity)
×
573

574
        // execute soft-remove operation
575
        return new EntityPersistExecutor(
649✔
576
            this.connection,
577
            this.queryRunner,
578
            "soft-remove",
579
            target,
580
            entity,
581
            options,
582
        )
583
            .execute()
584
            .then(() => entity)
569✔
585
    }
586

587
    /**
588
     * Recovers all given entities.
589
     */
590
    recover<Entity>(
591
        entities: Entity[],
592
        options?: SaveOptions,
593
    ): Promise<Entity[]>
594

595
    /**
596
     * Recovers a given entity.
597
     */
598
    recover<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
599

600
    /**
601
     * Recovers all given entities.
602
     */
603
    recover<Entity, T extends DeepPartial<Entity>>(
604
        targetOrEntity: EntityTarget<Entity>,
605
        entities: T[],
606
        options?: SaveOptions,
607
    ): Promise<T[]>
608

609
    /**
610
     * Recovers a given entity.
611
     */
612
    recover<Entity, T extends DeepPartial<Entity>>(
613
        targetOrEntity: EntityTarget<Entity>,
614
        entity: T,
615
        options?: SaveOptions,
616
    ): Promise<T>
617

618
    /**
619
     * Recovers one or many given entities.
620
     */
621
    recover<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
622
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
623
        maybeEntityOrOptions?: T | T[],
624
        maybeOptions?: SaveOptions,
625
    ): Promise<T | T[]> {
626
        // normalize mixed parameters
627
        let target =
628
            arguments.length > 1 &&
240✔
629
            (typeof targetOrEntity === "function" ||
630
                InstanceChecker.isEntitySchema(targetOrEntity) ||
631
                typeof targetOrEntity === "string")
632
                ? (targetOrEntity as Function | string)
633
                : undefined
634
        const entity: T | T[] = target
240✔
635
            ? (maybeEntityOrOptions as T | T[])
636
            : (targetOrEntity as T | T[])
637
        const options = target
240✔
638
            ? maybeOptions
639
            : (maybeEntityOrOptions as SaveOptions)
640

641
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
240!
642

643
        // if user passed empty array of entities then we don't need to do anything
644
        if (Array.isArray(entity) && entity.length === 0)
240!
645
            return Promise.resolve(entity)
×
646

647
        // execute recover operation
648
        return new EntityPersistExecutor(
240✔
649
            this.connection,
650
            this.queryRunner,
651
            "recover",
652
            target,
653
            entity,
654
            options,
655
        )
656
            .execute()
657
            .then(() => entity)
160✔
658
    }
659

660
    /**
661
     * Inserts a given entity into the database.
662
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
663
     * Executes fast and efficient INSERT query.
664
     * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted.
665
     * You can execute bulk inserts using this method.
666
     */
667
    async insert<Entity extends ObjectLiteral>(
668
        target: EntityTarget<Entity>,
669
        entity:
670
            | QueryDeepPartialEntity<Entity>
671
            | QueryDeepPartialEntity<Entity>[],
672
    ): Promise<InsertResult> {
673
        return this.createQueryBuilder()
551✔
674
            .insert()
675
            .into(target)
676
            .values(entity)
677
            .execute()
678
    }
679

680
    async upsert<Entity extends ObjectLiteral>(
681
        target: EntityTarget<Entity>,
682
        entityOrEntities:
683
            | QueryDeepPartialEntity<Entity>
684
            | QueryDeepPartialEntity<Entity>[],
685
        conflictPathsOrOptions: string[] | UpsertOptions<Entity>,
686
    ): Promise<InsertResult> {
687
        const metadata = this.connection.getMetadata(target)
586✔
688

689
        let options: UpsertOptions<Entity>
690

691
        if (Array.isArray(conflictPathsOrOptions)) {
586✔
692
            options = {
526✔
693
                conflictPaths: conflictPathsOrOptions,
694
            }
695
        } else {
696
            options = conflictPathsOrOptions
60✔
697
        }
698

699
        let entities: QueryDeepPartialEntity<Entity>[]
700

701
        if (!Array.isArray(entityOrEntities)) {
586✔
702
            entities = [entityOrEntities]
460✔
703
        } else {
704
            entities = entityOrEntities
126✔
705
        }
706

707
        const conflictColumns = metadata.mapPropertyPathsToColumns(
586✔
708
            Array.isArray(options.conflictPaths)
586✔
709
                ? options.conflictPaths
710
                : Object.keys(options.conflictPaths),
711
        )
712

713
        const overwriteColumns = metadata.columns.filter(
586✔
714
            (col) =>
715
                !conflictColumns.includes(col) &&
2,717✔
716
                entities.some(
717
                    (entity) =>
718
                        typeof col.getEntityValue(entity) !== "undefined",
2,719✔
719
                ),
720
        )
721

722
        return this.createQueryBuilder()
586✔
723
            .insert()
724
            .into(target)
725
            .values(entities)
726
            .orUpdate(
727
                [...conflictColumns, ...overwriteColumns].map(
728
                    (col) => col.databaseName,
1,319✔
729
                ),
730
                conflictColumns.map((col) => col.databaseName),
592✔
731
                {
732
                    skipUpdateIfNoValuesChanged:
733
                        options.skipUpdateIfNoValuesChanged,
734
                    indexPredicate: options.indexPredicate,
735
                    upsertType:
736
                        options.upsertType ||
1,172✔
737
                        this.connection.driver.supportedUpsertTypes[0],
738
                },
739
            )
740
            .execute()
741
    }
742

743
    /**
744
     * Updates entity partially. Entity can be found by a given condition(s).
745
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
746
     * Executes fast and efficient UPDATE query.
747
     * Does not check if entity exist in the database.
748
     * Condition(s) cannot be empty.
749
     */
750
    update<Entity extends ObjectLiteral>(
751
        target: EntityTarget<Entity>,
752
        criteria:
753
            | string
754
            | string[]
755
            | number
756
            | number[]
757
            | Date
758
            | Date[]
759
            | ObjectId
760
            | ObjectId[]
761
            | any,
762
        partialEntity: QueryDeepPartialEntity<Entity>,
763
    ): Promise<UpdateResult> {
764
        // if user passed empty criteria or empty list of criterias, then throw an error
765
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
577✔
766
            return Promise.reject(
160✔
767
                new TypeORMError(
768
                    `Empty criteria(s) are not allowed for the update method.`,
769
                ),
770
            )
771
        }
772

773
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
417✔
774
            return this.createQueryBuilder()
166✔
775
                .update(target)
776
                .set(partialEntity)
777
                .whereInIds(criteria)
778
                .execute()
779
        } else {
780
            return this.createQueryBuilder()
251✔
781
                .update(target)
782
                .set(partialEntity)
783
                .where(criteria)
784
                .execute()
785
        }
786
    }
787

788
    /**
789
     * Deletes entities by a given condition(s).
790
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
791
     * Executes fast and efficient DELETE query.
792
     * Does not check if entity exist in the database.
793
     * Condition(s) cannot be empty.
794
     */
795
    delete<Entity extends ObjectLiteral>(
796
        targetOrEntity: EntityTarget<Entity>,
797
        criteria:
798
            | string
799
            | string[]
800
            | number
801
            | number[]
802
            | Date
803
            | Date[]
804
            | ObjectId
805
            | ObjectId[]
806
            | any,
807
    ): Promise<DeleteResult> {
808
        // if user passed empty criteria or empty list of criterias, then throw an error
809
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
507✔
810
            return Promise.reject(
160✔
811
                new TypeORMError(
812
                    `Empty criteria(s) are not allowed for the delete method.`,
813
                ),
814
            )
815
        }
816

817
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
347✔
818
            return this.createQueryBuilder()
227✔
819
                .delete()
820
                .from(targetOrEntity)
821
                .whereInIds(criteria)
822
                .execute()
823
        } else {
824
            return this.createQueryBuilder()
120✔
825
                .delete()
826
                .from(targetOrEntity)
827
                .where(criteria)
828
                .execute()
829
        }
830
    }
831

832
    /**
833
     * Records the delete date of entities by a given condition(s).
834
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
835
     * Executes fast and efficient DELETE query.
836
     * Does not check if entity exist in the database.
837
     * Condition(s) cannot be empty.
838
     */
839
    softDelete<Entity extends ObjectLiteral>(
840
        targetOrEntity: EntityTarget<Entity>,
841
        criteria:
842
            | string
843
            | string[]
844
            | number
845
            | number[]
846
            | Date
847
            | Date[]
848
            | ObjectId
849
            | ObjectId[]
850
            | any,
851
    ): Promise<UpdateResult> {
852
        // if user passed empty criteria or empty list of criterias, then throw an error
853
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
160!
UNCOV
854
            return Promise.reject(
×
855
                new TypeORMError(
856
                    `Empty criteria(s) are not allowed for the delete method.`,
857
                ),
858
            )
859
        }
860

861
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
160!
UNCOV
862
            return this.createQueryBuilder()
×
863
                .softDelete()
864
                .from(targetOrEntity)
865
                .whereInIds(criteria)
866
                .execute()
867
        } else {
868
            return this.createQueryBuilder()
160✔
869
                .softDelete()
870
                .from(targetOrEntity)
871
                .where(criteria)
872
                .execute()
873
        }
874
    }
875

876
    /**
877
     * Restores entities by a given condition(s).
878
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
879
     * Executes fast and efficient DELETE query.
880
     * Does not check if entity exist in the database.
881
     * Condition(s) cannot be empty.
882
     */
883
    restore<Entity extends ObjectLiteral>(
884
        targetOrEntity: EntityTarget<Entity>,
885
        criteria:
886
            | string
887
            | string[]
888
            | number
889
            | number[]
890
            | Date
891
            | Date[]
892
            | ObjectId
893
            | ObjectId[]
894
            | any,
895
    ): Promise<UpdateResult> {
896
        // if user passed empty criteria or empty list of criterias, then throw an error
897
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
80!
UNCOV
898
            return Promise.reject(
×
899
                new TypeORMError(
900
                    `Empty criteria(s) are not allowed for the delete method.`,
901
                ),
902
            )
903
        }
904

905
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
80!
UNCOV
906
            return this.createQueryBuilder()
×
907
                .restore()
908
                .from(targetOrEntity)
909
                .whereInIds(criteria)
910
                .execute()
911
        } else {
912
            return this.createQueryBuilder()
80✔
913
                .restore()
914
                .from(targetOrEntity)
915
                .where(criteria)
916
                .execute()
917
        }
918
    }
919

920
    /**
921
     * Checks whether any entity exists with the given options.
922
     */
923
    exists<Entity extends ObjectLiteral>(
924
        entityClass: EntityTarget<Entity>,
925
        options?: FindManyOptions<Entity>,
926
    ): Promise<boolean> {
927
        const metadata = this.connection.getMetadata(entityClass)
280✔
928
        return this.createQueryBuilder(
280✔
929
            entityClass,
930
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
560✔
931
                metadata.name,
932
        )
933
            .setFindOptions(options || {})
400✔
934
            .getExists()
935
    }
936

937
    /**
938
     * Checks whether any entity exists with the given conditions.
939
     */
940
    async existsBy<Entity extends ObjectLiteral>(
941
        entityClass: EntityTarget<Entity>,
942
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
943
    ): Promise<boolean> {
944
        const metadata = this.connection.getMetadata(entityClass)
×
945
        return this.createQueryBuilder(entityClass, metadata.name)
×
946
            .setFindOptions({ where })
947
            .getExists()
948
    }
949

950
    /**
951
     * Counts entities that match given options.
952
     * Useful for pagination.
953
     */
954
    count<Entity extends ObjectLiteral>(
955
        entityClass: EntityTarget<Entity>,
956
        options?: FindManyOptions<Entity>,
957
    ): Promise<number> {
958
        const metadata = this.connection.getMetadata(entityClass)
603✔
959
        return this.createQueryBuilder(
603✔
960
            entityClass,
961
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
1,206✔
962
                metadata.name,
963
        )
964
            .setFindOptions(options || {})
929✔
965
            .getCount()
966
    }
967

968
    /**
969
     * Counts entities that match given conditions.
970
     * Useful for pagination.
971
     */
972
    countBy<Entity extends ObjectLiteral>(
973
        entityClass: EntityTarget<Entity>,
974
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
975
    ): Promise<number> {
976
        const metadata = this.connection.getMetadata(entityClass)
80✔
977
        return this.createQueryBuilder(entityClass, metadata.name)
80✔
978
            .setFindOptions({ where })
979
            .getCount()
980
    }
981

982
    /**
983
     * Return the SUM of a column
984
     */
985
    sum<Entity extends ObjectLiteral>(
986
        entityClass: EntityTarget<Entity>,
987
        columnName: PickKeysByType<Entity, number>,
988
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
989
    ): Promise<number | null> {
990
        return this.callAggregateFun(entityClass, "SUM", columnName, where)
83✔
991
    }
992

993
    /**
994
     * Return the AVG of a column
995
     */
996
    average<Entity extends ObjectLiteral>(
997
        entityClass: EntityTarget<Entity>,
998
        columnName: PickKeysByType<Entity, number>,
999
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1000
    ): Promise<number | null> {
1001
        return this.callAggregateFun(entityClass, "AVG", columnName, where)
83✔
1002
    }
1003

1004
    /**
1005
     * Return the MIN of a column
1006
     */
1007
    minimum<Entity extends ObjectLiteral>(
1008
        entityClass: EntityTarget<Entity>,
1009
        columnName: PickKeysByType<Entity, number>,
1010
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1011
    ): Promise<number | null> {
1012
        return this.callAggregateFun(entityClass, "MIN", columnName, where)
83✔
1013
    }
1014

1015
    /**
1016
     * Return the MAX of a column
1017
     */
1018
    maximum<Entity extends ObjectLiteral>(
1019
        entityClass: EntityTarget<Entity>,
1020
        columnName: PickKeysByType<Entity, number>,
1021
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1022
    ): Promise<number | null> {
1023
        return this.callAggregateFun(entityClass, "MAX", columnName, where)
83✔
1024
    }
1025

1026
    private async callAggregateFun<Entity extends ObjectLiteral>(
1027
        entityClass: EntityTarget<Entity>,
1028
        fnName: "SUM" | "AVG" | "MIN" | "MAX",
1029
        columnName: PickKeysByType<Entity, number>,
1030
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[] = {},
160✔
1031
    ): Promise<number | null> {
1032
        const metadata = this.connection.getMetadata(entityClass)
332✔
1033
        const column = metadata.columns.find(
332✔
1034
            (item) => item.propertyPath === columnName,
664✔
1035
        )
1036
        if (!column) {
332!
1037
            throw new TypeORMError(
×
1038
                `Column "${columnName}" was not found in table "${metadata.name}"`,
1039
            )
1040
        }
1041

1042
        const result = await this.createQueryBuilder(entityClass, metadata.name)
332✔
1043
            .setFindOptions({ where })
1044
            .select(
1045
                `${fnName}(${this.connection.driver.escape(
1046
                    column.databaseName,
1047
                )})`,
1048
                fnName,
1049
            )
1050
            .getRawOne()
1051
        return result[fnName] === null ? null : parseFloat(result[fnName])
332✔
1052
    }
1053

1054
    /**
1055
     * Finds entities that match given find options.
1056
     */
1057
    async find<Entity extends ObjectLiteral>(
1058
        entityClass: EntityTarget<Entity>,
1059
        options?: FindManyOptions<Entity>,
1060
    ): Promise<Entity[]> {
1061
        const metadata = this.connection.getMetadata(entityClass)
6,226✔
1062
        return this.createQueryBuilder<Entity>(
6,226✔
1063
            entityClass as any,
1064
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
12,252✔
1065
                metadata.name,
1066
        )
1067
            .setFindOptions(options || {})
8,570✔
1068
            .getMany()
1069
    }
1070

1071
    /**
1072
     * Finds entities that match given find options.
1073
     */
1074
    async findBy<Entity extends ObjectLiteral>(
1075
        entityClass: EntityTarget<Entity>,
1076
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1077
    ): Promise<Entity[]> {
1078
        const metadata = this.connection.getMetadata(entityClass)
1,729✔
1079
        return this.createQueryBuilder<Entity>(
1,729✔
1080
            entityClass as any,
1081
            metadata.name,
1082
        )
1083
            .setFindOptions({ where: where })
1084
            .getMany()
1085
    }
1086

1087
    /**
1088
     * Finds entities that match given find options.
1089
     * Also counts all entities that match given conditions,
1090
     * but ignores pagination settings (from and take options).
1091
     */
1092
    findAndCount<Entity extends ObjectLiteral>(
1093
        entityClass: EntityTarget<Entity>,
1094
        options?: FindManyOptions<Entity>,
1095
    ): Promise<[Entity[], number]> {
1096
        const metadata = this.connection.getMetadata(entityClass)
486✔
1097
        return this.createQueryBuilder<Entity>(
486✔
1098
            entityClass as any,
1099
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
972✔
1100
                metadata.name,
1101
        )
1102
            .setFindOptions(options || {})
566✔
1103
            .getManyAndCount()
1104
    }
1105

1106
    /**
1107
     * Finds entities that match given WHERE conditions.
1108
     * Also counts all entities that match given conditions,
1109
     * but ignores pagination settings (from and take options).
1110
     */
1111
    findAndCountBy<Entity extends ObjectLiteral>(
1112
        entityClass: EntityTarget<Entity>,
1113
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1114
    ): Promise<[Entity[], number]> {
1115
        const metadata = this.connection.getMetadata(entityClass)
×
1116
        return this.createQueryBuilder<Entity>(
×
1117
            entityClass as any,
1118
            metadata.name,
1119
        )
1120
            .setFindOptions({ where })
1121
            .getManyAndCount()
1122
    }
1123

1124
    /**
1125
     * Finds entities with ids.
1126
     * Optionally find options or conditions can be applied.
1127
     *
1128
     * @deprecated use `findBy` method instead in conjunction with `In` operator, for example:
1129
     *
1130
     * .findBy({
1131
     *     id: In([1, 2, 3])
1132
     * })
1133
     */
1134
    async findByIds<Entity extends ObjectLiteral>(
1135
        entityClass: EntityTarget<Entity>,
1136
        ids: any[],
1137
    ): Promise<Entity[]> {
1138
        // if no ids passed, no need to execute a query - just return an empty array of values
1139
        if (!ids.length) return Promise.resolve([])
326✔
1140

1141
        const metadata = this.connection.getMetadata(entityClass)
286✔
1142
        return this.createQueryBuilder<Entity>(
286✔
1143
            entityClass as any,
1144
            metadata.name,
1145
        )
1146
            .andWhereInIds(ids)
1147
            .getMany()
1148
    }
1149

1150
    /**
1151
     * Finds first entity by a given find options.
1152
     * If entity was not found in the database - returns null.
1153
     */
1154
    async findOne<Entity extends ObjectLiteral>(
1155
        entityClass: EntityTarget<Entity>,
1156
        options: FindOneOptions<Entity>,
1157
    ): Promise<Entity | null> {
1158
        const metadata = this.connection.getMetadata(entityClass)
13,991✔
1159

1160
        // prepare alias for built query
1161
        let alias: string = metadata.name
13,991✔
1162
        if (options && options.join) {
13,991✔
1163
            alias = options.join.alias
729✔
1164
        }
1165

1166
        if (!options.where) {
13,991✔
1167
            throw new Error(
40✔
1168
                `You must provide selection conditions in order to find a single row.`,
1169
            )
1170
        }
1171

1172
        // create query builder and apply find options
1173
        return this.createQueryBuilder<Entity>(entityClass, alias)
13,951✔
1174
            .setFindOptions({
1175
                ...options,
1176
                take: 1,
1177
            })
1178
            .getOne()
1179
    }
1180

1181
    /**
1182
     * Finds first entity that matches given where condition.
1183
     * If entity was not found in the database - returns null.
1184
     */
1185
    async findOneBy<Entity extends ObjectLiteral>(
1186
        entityClass: EntityTarget<Entity>,
1187
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1188
    ): Promise<Entity | null> {
1189
        const metadata = this.connection.getMetadata(entityClass)
8,534✔
1190

1191
        // create query builder and apply find options
1192
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
8,534✔
1193
            .setFindOptions({
1194
                where,
1195
                take: 1,
1196
            })
1197
            .getOne()
1198
    }
1199

1200
    /**
1201
     * Finds first entity that matches given id.
1202
     * If entity was not found in the database - returns null.
1203
     *
1204
     * @deprecated use `findOneBy` method instead in conjunction with `In` operator, for example:
1205
     *
1206
     * .findOneBy({
1207
     *     id: 1 // where "id" is your primary column name
1208
     * })
1209
     */
1210
    async findOneById<Entity extends ObjectLiteral>(
1211
        entityClass: EntityTarget<Entity>,
1212
        id: number | string | Date | ObjectId,
1213
    ): Promise<Entity | null> {
1214
        const metadata = this.connection.getMetadata(entityClass)
163✔
1215

1216
        // create query builder and apply find options
1217
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
163✔
1218
            .setFindOptions({
1219
                take: 1,
1220
            })
1221
            .whereInIds(metadata.ensureEntityIdMap(id))
1222
            .getOne()
1223
    }
1224

1225
    /**
1226
     * Finds first entity by a given find options.
1227
     * If entity was not found in the database - rejects with error.
1228
     */
1229
    async findOneOrFail<Entity extends ObjectLiteral>(
1230
        entityClass: EntityTarget<Entity>,
1231
        options: FindOneOptions<Entity>,
1232
    ): Promise<Entity> {
1233
        return this.findOne<Entity>(entityClass as any, options).then(
686✔
1234
            (value) => {
1235
                if (value === null) {
686✔
1236
                    return Promise.reject(
80✔
1237
                        new EntityNotFoundError(entityClass, options),
1238
                    )
1239
                }
1240
                return Promise.resolve(value)
606✔
1241
            },
1242
        )
1243
    }
1244

1245
    /**
1246
     * Finds first entity that matches given where condition.
1247
     * If entity was not found in the database - rejects with error.
1248
     */
1249
    async findOneByOrFail<Entity extends ObjectLiteral>(
1250
        entityClass: EntityTarget<Entity>,
1251
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1252
    ): Promise<Entity> {
1253
        return this.findOneBy<Entity>(entityClass as any, where).then(
1,408✔
1254
            (value) => {
1255
                if (value === null) {
1,408✔
1256
                    return Promise.reject(
40✔
1257
                        new EntityNotFoundError(entityClass, where),
1258
                    )
1259
                }
1260
                return Promise.resolve(value)
1,368✔
1261
            },
1262
        )
1263
    }
1264

1265
    /**
1266
     * Clears all the data from the given table (truncates/drops it).
1267
     *
1268
     * Note: this method uses TRUNCATE and may not work as you expect in transactions on some platforms.
1269
     * @see https://stackoverflow.com/a/5972738/925151
1270
     */
1271
    async clear<Entity>(entityClass: EntityTarget<Entity>): Promise<void> {
1272
        const metadata = this.connection.getMetadata(entityClass)
86✔
1273
        const queryRunner =
1274
            this.queryRunner || this.connection.createQueryRunner()
86✔
1275
        try {
86✔
1276
            return await queryRunner.clearTable(metadata.tablePath) // await is needed here because we are using finally
86✔
1277
        } finally {
1278
            if (!this.queryRunner) await queryRunner.release()
86✔
1279
        }
1280
    }
1281

1282
    /**
1283
     * Increments some column by provided value of the entities matched given conditions.
1284
     */
1285
    async increment<Entity extends ObjectLiteral>(
1286
        entityClass: EntityTarget<Entity>,
1287
        conditions: any,
1288
        propertyPath: string,
1289
        value: number | string,
1290
    ): Promise<UpdateResult> {
1291
        const metadata = this.connection.getMetadata(entityClass)
348✔
1292
        const column = metadata.findColumnWithPropertyPath(propertyPath)
348✔
1293
        if (!column)
348✔
1294
            throw new TypeORMError(
40✔
1295
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1296
            )
1297

1298
        if (isNaN(Number(value)))
308✔
1299
            throw new TypeORMError(`Value "${value}" is not a number.`)
40✔
1300

1301
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
1302
        const values: QueryDeepPartialEntity<Entity> = propertyPath
268✔
1303
            .split(".")
1304
            .reduceRight(
1305
                (value, key) => ({ [key]: value } as any),
308✔
1306
                () =>
1307
                    this.connection.driver.escape(column.databaseName) +
268✔
1308
                    " + " +
1309
                    value,
1310
            )
1311

1312
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
268✔
1313
            .update(entityClass)
1314
            .set(values)
1315
            .where(conditions)
1316
            .execute()
1317
    }
1318

1319
    /**
1320
     * Decrements some column by provided value of the entities matched given conditions.
1321
     */
1322
    async decrement<Entity extends ObjectLiteral>(
1323
        entityClass: EntityTarget<Entity>,
1324
        conditions: any,
1325
        propertyPath: string,
1326
        value: number | string,
1327
    ): Promise<UpdateResult> {
1328
        const metadata = this.connection.getMetadata(entityClass)
344✔
1329
        const column = metadata.findColumnWithPropertyPath(propertyPath)
344✔
1330
        if (!column)
344✔
1331
            throw new TypeORMError(
40✔
1332
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1333
            )
1334

1335
        if (isNaN(Number(value)))
304✔
1336
            throw new TypeORMError(`Value "${value}" is not a number.`)
40✔
1337

1338
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
1339
        const values: QueryDeepPartialEntity<Entity> = propertyPath
264✔
1340
            .split(".")
1341
            .reduceRight(
1342
                (value, key) => ({ [key]: value } as any),
304✔
1343
                () =>
1344
                    this.connection.driver.escape(column.databaseName) +
264✔
1345
                    " - " +
1346
                    value,
1347
            )
1348

1349
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
264✔
1350
            .update(entityClass)
1351
            .set(values)
1352
            .where(conditions)
1353
            .execute()
1354
    }
1355

1356
    /**
1357
     * Gets repository for the given entity class or name.
1358
     * If single database connection mode is used, then repository is obtained from the
1359
     * repository aggregator, where each repository is individually created for this entity manager.
1360
     * When single database connection is not used, repository is being obtained from the connection.
1361
     */
1362
    getRepository<Entity extends ObjectLiteral>(
1363
        target: EntityTarget<Entity>,
1364
    ): Repository<Entity> {
1365
        // find already created repository instance and return it if found
1366
        const repoFromMap = this.repositories.get(target)
606,100✔
1367
        if (repoFromMap) return repoFromMap
606,100✔
1368

1369
        // if repository was not found then create it, store its instance and return it
1370
        if (this.connection.driver.options.type === "mongodb") {
180,248✔
1371
            const newRepository = new MongoRepository(
141✔
1372
                target,
1373
                this,
1374
                this.queryRunner,
1375
            )
1376
            this.repositories.set(target, newRepository)
141✔
1377
            return newRepository
141✔
1378
        } else {
1379
            const newRepository = new Repository<any>(
180,107✔
1380
                target,
1381
                this,
1382
                this.queryRunner,
1383
            )
1384
            this.repositories.set(target, newRepository)
180,107✔
1385
            return newRepository
180,107✔
1386
        }
1387
    }
1388

1389
    /**
1390
     * Gets tree repository for the given entity class or name.
1391
     * If single database connection mode is used, then repository is obtained from the
1392
     * repository aggregator, where each repository is individually created for this entity manager.
1393
     * When single database connection is not used, repository is being obtained from the connection.
1394
     */
1395
    getTreeRepository<Entity extends ObjectLiteral>(
1396
        target: EntityTarget<Entity>,
1397
    ): TreeRepository<Entity> {
1398
        // tree tables aren't supported by some drivers (mongodb)
1399
        if (this.connection.driver.treeSupport === false)
4,317!
1400
            throw new TreeRepositoryNotSupportedError(this.connection.driver)
×
1401

1402
        // find already created repository instance and return it if found
1403
        const repository = this.treeRepositories.find(
4,317✔
1404
            (repository) => repository.target === target,
5,048✔
1405
        )
1406
        if (repository) return repository
4,317✔
1407

1408
        // check if repository is real tree repository
1409
        const newRepository = new TreeRepository(target, this, this.queryRunner)
983✔
1410
        this.treeRepositories.push(newRepository)
983✔
1411
        return newRepository
983✔
1412
    }
1413

1414
    /**
1415
     * Gets mongodb repository for the given entity class.
1416
     */
1417
    getMongoRepository<Entity extends ObjectLiteral>(
1418
        target: EntityTarget<Entity>,
1419
    ): MongoRepository<Entity> {
1420
        return this.connection.getMongoRepository<Entity>(target)
9✔
1421
    }
1422

1423
    /**
1424
     * Creates a new repository instance out of a given Repository and
1425
     * sets current EntityManager instance to it. Used to work with custom repositories
1426
     * in transactions.
1427
     */
1428
    withRepository<Entity extends ObjectLiteral, R extends Repository<any>>(
1429
        repository: R & Repository<Entity>,
1430
    ): R {
1431
        const repositoryConstructor =
1432
            repository.constructor as typeof Repository
52✔
1433
        const { target, manager, queryRunner, ...otherRepositoryProperties } =
1434
            repository
52✔
1435
        return Object.assign(
52✔
1436
            new repositoryConstructor(repository.target, this) as R,
1437
            {
1438
                ...otherRepositoryProperties,
1439
            },
1440
        )
1441
    }
1442

1443
    /**
1444
     * Gets custom entity repository marked with @EntityRepository decorator.
1445
     *
1446
     * @deprecated use Repository.extend to create custom repositories
1447
     */
1448
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
1449
        const entityRepositoryMetadataArgs =
1450
            getMetadataArgsStorage().entityRepositories.find((repository) => {
52✔
1451
                return (
70✔
1452
                    repository.target ===
1453
                    (typeof customRepository === "function"
70!
1454
                        ? customRepository
1455
                        : (customRepository as any).constructor)
1456
                )
1457
            })
1458
        if (!entityRepositoryMetadataArgs)
52!
1459
            throw new CustomRepositoryNotFoundError(customRepository)
×
1460

1461
        const entityMetadata = entityRepositoryMetadataArgs.entity
52✔
1462
            ? this.connection.getMetadata(entityRepositoryMetadataArgs.entity)
1463
            : undefined
1464
        const entityRepositoryInstance =
1465
            new (entityRepositoryMetadataArgs.target as any)(
52✔
1466
                this,
1467
                entityMetadata,
1468
            )
1469

1470
        // NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
1471
        // however we need these properties for internal work of the class
1472
        if (entityRepositoryInstance instanceof AbstractRepository) {
52!
1473
            if (!(entityRepositoryInstance as any)["manager"])
52✔
1474
                (entityRepositoryInstance as any)["manager"] = this
52✔
1475
        } else {
1476
            if (!entityMetadata)
×
1477
                throw new CustomRepositoryCannotInheritRepositoryError(
×
1478
                    customRepository,
1479
                )
1480
            ;(entityRepositoryInstance as any)["manager"] = this
×
1481
            ;(entityRepositoryInstance as any)["metadata"] = entityMetadata
×
1482
        }
1483

1484
        return entityRepositoryInstance
52✔
1485
    }
1486

1487
    /**
1488
     * Releases all resources used by entity manager.
1489
     * This is used when entity manager is created with a single query runner,
1490
     * and this single query runner needs to be released after job with entity manager is done.
1491
     */
1492
    async release(): Promise<void> {
1493
        if (!this.queryRunner) throw new NoNeedToReleaseEntityManagerError()
×
1494

1495
        return this.queryRunner.release()
×
1496
    }
1497
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc