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

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

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

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

51
    // -------------------------------------------------------------------------
52
    // Public Properties
53
    // -------------------------------------------------------------------------
54

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

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

66
    // -------------------------------------------------------------------------
67
    // Protected Properties
68
    // -------------------------------------------------------------------------
69

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

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

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

87
    // -------------------------------------------------------------------------
88
    // Constructor
89
    // -------------------------------------------------------------------------
90

91
    constructor(connection: DataSource, queryRunner?: QueryRunner) {
92
        this.connection = connection
331✔
93
        if (queryRunner) {
331!
UNCOV
94
            this.queryRunner = queryRunner
×
95
            // dynamic: this.queryRunner = manager;
UNCOV
96
            ObjectUtils.assign(this.queryRunner, { manager: this })
×
97
        }
98
    }
99

100
    // -------------------------------------------------------------------------
101
    // Public Methods
102
    // -------------------------------------------------------------------------
103

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

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

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

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

UNCOV
146
        if (this.queryRunner && this.queryRunner.isReleased)
×
147
            throw new QueryRunnerProviderAlreadyReleasedError()
×
148

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

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

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

181
    /**
182
     * Tagged template function that executes raw SQL query and returns raw database results.
183
     * Template expressions are automatically transformed into database parameters.
184
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
185
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
186
     * Example: entityManager.sql`SELECT * FROM table_name WHERE id = ${id}`
187
     */
188
    async sql<T = any>(
189
        strings: TemplateStringsArray,
190
        ...values: unknown[]
191
    ): Promise<T> {
192
        const { query, parameters } = buildSqlTag({
×
193
            driver: this.connection.driver,
194
            strings: strings,
195
            expressions: values,
196
        })
197

198
        return await this.query(query, parameters)
×
199
    }
200

201
    /**
202
     * Creates a new query builder that can be used to build a SQL query.
203
     */
204
    createQueryBuilder<Entity extends ObjectLiteral>(
205
        entityClass: EntityTarget<Entity>,
206
        alias: string,
207
        queryRunner?: QueryRunner,
208
    ): SelectQueryBuilder<Entity>
209

210
    /**
211
     * Creates a new query builder that can be used to build a SQL query.
212
     */
213
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
214

215
    /**
216
     * Creates a new query builder that can be used to build a SQL query.
217
     */
218
    createQueryBuilder<Entity extends ObjectLiteral>(
219
        entityClass?: EntityTarget<Entity> | QueryRunner,
220
        alias?: string,
221
        queryRunner?: QueryRunner,
222
    ): SelectQueryBuilder<Entity> {
UNCOV
223
        if (alias) {
×
UNCOV
224
            return this.connection.createQueryBuilder(
×
225
                entityClass as EntityTarget<Entity>,
226
                alias,
227
                queryRunner || this.queryRunner,
×
228
            )
229
        } else {
UNCOV
230
            return this.connection.createQueryBuilder(
×
231
                (entityClass as QueryRunner | undefined) ||
×
232
                    queryRunner ||
233
                    this.queryRunner,
234
            )
235
        }
236
    }
237

238
    /**
239
     * Checks if entity has an id.
240
     */
241
    hasId(entity: any): boolean
242

243
    /**
244
     * Checks if entity of given schema name has an id.
245
     */
246
    hasId(target: Function | string, entity: any): boolean
247

248
    /**
249
     * Checks if entity has an id by its Function type or schema name.
250
     */
251
    hasId(targetOrEntity: any | Function | string, maybeEntity?: any): boolean {
252
        const target =
253
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
1!
254
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
1!
255
        const metadata = this.connection.getMetadata(target)
1✔
256
        return metadata.hasId(entity)
1✔
257
    }
258

259
    /**
260
     * Gets entity mixed id.
261
     */
262
    getId(entity: any): any
263

264
    /**
265
     * Gets entity mixed id.
266
     */
267
    getId(target: EntityTarget<any>, entity: any): any
268

269
    /**
270
     * Gets entity mixed id.
271
     */
272
    getId(targetOrEntity: any | EntityTarget<any>, maybeEntity?: any): any {
273
        const target =
UNCOV
274
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
×
UNCOV
275
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
×
UNCOV
276
        const metadata = this.connection.getMetadata(target)
×
UNCOV
277
        return metadata.getEntityIdMixedMap(entity)
×
278
    }
279

280
    /**
281
     * Creates a new entity instance and copies all entity properties from this object into a new entity.
282
     * Note that it copies only properties that present in entity schema.
283
     */
284
    create<Entity, EntityLike extends DeepPartial<Entity>>(
285
        entityClass: EntityTarget<Entity>,
286
        plainObject?: EntityLike,
287
    ): Entity
288

289
    /**
290
     * Creates a new entities and copies all entity properties from given objects into their new entities.
291
     * Note that it copies only properties that present in entity schema.
292
     */
293
    create<Entity, EntityLike extends DeepPartial<Entity>>(
294
        entityClass: EntityTarget<Entity>,
295
        plainObjects?: EntityLike[],
296
    ): Entity[]
297

298
    /**
299
     * Creates a new entity instance or instances.
300
     * Can copy properties from the given object into new entities.
301
     */
302
    create<Entity, EntityLike extends DeepPartial<Entity>>(
303
        entityClass: EntityTarget<Entity>,
304
        plainObjectOrObjects?: EntityLike | EntityLike[],
305
    ): Entity | Entity[] {
306
        const metadata = this.connection.getMetadata(entityClass)
5✔
307

308
        if (!plainObjectOrObjects) return metadata.create(this.queryRunner)
5✔
309

310
        if (Array.isArray(plainObjectOrObjects))
4!
UNCOV
311
            return (plainObjectOrObjects as EntityLike[]).map(
×
UNCOV
312
                (plainEntityLike) => this.create(entityClass, plainEntityLike),
×
313
            )
314

315
        const mergeIntoEntity = metadata.create(this.queryRunner)
4✔
316
        this.plainObjectToEntityTransformer.transform(
4✔
317
            mergeIntoEntity,
318
            plainObjectOrObjects,
319
            metadata,
320
            true,
321
        )
322
        return mergeIntoEntity
4✔
323
    }
324

325
    /**
326
     * Merges two entities into one new entity.
327
     */
328
    merge<Entity extends ObjectLiteral>(
329
        entityClass: EntityTarget<Entity>,
330
        mergeIntoEntity: Entity,
331
        ...entityLikes: DeepPartial<Entity>[]
332
    ): Entity {
333
        // todo: throw exception if entity manager is released
334
        const metadata = this.connection.getMetadata(entityClass)
255✔
335
        entityLikes.forEach((object) =>
255✔
336
            this.plainObjectToEntityTransformer.transform(
258✔
337
                mergeIntoEntity,
338
                object,
339
                metadata,
340
            ),
341
        )
342
        return mergeIntoEntity
255✔
343
    }
344

345
    /**
346
     * Creates a new entity from the given plain javascript object. If entity already exist in the database, then
347
     * it loads it (and everything related to it), replaces all values with the new ones from the given object
348
     * and returns this new entity. This new entity is actually a loaded from the db entity with all properties
349
     * replaced from the new object.
350
     */
351
    async preload<Entity extends ObjectLiteral>(
352
        entityClass: EntityTarget<Entity>,
353
        entityLike: DeepPartial<Entity>,
354
    ): Promise<Entity | undefined> {
355
        const metadata = this.connection.getMetadata(entityClass)
1✔
356
        const plainObjectToDatabaseEntityTransformer =
357
            new PlainObjectToDatabaseEntityTransformer(this.connection.manager)
1✔
358
        const transformedEntity =
359
            await plainObjectToDatabaseEntityTransformer.transform(
1✔
360
                entityLike,
361
                metadata,
362
            )
363
        if (transformedEntity)
1✔
364
            return this.merge(
1✔
365
                entityClass as any,
366
                transformedEntity as Entity,
367
                entityLike,
368
            )
369

370
        return undefined
×
371
    }
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>(entities: Entity[], options?: SaveOptions): Promise<Entity[]>
378

379
    /**
380
     * Saves all given entities in the database.
381
     * If entities do not exist in the database then inserts, otherwise updates.
382
     */
383
    save<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
384

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

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

405
    /**
406
     * Saves a given entity in the database.
407
     * If entity does not exist in the database then inserts, otherwise updates.
408
     */
409
    save<Entity, T extends DeepPartial<Entity>>(
410
        targetOrEntity: EntityTarget<Entity>,
411
        entity: T,
412
        options: SaveOptions & { reload: false },
413
    ): Promise<T>
414

415
    /**
416
     * Saves a given entity in the database.
417
     * If entity does not exist in the database then inserts, otherwise updates.
418
     */
419
    save<Entity, T extends DeepPartial<Entity>>(
420
        targetOrEntity: EntityTarget<Entity>,
421
        entity: T,
422
        options?: SaveOptions,
423
    ): Promise<T & Entity>
424

425
    /**
426
     * Saves a given entity in the database.
427
     */
428
    save<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
429
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
430
        maybeEntityOrOptions?: T | T[],
431
        maybeOptions?: SaveOptions,
432
    ): Promise<T | T[]> {
433
        // normalize mixed parameters
434
        let target =
435
            arguments.length > 1 &&
105!
436
            (typeof targetOrEntity === "function" ||
437
                InstanceChecker.isEntitySchema(targetOrEntity) ||
438
                typeof targetOrEntity === "string")
439
                ? (targetOrEntity as Function | string)
440
                : undefined
441
        const entity: T | T[] = target
105✔
442
            ? (maybeEntityOrOptions as T | T[])
443
            : (targetOrEntity as T | T[])
444
        const options = target
105✔
445
            ? maybeOptions
446
            : (maybeEntityOrOptions as SaveOptions)
447

448
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
105!
449

450
        // if user passed empty array of entities then we don't need to do anything
451
        if (Array.isArray(entity) && entity.length === 0)
105✔
452
            return Promise.resolve(entity)
1✔
453

454
        // execute save operation
455
        return new EntityPersistExecutor(
104✔
456
            this.connection,
457
            this.queryRunner,
458
            "save",
459
            target,
460
            entity,
461
            options,
462
        )
463
            .execute()
464
            .then(() => entity)
104✔
465
    }
466

467
    /**
468
     * Removes a given entity from the database.
469
     */
470
    remove<Entity>(entity: Entity, options?: RemoveOptions): Promise<Entity>
471

472
    /**
473
     * Removes a given entity from the database.
474
     */
475
    remove<Entity>(
476
        targetOrEntity: EntityTarget<Entity>,
477
        entity: Entity,
478
        options?: RemoveOptions,
479
    ): Promise<Entity>
480

481
    /**
482
     * Removes a given entity from the database.
483
     */
484
    remove<Entity>(entity: Entity[], options?: RemoveOptions): Promise<Entity>
485

486
    /**
487
     * Removes a given entity from the database.
488
     */
489
    remove<Entity>(
490
        targetOrEntity: EntityTarget<Entity>,
491
        entity: Entity[],
492
        options?: RemoveOptions,
493
    ): Promise<Entity[]>
494

495
    /**
496
     * Removes a given entity from the database.
497
     */
498
    remove<Entity extends ObjectLiteral>(
499
        targetOrEntity: (Entity | Entity[]) | EntityTarget<Entity>,
500
        maybeEntityOrOptions?: Entity | Entity[],
501
        maybeOptions?: RemoveOptions,
502
    ): Promise<Entity | Entity[]> {
503
        // normalize mixed parameters
504
        const target =
505
            arguments.length > 1 &&
4!
506
            (typeof targetOrEntity === "function" ||
507
                InstanceChecker.isEntitySchema(targetOrEntity) ||
508
                typeof targetOrEntity === "string")
509
                ? (targetOrEntity as Function | string)
510
                : undefined
511
        const entity: Entity | Entity[] = target
4!
512
            ? (maybeEntityOrOptions as Entity | Entity[])
513
            : (targetOrEntity as Entity | Entity[])
514
        const options = target
4!
515
            ? maybeOptions
516
            : (maybeEntityOrOptions as SaveOptions)
517

518
        // if user passed empty array of entities then we don't need to do anything
519
        if (Array.isArray(entity) && entity.length === 0)
4!
520
            return Promise.resolve(entity)
×
521

522
        // execute save operation
523
        return new EntityPersistExecutor(
4✔
524
            this.connection,
525
            this.queryRunner,
526
            "remove",
527
            target,
528
            entity,
529
            options,
530
        )
531
            .execute()
532
            .then(() => entity)
4✔
533
    }
534

535
    /**
536
     * Records the delete date of all given entities.
537
     */
538
    softRemove<Entity>(
539
        entities: Entity[],
540
        options?: SaveOptions,
541
    ): Promise<Entity[]>
542

543
    /**
544
     * Records the delete date of a given entity.
545
     */
546
    softRemove<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
547

548
    /**
549
     * Records the delete date of all given entities.
550
     */
551
    softRemove<Entity, T extends DeepPartial<Entity>>(
552
        targetOrEntity: EntityTarget<Entity>,
553
        entities: T[],
554
        options?: SaveOptions,
555
    ): Promise<T[]>
556

557
    /**
558
     * Records the delete date of a given entity.
559
     */
560
    softRemove<Entity, T extends DeepPartial<Entity>>(
561
        targetOrEntity: EntityTarget<Entity>,
562
        entity: T,
563
        options?: SaveOptions,
564
    ): Promise<T>
565

566
    /**
567
     * Records the delete date of one or many given entities.
568
     */
569
    softRemove<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
570
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
571
        maybeEntityOrOptions?: T | T[],
572
        maybeOptions?: SaveOptions,
573
    ): Promise<T | T[]> {
574
        // normalize mixed parameters
575
        let target =
576
            arguments.length > 1 &&
3!
577
            (typeof targetOrEntity === "function" ||
578
                InstanceChecker.isEntitySchema(targetOrEntity) ||
579
                typeof targetOrEntity === "string")
580
                ? (targetOrEntity as Function | string)
581
                : undefined
582
        const entity: T | T[] = target
3!
583
            ? (maybeEntityOrOptions as T | T[])
584
            : (targetOrEntity as T | T[])
585
        const options = target
3!
586
            ? maybeOptions
587
            : (maybeEntityOrOptions as SaveOptions)
588

589
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
3!
590

591
        // if user passed empty array of entities then we don't need to do anything
592
        if (Array.isArray(entity) && entity.length === 0)
3!
593
            return Promise.resolve(entity)
×
594

595
        // execute soft-remove operation
596
        return new EntityPersistExecutor(
3✔
597
            this.connection,
598
            this.queryRunner,
599
            "soft-remove",
600
            target,
601
            entity,
602
            options,
603
        )
604
            .execute()
605
            .then(() => entity)
3✔
606
    }
607

608
    /**
609
     * Recovers all given entities.
610
     */
611
    recover<Entity>(
612
        entities: Entity[],
613
        options?: SaveOptions,
614
    ): Promise<Entity[]>
615

616
    /**
617
     * Recovers a given entity.
618
     */
619
    recover<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
620

621
    /**
622
     * Recovers all given entities.
623
     */
624
    recover<Entity, T extends DeepPartial<Entity>>(
625
        targetOrEntity: EntityTarget<Entity>,
626
        entities: T[],
627
        options?: SaveOptions,
628
    ): Promise<T[]>
629

630
    /**
631
     * Recovers a given entity.
632
     */
633
    recover<Entity, T extends DeepPartial<Entity>>(
634
        targetOrEntity: EntityTarget<Entity>,
635
        entity: T,
636
        options?: SaveOptions,
637
    ): Promise<T>
638

639
    /**
640
     * Recovers one or many given entities.
641
     */
642
    recover<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
643
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
644
        maybeEntityOrOptions?: T | T[],
645
        maybeOptions?: SaveOptions,
646
    ): Promise<T | T[]> {
647
        // normalize mixed parameters
648
        let target =
UNCOV
649
            arguments.length > 1 &&
×
650
            (typeof targetOrEntity === "function" ||
651
                InstanceChecker.isEntitySchema(targetOrEntity) ||
652
                typeof targetOrEntity === "string")
653
                ? (targetOrEntity as Function | string)
654
                : undefined
UNCOV
655
        const entity: T | T[] = target
×
656
            ? (maybeEntityOrOptions as T | T[])
657
            : (targetOrEntity as T | T[])
UNCOV
658
        const options = target
×
659
            ? maybeOptions
660
            : (maybeEntityOrOptions as SaveOptions)
661

UNCOV
662
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
×
663

664
        // if user passed empty array of entities then we don't need to do anything
UNCOV
665
        if (Array.isArray(entity) && entity.length === 0)
×
666
            return Promise.resolve(entity)
×
667

668
        // execute recover operation
UNCOV
669
        return new EntityPersistExecutor(
×
670
            this.connection,
671
            this.queryRunner,
672
            "recover",
673
            target,
674
            entity,
675
            options,
676
        )
677
            .execute()
UNCOV
678
            .then(() => entity)
×
679
    }
680

681
    /**
682
     * Inserts a given entity into the database.
683
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
684
     * Executes fast and efficient INSERT query.
685
     * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted.
686
     * You can execute bulk inserts using this method.
687
     */
688
    async insert<Entity extends ObjectLiteral>(
689
        target: EntityTarget<Entity>,
690
        entity:
691
            | QueryDeepPartialEntity<Entity>
692
            | QueryDeepPartialEntity<Entity>[],
693
    ): Promise<InsertResult> {
UNCOV
694
        return this.createQueryBuilder()
×
695
            .insert()
696
            .into(target)
697
            .values(entity)
698
            .execute()
699
    }
700

701
    async upsert<Entity extends ObjectLiteral>(
702
        target: EntityTarget<Entity>,
703
        entityOrEntities:
704
            | QueryDeepPartialEntity<Entity>
705
            | QueryDeepPartialEntity<Entity>[],
706
        conflictPathsOrOptions: string[] | UpsertOptions<Entity>,
707
    ): Promise<InsertResult> {
UNCOV
708
        const metadata = this.connection.getMetadata(target)
×
709

710
        let options: UpsertOptions<Entity>
711

UNCOV
712
        if (Array.isArray(conflictPathsOrOptions)) {
×
UNCOV
713
            options = {
×
714
                conflictPaths: conflictPathsOrOptions,
715
            }
716
        } else {
UNCOV
717
            options = conflictPathsOrOptions
×
718
        }
719

720
        let entities: QueryDeepPartialEntity<Entity>[]
721

UNCOV
722
        if (!Array.isArray(entityOrEntities)) {
×
UNCOV
723
            entities = [entityOrEntities]
×
724
        } else {
UNCOV
725
            entities = entityOrEntities
×
726
        }
727

UNCOV
728
        const conflictColumns = metadata.mapPropertyPathsToColumns(
×
729
            Array.isArray(options.conflictPaths)
×
730
                ? options.conflictPaths
731
                : Object.keys(options.conflictPaths),
732
        )
733

UNCOV
734
        const overwriteColumns = metadata.columns.filter(
×
735
            (col) =>
UNCOV
736
                !conflictColumns.includes(col) &&
×
737
                entities.some(
738
                    (entity) =>
UNCOV
739
                        typeof col.getEntityValue(entity) !== "undefined",
×
740
                ),
741
        )
742

UNCOV
743
        return this.createQueryBuilder()
×
744
            .insert()
745
            .into(target)
746
            .values(entities)
747
            .orUpdate(
748
                [...conflictColumns, ...overwriteColumns].map(
UNCOV
749
                    (col) => col.databaseName,
×
750
                ),
UNCOV
751
                conflictColumns.map((col) => col.databaseName),
×
752
                {
753
                    skipUpdateIfNoValuesChanged:
754
                        options.skipUpdateIfNoValuesChanged,
755
                    indexPredicate: options.indexPredicate,
756
                    upsertType:
757
                        options.upsertType ||
×
758
                        this.connection.driver.supportedUpsertTypes[0],
759
                },
760
            )
761
            .execute()
762
    }
763

764
    /**
765
     * Updates entity partially. Entity can be found by a given condition(s).
766
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
767
     * Executes fast and efficient UPDATE query.
768
     * Does not check if entity exist in the database.
769
     * Condition(s) cannot be empty.
770
     */
771
    update<Entity extends ObjectLiteral>(
772
        target: EntityTarget<Entity>,
773
        criteria:
774
            | string
775
            | string[]
776
            | number
777
            | number[]
778
            | Date
779
            | Date[]
780
            | ObjectId
781
            | ObjectId[]
782
            | any,
783
        partialEntity: QueryDeepPartialEntity<Entity>,
784
    ): Promise<UpdateResult> {
785
        // if user passed empty criteria or empty list of criterias, then throw an error
UNCOV
786
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
×
UNCOV
787
            return Promise.reject(
×
788
                new TypeORMError(
789
                    `Empty criteria(s) are not allowed for the update method.`,
790
                ),
791
            )
792
        }
793

UNCOV
794
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
×
UNCOV
795
            return this.createQueryBuilder()
×
796
                .update(target)
797
                .set(partialEntity)
798
                .whereInIds(criteria)
799
                .execute()
800
        } else {
UNCOV
801
            return this.createQueryBuilder()
×
802
                .update(target)
803
                .set(partialEntity)
804
                .where(criteria)
805
                .execute()
806
        }
807
    }
808

809
    /**
810
     * Updates all entities of target type, setting fields from supplied partial entity.
811
     * This is a primitive operation without cascades, relations or other operations included.
812
     * Executes fast and efficient UPDATE query without WHERE clause.
813
     *
814
     * WARNING! This method updates ALL rows in the target table.
815
     */
816
    updateAll<Entity extends ObjectLiteral>(
817
        target: EntityTarget<Entity>,
818
        partialEntity: QueryDeepPartialEntity<Entity>,
819
    ): Promise<UpdateResult> {
UNCOV
820
        return this.createQueryBuilder()
×
821
            .update(target)
822
            .set(partialEntity)
823
            .execute()
824
    }
825

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

UNCOV
855
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
×
UNCOV
856
            return this.createQueryBuilder()
×
857
                .delete()
858
                .from(targetOrEntity)
859
                .whereInIds(criteria)
860
                .execute()
861
        } else {
UNCOV
862
            return this.createQueryBuilder()
×
863
                .delete()
864
                .from(targetOrEntity)
865
                .where(criteria)
866
                .execute()
867
        }
868
    }
869

870
    /**
871
     * Deletes all entities of target type.
872
     * This is a primitive operation without cascades, relations or other operations included.
873
     * Executes fast and efficient DELETE query without WHERE clause.
874
     *
875
     * WARNING! This method deletes ALL rows in the target table.
876
     */
877
    deleteAll<Entity extends ObjectLiteral>(
878
        targetOrEntity: EntityTarget<Entity>,
879
    ): Promise<DeleteResult> {
UNCOV
880
        return this.createQueryBuilder().delete().from(targetOrEntity).execute()
×
881
    }
882

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

UNCOV
912
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
×
913
            return this.createQueryBuilder()
×
914
                .softDelete()
915
                .from(targetOrEntity)
916
                .whereInIds(criteria)
917
                .execute()
918
        } else {
UNCOV
919
            return this.createQueryBuilder()
×
920
                .softDelete()
921
                .from(targetOrEntity)
922
                .where(criteria)
923
                .execute()
924
        }
925
    }
926

927
    /**
928
     * Restores entities by a given condition(s).
929
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
930
     * Executes fast and efficient DELETE query.
931
     * Does not check if entity exist in the database.
932
     * Condition(s) cannot be empty.
933
     */
934
    restore<Entity extends ObjectLiteral>(
935
        targetOrEntity: EntityTarget<Entity>,
936
        criteria:
937
            | string
938
            | string[]
939
            | number
940
            | number[]
941
            | Date
942
            | Date[]
943
            | ObjectId
944
            | ObjectId[]
945
            | any,
946
    ): Promise<UpdateResult> {
947
        // if user passed empty criteria or empty list of criterias, then throw an error
UNCOV
948
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
×
949
            return Promise.reject(
×
950
                new TypeORMError(
951
                    `Empty criteria(s) are not allowed for the restore method.`,
952
                ),
953
            )
954
        }
955

UNCOV
956
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
×
957
            return this.createQueryBuilder()
×
958
                .restore()
959
                .from(targetOrEntity)
960
                .whereInIds(criteria)
961
                .execute()
962
        } else {
UNCOV
963
            return this.createQueryBuilder()
×
964
                .restore()
965
                .from(targetOrEntity)
966
                .where(criteria)
967
                .execute()
968
        }
969
    }
970

971
    /**
972
     * Checks whether any entity exists with the given options.
973
     */
974
    exists<Entity extends ObjectLiteral>(
975
        entityClass: EntityTarget<Entity>,
976
        options?: FindManyOptions<Entity>,
977
    ): Promise<boolean> {
UNCOV
978
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
979
        return this.createQueryBuilder(
×
980
            entityClass,
981
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
×
982
                metadata.name,
983
        )
984
            .setFindOptions(options || {})
×
985
            .getExists()
986
    }
987

988
    /**
989
     * Checks whether any entity exists with the given conditions.
990
     */
991
    async existsBy<Entity extends ObjectLiteral>(
992
        entityClass: EntityTarget<Entity>,
993
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
994
    ): Promise<boolean> {
995
        const metadata = this.connection.getMetadata(entityClass)
×
996
        return this.createQueryBuilder(entityClass, metadata.name)
×
997
            .setFindOptions({ where })
998
            .getExists()
999
    }
1000

1001
    /**
1002
     * Counts entities that match given options.
1003
     * Useful for pagination.
1004
     */
1005
    count<Entity extends ObjectLiteral>(
1006
        entityClass: EntityTarget<Entity>,
1007
        options?: FindManyOptions<Entity>,
1008
    ): Promise<number> {
UNCOV
1009
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1010
        return this.createQueryBuilder(
×
1011
            entityClass,
1012
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
×
1013
                metadata.name,
1014
        )
1015
            .setFindOptions(options || {})
×
1016
            .getCount()
1017
    }
1018

1019
    /**
1020
     * Counts entities that match given conditions.
1021
     * Useful for pagination.
1022
     */
1023
    countBy<Entity extends ObjectLiteral>(
1024
        entityClass: EntityTarget<Entity>,
1025
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1026
    ): Promise<number> {
UNCOV
1027
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1028
        return this.createQueryBuilder(entityClass, metadata.name)
×
1029
            .setFindOptions({ where })
1030
            .getCount()
1031
    }
1032

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

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

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

1066
    /**
1067
     * Return the MAX of a column
1068
     */
1069
    maximum<Entity extends ObjectLiteral>(
1070
        entityClass: EntityTarget<Entity>,
1071
        columnName: PickKeysByType<Entity, number>,
1072
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1073
    ): Promise<number | null> {
UNCOV
1074
        return this.callAggregateFun(entityClass, "MAX", columnName, where)
×
1075
    }
1076

1077
    private async callAggregateFun<Entity extends ObjectLiteral>(
1078
        entityClass: EntityTarget<Entity>,
1079
        fnName: "SUM" | "AVG" | "MIN" | "MAX",
1080
        columnName: PickKeysByType<Entity, number>,
1081
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[] = {},
×
1082
    ): Promise<number | null> {
UNCOV
1083
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1084
        const column = metadata.columns.find(
×
UNCOV
1085
            (item) => item.propertyPath === columnName,
×
1086
        )
UNCOV
1087
        if (!column) {
×
1088
            throw new TypeORMError(
×
1089
                `Column "${columnName}" was not found in table "${metadata.name}"`,
1090
            )
1091
        }
1092

UNCOV
1093
        const result = await this.createQueryBuilder(entityClass, metadata.name)
×
1094
            .setFindOptions({ where })
1095
            .select(
1096
                `${fnName}(${this.connection.driver.escape(
1097
                    column.databaseName,
1098
                )})`,
1099
                fnName,
1100
            )
1101
            .getRawOne()
UNCOV
1102
        return result[fnName] === null ? null : parseFloat(result[fnName])
×
1103
    }
1104

1105
    /**
1106
     * Finds entities that match given find options.
1107
     */
1108
    async find<Entity extends ObjectLiteral>(
1109
        entityClass: EntityTarget<Entity>,
1110
        options?: FindManyOptions<Entity>,
1111
    ): Promise<Entity[]> {
UNCOV
1112
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1113
        return this.createQueryBuilder<Entity>(
×
1114
            entityClass as any,
1115
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
×
1116
                metadata.name,
1117
        )
1118
            .setFindOptions(options || {})
×
1119
            .getMany()
1120
    }
1121

1122
    /**
1123
     * Finds entities that match given find options.
1124
     */
1125
    async findBy<Entity extends ObjectLiteral>(
1126
        entityClass: EntityTarget<Entity>,
1127
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1128
    ): Promise<Entity[]> {
UNCOV
1129
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1130
        return this.createQueryBuilder<Entity>(
×
1131
            entityClass as any,
1132
            metadata.name,
1133
        )
1134
            .setFindOptions({ where: where })
1135
            .getMany()
1136
    }
1137

1138
    /**
1139
     * Finds entities that match given find options.
1140
     * Also counts all entities that match given conditions,
1141
     * but ignores pagination settings (from and take options).
1142
     */
1143
    findAndCount<Entity extends ObjectLiteral>(
1144
        entityClass: EntityTarget<Entity>,
1145
        options?: FindManyOptions<Entity>,
1146
    ): Promise<[Entity[], number]> {
UNCOV
1147
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1148
        return this.createQueryBuilder<Entity>(
×
1149
            entityClass as any,
1150
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
×
1151
                metadata.name,
1152
        )
1153
            .setFindOptions(options || {})
×
1154
            .getManyAndCount()
1155
    }
1156

1157
    /**
1158
     * Finds entities that match given WHERE conditions.
1159
     * Also counts all entities that match given conditions,
1160
     * but ignores pagination settings (from and take options).
1161
     */
1162
    findAndCountBy<Entity extends ObjectLiteral>(
1163
        entityClass: EntityTarget<Entity>,
1164
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1165
    ): Promise<[Entity[], number]> {
1166
        const metadata = this.connection.getMetadata(entityClass)
×
1167
        return this.createQueryBuilder<Entity>(
×
1168
            entityClass as any,
1169
            metadata.name,
1170
        )
1171
            .setFindOptions({ where })
1172
            .getManyAndCount()
1173
    }
1174

1175
    /**
1176
     * Finds entities with ids.
1177
     * Optionally find options or conditions can be applied.
1178
     *
1179
     * @deprecated use `findBy` method instead in conjunction with `In` operator, for example:
1180
     *
1181
     * .findBy({
1182
     *     id: In([1, 2, 3])
1183
     * })
1184
     */
1185
    async findByIds<Entity extends ObjectLiteral>(
1186
        entityClass: EntityTarget<Entity>,
1187
        ids: any[],
1188
    ): Promise<Entity[]> {
1189
        // if no ids passed, no need to execute a query - just return an empty array of values
UNCOV
1190
        if (!ids.length) return Promise.resolve([])
×
1191

UNCOV
1192
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1193
        return this.createQueryBuilder<Entity>(
×
1194
            entityClass as any,
1195
            metadata.name,
1196
        )
1197
            .andWhereInIds(ids)
1198
            .getMany()
1199
    }
1200

1201
    /**
1202
     * Finds first entity by a given find options.
1203
     * If entity was not found in the database - returns null.
1204
     */
1205
    async findOne<Entity extends ObjectLiteral>(
1206
        entityClass: EntityTarget<Entity>,
1207
        options: FindOneOptions<Entity>,
1208
    ): Promise<Entity | null> {
UNCOV
1209
        const metadata = this.connection.getMetadata(entityClass)
×
1210

1211
        // prepare alias for built query
UNCOV
1212
        let alias: string = metadata.name
×
UNCOV
1213
        if (options && options.join) {
×
UNCOV
1214
            alias = options.join.alias
×
1215
        }
1216

UNCOV
1217
        if (!options.where) {
×
UNCOV
1218
            throw new Error(
×
1219
                `You must provide selection conditions in order to find a single row.`,
1220
            )
1221
        }
1222

1223
        // create query builder and apply find options
UNCOV
1224
        return this.createQueryBuilder<Entity>(entityClass, alias)
×
1225
            .setFindOptions({
1226
                ...options,
1227
                take: 1,
1228
            })
1229
            .getOne()
1230
    }
1231

1232
    /**
1233
     * Finds first entity that matches given where condition.
1234
     * If entity was not found in the database - returns null.
1235
     */
1236
    async findOneBy<Entity extends ObjectLiteral>(
1237
        entityClass: EntityTarget<Entity>,
1238
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1239
    ): Promise<Entity | null> {
UNCOV
1240
        const metadata = this.connection.getMetadata(entityClass)
×
1241

1242
        // create query builder and apply find options
UNCOV
1243
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
×
1244
            .setFindOptions({
1245
                where,
1246
                take: 1,
1247
            })
1248
            .getOne()
1249
    }
1250

1251
    /**
1252
     * Finds first entity that matches given id.
1253
     * If entity was not found in the database - returns null.
1254
     *
1255
     * @deprecated use `findOneBy` method instead in conjunction with `In` operator, for example:
1256
     *
1257
     * .findOneBy({
1258
     *     id: 1 // where "id" is your primary column name
1259
     * })
1260
     */
1261
    async findOneById<Entity extends ObjectLiteral>(
1262
        entityClass: EntityTarget<Entity>,
1263
        id: number | string | Date | ObjectId,
1264
    ): Promise<Entity | null> {
UNCOV
1265
        const metadata = this.connection.getMetadata(entityClass)
×
1266

1267
        // create query builder and apply find options
UNCOV
1268
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
×
1269
            .setFindOptions({
1270
                take: 1,
1271
            })
1272
            .whereInIds(metadata.ensureEntityIdMap(id))
1273
            .getOne()
1274
    }
1275

1276
    /**
1277
     * Finds first entity by a given find options.
1278
     * If entity was not found in the database - rejects with error.
1279
     */
1280
    async findOneOrFail<Entity extends ObjectLiteral>(
1281
        entityClass: EntityTarget<Entity>,
1282
        options: FindOneOptions<Entity>,
1283
    ): Promise<Entity> {
UNCOV
1284
        return this.findOne<Entity>(entityClass as any, options).then(
×
1285
            (value) => {
UNCOV
1286
                if (value === null) {
×
UNCOV
1287
                    return Promise.reject(
×
1288
                        new EntityNotFoundError(entityClass, options),
1289
                    )
1290
                }
UNCOV
1291
                return Promise.resolve(value)
×
1292
            },
1293
        )
1294
    }
1295

1296
    /**
1297
     * Finds first entity that matches given where condition.
1298
     * If entity was not found in the database - rejects with error.
1299
     */
1300
    async findOneByOrFail<Entity extends ObjectLiteral>(
1301
        entityClass: EntityTarget<Entity>,
1302
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1303
    ): Promise<Entity> {
1304
        return this.findOneBy<Entity>(entityClass as any, where).then(
1✔
1305
            (value) => {
1306
                if (value === null) {
1!
UNCOV
1307
                    return Promise.reject(
×
1308
                        new EntityNotFoundError(entityClass, where),
1309
                    )
1310
                }
1311
                return Promise.resolve(value)
1✔
1312
            },
1313
        )
1314
    }
1315

1316
    /**
1317
     * Clears all the data from the given table (truncates/drops it).
1318
     *
1319
     * Note: this method uses TRUNCATE and may not work as you expect in transactions on some platforms.
1320
     * @see https://stackoverflow.com/a/5972738/925151
1321
     */
1322
    async clear<Entity>(entityClass: EntityTarget<Entity>): Promise<void> {
1323
        const metadata = this.connection.getMetadata(entityClass)
2✔
1324
        const queryRunner =
1325
            this.queryRunner || this.connection.createQueryRunner()
2✔
1326
        try {
2✔
1327
            return await queryRunner.clearTable(metadata.tablePath) // await is needed here because we are using finally
2✔
1328
        } finally {
1329
            if (!this.queryRunner) await queryRunner.release()
2✔
1330
        }
1331
    }
1332

1333
    /**
1334
     * Increments some column by provided value of the entities matched given conditions.
1335
     */
1336
    async increment<Entity extends ObjectLiteral>(
1337
        entityClass: EntityTarget<Entity>,
1338
        conditions: any,
1339
        propertyPath: string,
1340
        value: number | string,
1341
    ): Promise<UpdateResult> {
UNCOV
1342
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1343
        const column = metadata.findColumnWithPropertyPath(propertyPath)
×
UNCOV
1344
        if (!column)
×
UNCOV
1345
            throw new TypeORMError(
×
1346
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1347
            )
1348

UNCOV
1349
        if (isNaN(Number(value)))
×
UNCOV
1350
            throw new TypeORMError(`Value "${value}" is not a number.`)
×
1351

1352
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
UNCOV
1353
        const values: QueryDeepPartialEntity<Entity> = propertyPath
×
1354
            .split(".")
1355
            .reduceRight(
UNCOV
1356
                (value, key) => ({ [key]: value } as any),
×
1357
                () =>
UNCOV
1358
                    this.connection.driver.escape(column.databaseName) +
×
1359
                    " + " +
1360
                    value,
1361
            )
1362

UNCOV
1363
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
×
1364
            .update(entityClass)
1365
            .set(values)
1366
            .where(conditions)
1367
            .execute()
1368
    }
1369

1370
    /**
1371
     * Decrements some column by provided value of the entities matched given conditions.
1372
     */
1373
    async decrement<Entity extends ObjectLiteral>(
1374
        entityClass: EntityTarget<Entity>,
1375
        conditions: any,
1376
        propertyPath: string,
1377
        value: number | string,
1378
    ): Promise<UpdateResult> {
UNCOV
1379
        const metadata = this.connection.getMetadata(entityClass)
×
UNCOV
1380
        const column = metadata.findColumnWithPropertyPath(propertyPath)
×
UNCOV
1381
        if (!column)
×
UNCOV
1382
            throw new TypeORMError(
×
1383
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1384
            )
1385

UNCOV
1386
        if (isNaN(Number(value)))
×
UNCOV
1387
            throw new TypeORMError(`Value "${value}" is not a number.`)
×
1388

1389
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
UNCOV
1390
        const values: QueryDeepPartialEntity<Entity> = propertyPath
×
1391
            .split(".")
1392
            .reduceRight(
UNCOV
1393
                (value, key) => ({ [key]: value } as any),
×
1394
                () =>
UNCOV
1395
                    this.connection.driver.escape(column.databaseName) +
×
1396
                    " - " +
1397
                    value,
1398
            )
1399

UNCOV
1400
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
×
1401
            .update(entityClass)
1402
            .set(values)
1403
            .where(conditions)
1404
            .execute()
1405
    }
1406

1407
    /**
1408
     * Gets repository for the given entity class or name.
1409
     * If single database connection mode is used, then repository is obtained from the
1410
     * repository aggregator, where each repository is individually created for this entity manager.
1411
     * When single database connection is not used, repository is being obtained from the connection.
1412
     */
1413
    getRepository<Entity extends ObjectLiteral>(
1414
        target: EntityTarget<Entity>,
1415
    ): Repository<Entity> {
1416
        // find already created repository instance and return it if found
1417
        const repoFromMap = this.repositories.get(target)
88✔
1418
        if (repoFromMap) return repoFromMap
88✔
1419

1420
        // if repository was not found then create it, store its instance and return it
1421
        if (this.connection.driver.options.type === "mongodb") {
47!
1422
            const newRepository = new MongoRepository(
47✔
1423
                target,
1424
                this,
1425
                this.queryRunner,
1426
            )
1427
            this.repositories.set(target, newRepository)
47✔
1428
            return newRepository
47✔
1429
        } else {
UNCOV
1430
            const newRepository = new Repository<any>(
×
1431
                target,
1432
                this,
1433
                this.queryRunner,
1434
            )
UNCOV
1435
            this.repositories.set(target, newRepository)
×
UNCOV
1436
            return newRepository
×
1437
        }
1438
    }
1439

1440
    /**
1441
     * Gets tree repository for the given entity class or name.
1442
     * If single database connection mode is used, then repository is obtained from the
1443
     * repository aggregator, where each repository is individually created for this entity manager.
1444
     * When single database connection is not used, repository is being obtained from the connection.
1445
     */
1446
    getTreeRepository<Entity extends ObjectLiteral>(
1447
        target: EntityTarget<Entity>,
1448
    ): TreeRepository<Entity> {
1449
        // tree tables aren't supported by some drivers (mongodb)
UNCOV
1450
        if (this.connection.driver.treeSupport === false)
×
1451
            throw new TreeRepositoryNotSupportedError(this.connection.driver)
×
1452

1453
        // find already created repository instance and return it if found
UNCOV
1454
        const repository = this.treeRepositories.find(
×
UNCOV
1455
            (repository) => repository.target === target,
×
1456
        )
UNCOV
1457
        if (repository) return repository
×
1458

1459
        // check if repository is real tree repository
UNCOV
1460
        const newRepository = new TreeRepository(target, this, this.queryRunner)
×
UNCOV
1461
        this.treeRepositories.push(newRepository)
×
UNCOV
1462
        return newRepository
×
1463
    }
1464

1465
    /**
1466
     * Gets mongodb repository for the given entity class.
1467
     */
1468
    getMongoRepository<Entity extends ObjectLiteral>(
1469
        target: EntityTarget<Entity>,
1470
    ): MongoRepository<Entity> {
1471
        return this.connection.getMongoRepository<Entity>(target)
3✔
1472
    }
1473

1474
    /**
1475
     * Creates a new repository instance out of a given Repository and
1476
     * sets current EntityManager instance to it. Used to work with custom repositories
1477
     * in transactions.
1478
     */
1479
    withRepository<Entity extends ObjectLiteral, R extends Repository<any>>(
1480
        repository: R & Repository<Entity>,
1481
    ): R {
1482
        const repositoryConstructor =
UNCOV
1483
            repository.constructor as typeof Repository
×
1484
        const { target, manager, queryRunner, ...otherRepositoryProperties } =
UNCOV
1485
            repository
×
UNCOV
1486
        return Object.assign(
×
1487
            new repositoryConstructor(repository.target, this) as R,
1488
            {
1489
                ...otherRepositoryProperties,
1490
            },
1491
        )
1492
    }
1493

1494
    /**
1495
     * Gets custom entity repository marked with @EntityRepository decorator.
1496
     *
1497
     * @deprecated use Repository.extend to create custom repositories
1498
     */
1499
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
1500
        const entityRepositoryMetadataArgs =
UNCOV
1501
            getMetadataArgsStorage().entityRepositories.find((repository) => {
×
UNCOV
1502
                return (
×
1503
                    repository.target ===
1504
                    (typeof customRepository === "function"
×
1505
                        ? customRepository
1506
                        : (customRepository as any).constructor)
1507
                )
1508
            })
UNCOV
1509
        if (!entityRepositoryMetadataArgs)
×
1510
            throw new CustomRepositoryNotFoundError(customRepository)
×
1511

UNCOV
1512
        const entityMetadata = entityRepositoryMetadataArgs.entity
×
1513
            ? this.connection.getMetadata(entityRepositoryMetadataArgs.entity)
1514
            : undefined
1515
        const entityRepositoryInstance =
UNCOV
1516
            new (entityRepositoryMetadataArgs.target as any)(
×
1517
                this,
1518
                entityMetadata,
1519
            )
1520

1521
        // NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
1522
        // however we need these properties for internal work of the class
UNCOV
1523
        if (entityRepositoryInstance instanceof AbstractRepository) {
×
UNCOV
1524
            if (!(entityRepositoryInstance as any)["manager"])
×
UNCOV
1525
                (entityRepositoryInstance as any)["manager"] = this
×
1526
        } else {
1527
            if (!entityMetadata)
×
1528
                throw new CustomRepositoryCannotInheritRepositoryError(
×
1529
                    customRepository,
1530
                )
1531
            ;(entityRepositoryInstance as any)["manager"] = this
×
1532
            ;(entityRepositoryInstance as any)["metadata"] = entityMetadata
×
1533
        }
1534

UNCOV
1535
        return entityRepositoryInstance
×
1536
    }
1537

1538
    /**
1539
     * Releases all resources used by entity manager.
1540
     * This is used when entity manager is created with a single query runner,
1541
     * and this single query runner needs to be released after job with entity manager is done.
1542
     */
1543
    async release(): Promise<void> {
1544
        if (!this.queryRunner) throw new NoNeedToReleaseEntityManagerError()
×
1545

1546
        return this.queryRunner.release()
×
1547
    }
1548
}
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