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

typeorm / typeorm / 14796576772

02 May 2025 01:52PM UTC coverage: 45.367% (-30.9%) from 76.309%
14796576772

Pull #11434

github

web-flow
Merge ec4ce2d00 into fadad1a74
Pull Request #11434: feat: release PR releases using pkg.pr.new

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

87.27
/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"
4✔
6
import { QueryRunnerProviderAlreadyReleasedError } from "../error/QueryRunnerProviderAlreadyReleasedError"
4✔
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"
4✔
12
import { MongoRepository } from "../repository/MongoRepository"
4✔
13
import { TreeRepository } from "../repository/TreeRepository"
4✔
14
import { Repository } from "../repository/Repository"
4✔
15
import { FindOptionsUtils } from "../find-options/FindOptionsUtils"
4✔
16
import { PlainObjectToNewEntityTransformer } from "../query-builder/transformer/PlainObjectToNewEntityTransformer"
4✔
17
import { PlainObjectToDatabaseEntityTransformer } from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer"
4✔
18
import {
4✔
19
    CustomRepositoryCannotInheritRepositoryError,
20
    CustomRepositoryNotFoundError,
21
    TreeRepositoryNotSupportedError,
22
    TypeORMError,
23
} from "../error"
24
import { AbstractRepository } from "../repository/AbstractRepository"
4✔
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"
4✔
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"
4✔
36
import { getMetadataArgsStorage } from "../globals"
4✔
37
import { UpsertOptions } from "../repository/UpsertOptions"
38
import { InstanceChecker } from "../util/InstanceChecker"
4✔
39
import { ObjectLiteral } from "../common/ObjectLiteral"
40
import { PickKeysByType } from "../common/PickKeysByType"
41

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

49
    // -------------------------------------------------------------------------
50
    // Public Properties
51
    // -------------------------------------------------------------------------
52

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

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

64
    // -------------------------------------------------------------------------
65
    // Protected Properties
66
    // -------------------------------------------------------------------------
67

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

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

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

85
    // -------------------------------------------------------------------------
86
    // Constructor
87
    // -------------------------------------------------------------------------
88

89
    constructor(connection: DataSource, queryRunner?: QueryRunner) {
90
        this.connection = connection
47,104✔
91
        if (queryRunner) {
47,104✔
92
            this.queryRunner = queryRunner
45,232✔
93
            // dynamic: this.queryRunner = manager;
94
            ObjectUtils.assign(this.queryRunner, { manager: this })
45,232✔
95
        }
96
    }
97

98
    // -------------------------------------------------------------------------
99
    // Public Methods
100
    // -------------------------------------------------------------------------
101

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

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

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

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

144
        if (this.queryRunner && this.queryRunner.isReleased)
125!
145
            throw new QueryRunnerProviderAlreadyReleasedError()
×
146

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

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

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

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

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

193
    /**
194
     * Creates a new query builder that can be used to build a SQL query.
195
     */
196
    createQueryBuilder<Entity extends ObjectLiteral>(
197
        entityClass?: EntityTarget<Entity> | QueryRunner,
198
        alias?: string,
199
        queryRunner?: QueryRunner,
200
    ): SelectQueryBuilder<Entity> {
201
        if (alias) {
102,440✔
202
            return this.connection.createQueryBuilder(
62,503✔
203
                entityClass as EntityTarget<Entity>,
204
                alias,
205
                queryRunner || this.queryRunner,
108,521✔
206
            )
207
        } else {
208
            return this.connection.createQueryBuilder(
39,937✔
209
                (entityClass as QueryRunner | undefined) ||
119,721✔
210
                    queryRunner ||
211
                    this.queryRunner,
212
            )
213
        }
214
    }
215

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

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

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

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

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

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

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

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

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

286
        if (!plainObjectOrObjects) return metadata.create(this.queryRunner)
500✔
287

288
        if (Array.isArray(plainObjectOrObjects))
316✔
289
            return (plainObjectOrObjects as EntityLike[]).map(
16✔
290
                (plainEntityLike) => this.create(entityClass, plainEntityLike),
40✔
291
            )
292

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

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

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

348
        return undefined
×
349
    }
350

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

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

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

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

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

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

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

426
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
25,497✔
427

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

432
        // execute save operation
433
        return new EntityPersistExecutor(
25,493✔
434
            this.connection,
435
            this.queryRunner,
436
            "save",
437
            target,
438
            entity,
439
            options,
440
        )
441
            .execute()
442
            .then(() => entity)
25,451✔
443
    }
444

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

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

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

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

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

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

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

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

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

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

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

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

567
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
64!
568

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

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

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

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

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

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

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

640
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
24!
641

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

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

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

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

688
        let options: UpsertOptions<Entity>
689

690
        if (Array.isArray(conflictPathsOrOptions)) {
61✔
691
            options = {
58✔
692
                conflictPaths: conflictPathsOrOptions,
693
            }
694
        } else {
695
            options = conflictPathsOrOptions
3✔
696
        }
697

698
        let entities: QueryDeepPartialEntity<Entity>[]
699

700
        if (!Array.isArray(entityOrEntities)) {
61✔
701
            entities = [entityOrEntities]
49✔
702
        } else {
703
            entities = entityOrEntities
12✔
704
        }
705

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

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

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

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

777
        if (
32✔
778
            typeof criteria === "string" ||
96✔
779
            typeof criteria === "number" ||
780
            criteria instanceof Date ||
781
            Array.isArray(criteria)
782
        ) {
783
            return this.createQueryBuilder()
16✔
784
                .update(target)
785
                .set(partialEntity)
786
                .whereInIds(criteria)
787
                .execute()
788
        } else {
789
            return this.createQueryBuilder()
16✔
790
                .update(target)
791
                .set(partialEntity)
792
                .where(criteria)
793
                .execute()
794
        }
795
    }
796

797
    /**
798
     * Deletes entities by a given condition(s).
799
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
800
     * Executes fast and efficient DELETE query.
801
     * Does not check if entity exist in the database.
802
     * Condition(s) cannot be empty.
803
     */
804
    delete<Entity extends ObjectLiteral>(
805
        targetOrEntity: EntityTarget<Entity>,
806
        criteria:
807
            | string
808
            | string[]
809
            | number
810
            | number[]
811
            | Date
812
            | Date[]
813
            | ObjectId
814
            | ObjectId[]
815
            | any,
816
    ): Promise<DeleteResult> {
817
        // if user passed empty criteria or empty list of criterias, then throw an error
818
        if (
44✔
819
            criteria === undefined ||
160✔
820
            criteria === null ||
821
            criteria === "" ||
822
            (Array.isArray(criteria) && criteria.length === 0)
823
        ) {
824
            return Promise.reject(
16✔
825
                new TypeORMError(
826
                    `Empty criteria(s) are not allowed for the delete method.`,
827
                ),
828
            )
829
        }
830

831
        if (
28✔
832
            typeof criteria === "string" ||
80✔
833
            typeof criteria === "number" ||
834
            criteria instanceof Date ||
835
            Array.isArray(criteria)
836
        ) {
837
            return this.createQueryBuilder()
20✔
838
                .delete()
839
                .from(targetOrEntity)
840
                .whereInIds(criteria)
841
                .execute()
842
        } else {
843
            return this.createQueryBuilder()
8✔
844
                .delete()
845
                .from(targetOrEntity)
846
                .where(criteria)
847
                .execute()
848
        }
849
    }
850

851
    /**
852
     * Records the delete date of entities by a given condition(s).
853
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
854
     * Executes fast and efficient DELETE query.
855
     * Does not check if entity exist in the database.
856
     * Condition(s) cannot be empty.
857
     */
858
    softDelete<Entity extends ObjectLiteral>(
859
        targetOrEntity: EntityTarget<Entity>,
860
        criteria:
861
            | string
862
            | string[]
863
            | number
864
            | number[]
865
            | Date
866
            | Date[]
867
            | ObjectId
868
            | ObjectId[]
869
            | any,
870
    ): Promise<UpdateResult> {
871
        // if user passed empty criteria or empty list of criterias, then throw an error
872
        if (
8!
873
            criteria === undefined ||
32!
874
            criteria === null ||
875
            criteria === "" ||
876
            (Array.isArray(criteria) && criteria.length === 0)
877
        ) {
878
            return Promise.reject(
×
879
                new TypeORMError(
880
                    `Empty criteria(s) are not allowed for the delete method.`,
881
                ),
882
            )
883
        }
884

885
        if (
8!
886
            typeof criteria === "string" ||
32✔
887
            typeof criteria === "number" ||
888
            criteria instanceof Date ||
889
            Array.isArray(criteria)
890
        ) {
891
            return this.createQueryBuilder()
×
892
                .softDelete()
893
                .from(targetOrEntity)
894
                .whereInIds(criteria)
895
                .execute()
896
        } else {
897
            return this.createQueryBuilder()
8✔
898
                .softDelete()
899
                .from(targetOrEntity)
900
                .where(criteria)
901
                .execute()
902
        }
903
    }
904

905
    /**
906
     * Restores entities by a given condition(s).
907
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
908
     * Executes fast and efficient DELETE query.
909
     * Does not check if entity exist in the database.
910
     * Condition(s) cannot be empty.
911
     */
912
    restore<Entity extends ObjectLiteral>(
913
        targetOrEntity: EntityTarget<Entity>,
914
        criteria:
915
            | string
916
            | string[]
917
            | number
918
            | number[]
919
            | Date
920
            | Date[]
921
            | ObjectId
922
            | ObjectId[]
923
            | any,
924
    ): Promise<UpdateResult> {
925
        // if user passed empty criteria or empty list of criterias, then throw an error
926
        if (
4!
927
            criteria === undefined ||
16!
928
            criteria === null ||
929
            criteria === "" ||
930
            (Array.isArray(criteria) && criteria.length === 0)
931
        ) {
932
            return Promise.reject(
×
933
                new TypeORMError(
934
                    `Empty criteria(s) are not allowed for the delete method.`,
935
                ),
936
            )
937
        }
938

939
        if (
4!
940
            typeof criteria === "string" ||
16✔
941
            typeof criteria === "number" ||
942
            criteria instanceof Date ||
943
            Array.isArray(criteria)
944
        ) {
945
            return this.createQueryBuilder()
×
946
                .restore()
947
                .from(targetOrEntity)
948
                .whereInIds(criteria)
949
                .execute()
950
        } else {
951
            return this.createQueryBuilder()
4✔
952
                .restore()
953
                .from(targetOrEntity)
954
                .where(criteria)
955
                .execute()
956
        }
957
    }
958

959
    /**
960
     * Checks whether any entity exists with the given options.
961
     */
962
    exists<Entity extends ObjectLiteral>(
963
        entityClass: EntityTarget<Entity>,
964
        options?: FindManyOptions<Entity>,
965
    ): Promise<boolean> {
966
        const metadata = this.connection.getMetadata(entityClass)
28✔
967
        return this.createQueryBuilder(
28✔
968
            entityClass,
969
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
56✔
970
                metadata.name,
971
        )
972
            .setFindOptions(options || {})
40✔
973
            .getExists()
974
    }
975

976
    /**
977
     * Checks whether any entity exists with the given conditions.
978
     */
979
    async existsBy<Entity extends ObjectLiteral>(
980
        entityClass: EntityTarget<Entity>,
981
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
982
    ): Promise<boolean> {
983
        const metadata = this.connection.getMetadata(entityClass)
×
984
        return this.createQueryBuilder(entityClass, metadata.name)
×
985
            .setFindOptions({ where })
986
            .getExists()
987
    }
988

989
    /**
990
     * Counts entities that match given options.
991
     * Useful for pagination.
992
     */
993
    count<Entity extends ObjectLiteral>(
994
        entityClass: EntityTarget<Entity>,
995
        options?: FindManyOptions<Entity>,
996
    ): Promise<number> {
997
        const metadata = this.connection.getMetadata(entityClass)
60✔
998
        return this.createQueryBuilder(
60✔
999
            entityClass,
1000
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
120✔
1001
                metadata.name,
1002
        )
1003
            .setFindOptions(options || {})
92✔
1004
            .getCount()
1005
    }
1006

1007
    /**
1008
     * Counts entities that match given conditions.
1009
     * Useful for pagination.
1010
     */
1011
    countBy<Entity extends ObjectLiteral>(
1012
        entityClass: EntityTarget<Entity>,
1013
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1014
    ): Promise<number> {
1015
        const metadata = this.connection.getMetadata(entityClass)
8✔
1016
        return this.createQueryBuilder(entityClass, metadata.name)
8✔
1017
            .setFindOptions({ where })
1018
            .getCount()
1019
    }
1020

1021
    /**
1022
     * Return the SUM of a column
1023
     */
1024
    sum<Entity extends ObjectLiteral>(
1025
        entityClass: EntityTarget<Entity>,
1026
        columnName: PickKeysByType<Entity, number>,
1027
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1028
    ): Promise<number | null> {
1029
        return this.callAggregateFun(entityClass, "SUM", columnName, where)
8✔
1030
    }
1031

1032
    /**
1033
     * Return the AVG of a column
1034
     */
1035
    average<Entity extends ObjectLiteral>(
1036
        entityClass: EntityTarget<Entity>,
1037
        columnName: PickKeysByType<Entity, number>,
1038
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1039
    ): Promise<number | null> {
1040
        return this.callAggregateFun(entityClass, "AVG", columnName, where)
8✔
1041
    }
1042

1043
    /**
1044
     * Return the MIN of a column
1045
     */
1046
    minimum<Entity extends ObjectLiteral>(
1047
        entityClass: EntityTarget<Entity>,
1048
        columnName: PickKeysByType<Entity, number>,
1049
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1050
    ): Promise<number | null> {
1051
        return this.callAggregateFun(entityClass, "MIN", columnName, where)
8✔
1052
    }
1053

1054
    /**
1055
     * Return the MAX of a column
1056
     */
1057
    maximum<Entity extends ObjectLiteral>(
1058
        entityClass: EntityTarget<Entity>,
1059
        columnName: PickKeysByType<Entity, number>,
1060
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1061
    ): Promise<number | null> {
1062
        return this.callAggregateFun(entityClass, "MAX", columnName, where)
8✔
1063
    }
1064

1065
    private async callAggregateFun<Entity extends ObjectLiteral>(
1066
        entityClass: EntityTarget<Entity>,
1067
        fnName: "SUM" | "AVG" | "MIN" | "MAX",
1068
        columnName: PickKeysByType<Entity, number>,
1069
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[] = {},
16✔
1070
    ): Promise<number | null> {
1071
        const metadata = this.connection.getMetadata(entityClass)
32✔
1072
        const column = metadata.columns.find(
32✔
1073
            (item) => item.propertyPath === columnName,
64✔
1074
        )
1075
        if (!column) {
32!
1076
            throw new TypeORMError(
×
1077
                `Column "${columnName}" was not found in table "${metadata.name}"`,
1078
            )
1079
        }
1080

1081
        const result = await this.createQueryBuilder(entityClass, metadata.name)
32✔
1082
            .setFindOptions({ where })
1083
            .select(
1084
                `${fnName}(${this.connection.driver.escape(
1085
                    column.databaseName,
1086
                )})`,
1087
                fnName,
1088
            )
1089
            .getRawOne()
1090
        return result[fnName] === null ? null : parseFloat(result[fnName])
32✔
1091
    }
1092

1093
    /**
1094
     * Finds entities that match given find options.
1095
     */
1096
    async find<Entity extends ObjectLiteral>(
1097
        entityClass: EntityTarget<Entity>,
1098
        options?: FindManyOptions<Entity>,
1099
    ): Promise<Entity[]> {
1100
        const metadata = this.connection.getMetadata(entityClass)
574✔
1101
        return this.createQueryBuilder<Entity>(
574✔
1102
            entityClass as any,
1103
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
1,128✔
1104
                metadata.name,
1105
        )
1106
            .setFindOptions(options || {})
797✔
1107
            .getMany()
1108
    }
1109

1110
    /**
1111
     * Finds entities that match given find options.
1112
     */
1113
    async findBy<Entity extends ObjectLiteral>(
1114
        entityClass: EntityTarget<Entity>,
1115
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1116
    ): Promise<Entity[]> {
1117
        const metadata = this.connection.getMetadata(entityClass)
169✔
1118
        return this.createQueryBuilder<Entity>(
169✔
1119
            entityClass as any,
1120
            metadata.name,
1121
        )
1122
            .setFindOptions({ where: where })
1123
            .getMany()
1124
    }
1125

1126
    /**
1127
     * Finds entities that match given find options.
1128
     * Also counts all entities that match given conditions,
1129
     * but ignores pagination settings (from and take options).
1130
     */
1131
    findAndCount<Entity extends ObjectLiteral>(
1132
        entityClass: EntityTarget<Entity>,
1133
        options?: FindManyOptions<Entity>,
1134
    ): Promise<[Entity[], number]> {
1135
        const metadata = this.connection.getMetadata(entityClass)
48✔
1136
        return this.createQueryBuilder<Entity>(
48✔
1137
            entityClass as any,
1138
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
96✔
1139
                metadata.name,
1140
        )
1141
            .setFindOptions(options || {})
56✔
1142
            .getManyAndCount()
1143
    }
1144

1145
    /**
1146
     * Finds entities that match given WHERE conditions.
1147
     * Also counts all entities that match given conditions,
1148
     * but ignores pagination settings (from and take options).
1149
     */
1150
    findAndCountBy<Entity extends ObjectLiteral>(
1151
        entityClass: EntityTarget<Entity>,
1152
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1153
    ): Promise<[Entity[], number]> {
1154
        const metadata = this.connection.getMetadata(entityClass)
×
1155
        return this.createQueryBuilder<Entity>(
×
1156
            entityClass as any,
1157
            metadata.name,
1158
        )
1159
            .setFindOptions({ where })
1160
            .getManyAndCount()
1161
    }
1162

1163
    /**
1164
     * Finds entities with ids.
1165
     * Optionally find options or conditions can be applied.
1166
     *
1167
     * @deprecated use `findBy` method instead in conjunction with `In` operator, for example:
1168
     *
1169
     * .findBy({
1170
     *     id: In([1, 2, 3])
1171
     * })
1172
     */
1173
    async findByIds<Entity extends ObjectLiteral>(
1174
        entityClass: EntityTarget<Entity>,
1175
        ids: any[],
1176
    ): Promise<Entity[]> {
1177
        // if no ids passed, no need to execute a query - just return an empty array of values
1178
        if (!ids.length) return Promise.resolve([])
32✔
1179

1180
        const metadata = this.connection.getMetadata(entityClass)
28✔
1181
        return this.createQueryBuilder<Entity>(
28✔
1182
            entityClass as any,
1183
            metadata.name,
1184
        )
1185
            .andWhereInIds(ids)
1186
            .getMany()
1187
    }
1188

1189
    /**
1190
     * Finds first entity by a given find options.
1191
     * If entity was not found in the database - returns null.
1192
     */
1193
    async findOne<Entity extends ObjectLiteral>(
1194
        entityClass: EntityTarget<Entity>,
1195
        options: FindOneOptions<Entity>,
1196
    ): Promise<Entity | null> {
1197
        const metadata = this.connection.getMetadata(entityClass)
1,316✔
1198

1199
        // prepare alias for built query
1200
        let alias: string = metadata.name
1,316✔
1201
        if (options && options.join) {
1,316✔
1202
            alias = options.join.alias
72✔
1203
        }
1204

1205
        if (!options.where) {
1,316✔
1206
            throw new Error(
4✔
1207
                `You must provide selection conditions in order to find a single row.`,
1208
            )
1209
        }
1210

1211
        // create query builder and apply find options
1212
        return this.createQueryBuilder<Entity>(entityClass, alias)
1,312✔
1213
            .setFindOptions({
1214
                ...options,
1215
                take: 1,
1216
            })
1217
            .getOne()
1218
    }
1219

1220
    /**
1221
     * Finds first entity that matches given where condition.
1222
     * If entity was not found in the database - returns null.
1223
     */
1224
    async findOneBy<Entity extends ObjectLiteral>(
1225
        entityClass: EntityTarget<Entity>,
1226
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1227
    ): Promise<Entity | null> {
1228
        const metadata = this.connection.getMetadata(entityClass)
804✔
1229

1230
        // create query builder and apply find options
1231
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
804✔
1232
            .setFindOptions({
1233
                where,
1234
                take: 1,
1235
            })
1236
            .getOne()
1237
    }
1238

1239
    /**
1240
     * Finds first entity that matches given id.
1241
     * If entity was not found in the database - returns null.
1242
     *
1243
     * @deprecated use `findOneBy` method instead in conjunction with `In` operator, for example:
1244
     *
1245
     * .findOneBy({
1246
     *     id: 1 // where "id" is your primary column name
1247
     * })
1248
     */
1249
    async findOneById<Entity extends ObjectLiteral>(
1250
        entityClass: EntityTarget<Entity>,
1251
        id: number | string | Date | ObjectId,
1252
    ): Promise<Entity | null> {
1253
        const metadata = this.connection.getMetadata(entityClass)
16✔
1254

1255
        // create query builder and apply find options
1256
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
16✔
1257
            .setFindOptions({
1258
                take: 1,
1259
            })
1260
            .whereInIds(metadata.ensureEntityIdMap(id))
1261
            .getOne()
1262
    }
1263

1264
    /**
1265
     * Finds first entity by a given find options.
1266
     * If entity was not found in the database - rejects with error.
1267
     */
1268
    async findOneOrFail<Entity extends ObjectLiteral>(
1269
        entityClass: EntityTarget<Entity>,
1270
        options: FindOneOptions<Entity>,
1271
    ): Promise<Entity> {
1272
        return this.findOne<Entity>(entityClass as any, options).then(
62✔
1273
            (value) => {
1274
                if (value === null) {
62✔
1275
                    return Promise.reject(
8✔
1276
                        new EntityNotFoundError(entityClass, options),
1277
                    )
1278
                }
1279
                return Promise.resolve(value)
54✔
1280
            },
1281
        )
1282
    }
1283

1284
    /**
1285
     * Finds first entity that matches given where condition.
1286
     * If entity was not found in the database - rejects with error.
1287
     */
1288
    async findOneByOrFail<Entity extends ObjectLiteral>(
1289
        entityClass: EntityTarget<Entity>,
1290
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1291
    ): Promise<Entity> {
1292
        return this.findOneBy<Entity>(entityClass as any, where).then(
139✔
1293
            (value) => {
1294
                if (value === null) {
139✔
1295
                    return Promise.reject(
4✔
1296
                        new EntityNotFoundError(entityClass, where),
1297
                    )
1298
                }
1299
                return Promise.resolve(value)
135✔
1300
            },
1301
        )
1302
    }
1303

1304
    /**
1305
     * Clears all the data from the given table (truncates/drops it).
1306
     *
1307
     * Note: this method uses TRUNCATE and may not work as you expect in transactions on some platforms.
1308
     * @see https://stackoverflow.com/a/5972738/925151
1309
     */
1310
    async clear<Entity>(entityClass: EntityTarget<Entity>): Promise<void> {
1311
        const metadata = this.connection.getMetadata(entityClass)
8✔
1312
        const queryRunner =
1313
            this.queryRunner || this.connection.createQueryRunner()
8✔
1314
        try {
8✔
1315
            return await queryRunner.clearTable(metadata.tablePath) // await is needed here because we are using finally
8✔
1316
        } finally {
1317
            if (!this.queryRunner) await queryRunner.release()
8✔
1318
        }
1319
    }
1320

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

1337
        if (isNaN(Number(value)))
28✔
1338
            throw new TypeORMError(`Value "${value}" is not a number.`)
4✔
1339

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

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

1358
    /**
1359
     * Decrements some column by provided value of the entities matched given conditions.
1360
     */
1361
    async decrement<Entity extends ObjectLiteral>(
1362
        entityClass: EntityTarget<Entity>,
1363
        conditions: any,
1364
        propertyPath: string,
1365
        value: number | string,
1366
    ): Promise<UpdateResult> {
1367
        const metadata = this.connection.getMetadata(entityClass)
32✔
1368
        const column = metadata.findColumnWithPropertyPath(propertyPath)
32✔
1369
        if (!column)
32✔
1370
            throw new TypeORMError(
4✔
1371
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1372
            )
1373

1374
        if (isNaN(Number(value)))
28✔
1375
            throw new TypeORMError(`Value "${value}" is not a number.`)
4✔
1376

1377
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
1378
        const values: QueryDeepPartialEntity<Entity> = propertyPath
24✔
1379
            .split(".")
1380
            .reduceRight(
1381
                (value, key) => ({ [key]: value } as any),
28✔
1382
                () =>
1383
                    this.connection.driver.escape(column.databaseName) +
24✔
1384
                    " - " +
1385
                    value,
1386
            )
1387

1388
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
24✔
1389
            .update(entityClass)
1390
            .set(values)
1391
            .where(conditions)
1392
            .execute()
1393
    }
1394

1395
    /**
1396
     * Gets repository for the given entity class or name.
1397
     * If single database connection mode is used, then repository is obtained from the
1398
     * repository aggregator, where each repository is individually created for this entity manager.
1399
     * When single database connection is not used, repository is being obtained from the connection.
1400
     */
1401
    getRepository<Entity extends ObjectLiteral>(
1402
        target: EntityTarget<Entity>,
1403
    ): Repository<Entity> {
1404
        // find already created repository instance and return it if found
1405
        const repoFromMap = this.repositories.get(target)
60,039✔
1406
        if (repoFromMap) return repoFromMap
60,039✔
1407

1408
        // if repository was not found then create it, store its instance and return it
1409
        if (this.connection.driver.options.type === "mongodb") {
17,626!
1410
            const newRepository = new MongoRepository(
×
1411
                target,
1412
                this,
1413
                this.queryRunner,
1414
            )
1415
            this.repositories.set(target, newRepository)
×
1416
            return newRepository
×
1417
        } else {
1418
            const newRepository = new Repository<any>(
17,626✔
1419
                target,
1420
                this,
1421
                this.queryRunner,
1422
            )
1423
            this.repositories.set(target, newRepository)
17,626✔
1424
            return newRepository
17,626✔
1425
        }
1426
    }
1427

1428
    /**
1429
     * Gets tree repository for the given entity class or name.
1430
     * If single database connection mode is used, then repository is obtained from the
1431
     * repository aggregator, where each repository is individually created for this entity manager.
1432
     * When single database connection is not used, repository is being obtained from the connection.
1433
     */
1434
    getTreeRepository<Entity extends ObjectLiteral>(
1435
        target: EntityTarget<Entity>,
1436
    ): TreeRepository<Entity> {
1437
        // tree tables aren't supported by some drivers (mongodb)
1438
        if (this.connection.driver.treeSupport === false)
342!
1439
            throw new TreeRepositoryNotSupportedError(this.connection.driver)
×
1440

1441
        // find already created repository instance and return it if found
1442
        const repository = this.treeRepositories.find(
342✔
1443
            (repository) => repository.target === target,
263✔
1444
        )
1445
        if (repository) return repository
342✔
1446

1447
        // check if repository is real tree repository
1448
        const newRepository = new TreeRepository(target, this, this.queryRunner)
83✔
1449
        this.treeRepositories.push(newRepository)
83✔
1450
        return newRepository
83✔
1451
    }
1452

1453
    /**
1454
     * Gets mongodb repository for the given entity class.
1455
     */
1456
    getMongoRepository<Entity extends ObjectLiteral>(
1457
        target: EntityTarget<Entity>,
1458
    ): MongoRepository<Entity> {
1459
        return this.connection.getMongoRepository<Entity>(target)
×
1460
    }
1461

1462
    /**
1463
     * Creates a new repository instance out of a given Repository and
1464
     * sets current EntityManager instance to it. Used to work with custom repositories
1465
     * in transactions.
1466
     */
1467
    withRepository<Entity extends ObjectLiteral, R extends Repository<any>>(
1468
        repository: R & Repository<Entity>,
1469
    ): R {
1470
        const repositoryConstructor =
1471
            repository.constructor as typeof Repository
4✔
1472
        const { target, manager, queryRunner, ...otherRepositoryProperties } =
1473
            repository
4✔
1474
        return Object.assign(
4✔
1475
            new repositoryConstructor(repository.target, this) as R,
1476
            {
1477
                ...otherRepositoryProperties,
1478
            },
1479
        )
1480
    }
1481

1482
    /**
1483
     * Gets custom entity repository marked with @EntityRepository decorator.
1484
     *
1485
     * @deprecated use Repository.extend to create custom repositories
1486
     */
1487
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
1488
        const entityRepositoryMetadataArgs =
1489
            getMetadataArgsStorage().entityRepositories.find((repository) => {
4✔
1490
                return (
4✔
1491
                    repository.target ===
1492
                    (typeof customRepository === "function"
4!
1493
                        ? customRepository
1494
                        : (customRepository as any).constructor)
1495
                )
1496
            })
1497
        if (!entityRepositoryMetadataArgs)
4!
1498
            throw new CustomRepositoryNotFoundError(customRepository)
×
1499

1500
        const entityMetadata = entityRepositoryMetadataArgs.entity
4!
1501
            ? this.connection.getMetadata(entityRepositoryMetadataArgs.entity)
1502
            : undefined
1503
        const entityRepositoryInstance =
1504
            new (entityRepositoryMetadataArgs.target as any)(
4✔
1505
                this,
1506
                entityMetadata,
1507
            )
1508

1509
        // NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
1510
        // however we need these properties for internal work of the class
1511
        if (entityRepositoryInstance instanceof AbstractRepository) {
4!
1512
            if (!(entityRepositoryInstance as any)["manager"])
4✔
1513
                (entityRepositoryInstance as any)["manager"] = this
4✔
1514
        } else {
1515
            if (!entityMetadata)
×
1516
                throw new CustomRepositoryCannotInheritRepositoryError(
×
1517
                    customRepository,
1518
                )
1519
            ;(entityRepositoryInstance as any)["manager"] = this
×
1520
            ;(entityRepositoryInstance as any)["metadata"] = entityMetadata
×
1521
        }
1522

1523
        return entityRepositoryInstance
4✔
1524
    }
1525

1526
    /**
1527
     * Releases all resources used by entity manager.
1528
     * This is used when entity manager is created with a single query runner,
1529
     * and this single query runner needs to be released after job with entity manager is done.
1530
     */
1531
    async release(): Promise<void> {
1532
        if (!this.queryRunner) throw new NoNeedToReleaseEntityManagerError()
×
1533

1534
        return this.queryRunner.release()
×
1535
    }
1536
}
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

© 2025 Coveralls, Inc