• 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

89.52
/src/metadata/EntityMetadata.ts
1
import { QueryRunner, SelectQueryBuilder } from ".."
2
import { ObjectLiteral } from "../common/ObjectLiteral"
3
import { DataSource } from "../data-source/DataSource"
4
import { CannotCreateEntityIdMapError } from "../error/CannotCreateEntityIdMapError"
4✔
5
import { OrderByCondition } from "../find-options/OrderByCondition"
6
import { TableMetadataArgs } from "../metadata-args/TableMetadataArgs"
7
import { TreeMetadataArgs } from "../metadata-args/TreeMetadataArgs"
8
import { OrmUtils } from "../util/OrmUtils"
4✔
9
import { CheckMetadata } from "./CheckMetadata"
10
import { ColumnMetadata } from "./ColumnMetadata"
11
import { EmbeddedMetadata } from "./EmbeddedMetadata"
12
import { EntityListenerMetadata } from "./EntityListenerMetadata"
13
import { ExclusionMetadata } from "./ExclusionMetadata"
14
import { ForeignKeyMetadata } from "./ForeignKeyMetadata"
15
import { IndexMetadata } from "./IndexMetadata"
16
import { RelationCountMetadata } from "./RelationCountMetadata"
17
import { RelationIdMetadata } from "./RelationIdMetadata"
18
import { RelationMetadata } from "./RelationMetadata"
19
import { TableType } from "./types/TableTypes"
20
import { TreeType } from "./types/TreeTypes"
21
import { UniqueMetadata } from "./UniqueMetadata"
22
import { ClosureTreeOptions } from "./types/ClosureTreeOptions"
23
import { EntityPropertyNotFoundError } from "../error/EntityPropertyNotFoundError"
4✔
24
import { ObjectUtils } from "../util/ObjectUtils"
4✔
25
import { shorten } from "../util/StringUtils"
4✔
26

27
/**
28
 * Contains all entity metadata.
29
 */
30
export class EntityMetadata {
4✔
31
    readonly "@instanceof" = Symbol.for("EntityMetadata")
4,776✔
32

33
    // -------------------------------------------------------------------------
34
    // Properties
35
    // -------------------------------------------------------------------------
36

37
    /**
38
     * Connection where this entity metadata is created.
39
     */
40
    connection: DataSource
41

42
    /**
43
     * Metadata arguments used to build this entity metadata.
44
     */
45
    tableMetadataArgs: TableMetadataArgs
46

47
    /**
48
     * If entity's table is a closure-typed table, then this entity will have a closure junction table metadata.
49
     */
50
    closureJunctionTable: EntityMetadata
51

52
    /**
53
     * If this is entity metadata for a junction closure table then its owner closure table metadata will be set here.
54
     */
55
    parentClosureEntityMetadata: EntityMetadata
56

57
    /**
58
     * Parent's entity metadata. Used in inheritance patterns.
59
     */
60
    parentEntityMetadata: EntityMetadata
61

62
    /**
63
     * Children entity metadatas. Used in inheritance patterns.
64
     */
65
    childEntityMetadatas: EntityMetadata[] = []
4,776✔
66

67
    /**
68
     * All "inheritance tree" from a target entity.
69
     * For example for target Post < ContentModel < Unit it will be an array of [Post, ContentModel, Unit].
70
     * It also contains child entities for single table inheritance.
71
     */
72
    inheritanceTree: Function[] = []
4,776✔
73

74
    /**
75
     * Table type. Tables can be closure, junction, etc.
76
     */
77
    tableType: TableType = "regular"
4,776✔
78

79
    /**
80
     * Target class to which this entity metadata is bind.
81
     * Note, that when using table inheritance patterns target can be different rather then table's target.
82
     * For virtual tables which lack of real entity (like junction tables) target is equal to their table name.
83
     */
84
    target: Function | string
85

86
    /**
87
     * Gets the name of the target.
88
     */
89
    targetName: string
90

91
    /**
92
     * Entity's name.
93
     * Equal to entity target class's name if target is set to table.
94
     * If target class is not then then it equals to table name.
95
     */
96
    name: string
97

98
    /**
99
     * View's expression.
100
     * Used in views
101
     */
102
    expression?: string | ((connection: DataSource) => SelectQueryBuilder<any>)
103

104
    /**
105
     * View's dependencies.
106
     * Used in views
107
     */
108
    dependsOn?: Set<Function | string>
109

110
    /**
111
     * Enables Sqlite "WITHOUT ROWID" modifier for the "CREATE TABLE" statement
112
     */
113
    withoutRowid?: boolean = false
4,776✔
114

115
    /**
116
     * Original user-given table name (taken from schema or @Entity(tableName) decorator).
117
     * If user haven't specified a table name this property will be undefined.
118
     */
119
    givenTableName?: string
120

121
    /**
122
     * Entity table name in the database.
123
     * This is final table name of the entity.
124
     * This name already passed naming strategy, and generated based on
125
     * multiple criteria, including user table name and global table prefix.
126
     */
127
    tableName: string
128

129
    /**
130
     * Entity table path. Contains database name, schema name and table name.
131
     * E.g. myDB.mySchema.myTable
132
     */
133
    tablePath: string
134

135
    /**
136
     * Gets the table name without global table prefix.
137
     * When querying table you need a table name with prefix, but in some scenarios,
138
     * for example when you want to name a junction table that contains names of two other tables,
139
     * you may want a table name without prefix.
140
     */
141
    tableNameWithoutPrefix: string
142

143
    /**
144
     * Indicates if schema will be synchronized for this entity or not.
145
     */
146
    synchronize: boolean = true
4,776✔
147

148
    /**
149
     * Table's database engine type (like "InnoDB", "MyISAM", etc).
150
     */
151
    engine?: string
152

153
    /**
154
     * Database name.
155
     */
156
    database?: string
157

158
    /**
159
     * Schema name. Used in Postgres and Sql Server.
160
     */
161
    schema?: string
162

163
    /**
164
     * Specifies a default order by used for queries from this table when no explicit order by is specified.
165
     */
166
    orderBy?: OrderByCondition
167

168
    /**
169
     * If this entity metadata's table using one of the inheritance patterns,
170
     * then this will contain what pattern it uses.
171
     */
172
    inheritancePattern?: "STI" /*|"CTI"*/
173

174
    /**
175
     * Checks if there any non-nullable column exist in this entity.
176
     */
177
    hasNonNullableRelations: boolean = false
4,776✔
178

179
    /**
180
     * Indicates if this entity metadata of a junction table, or not.
181
     * Junction table is a table created by many-to-many relationship.
182
     *
183
     * Its also possible to understand if entity is junction via tableType.
184
     */
185
    isJunction: boolean = false
4,776✔
186

187
    /**
188
     * Indicates if the entity should be instantiated using the constructor
189
     * or via allocating a new object via `Object.create()`.
190
     */
191
    isAlwaysUsingConstructor: boolean = true
4,776✔
192

193
    /**
194
     * Indicates if this entity is a tree, what type of tree it is.
195
     */
196
    treeType?: TreeType
197

198
    /**
199
     * Indicates if this entity is a tree, what options of tree it has.
200
     */
201
    treeOptions?: ClosureTreeOptions
202

203
    /**
204
     * Checks if this table is a junction table of the closure table.
205
     * This type is for tables that contain junction metadata of the closure tables.
206
     */
207
    isClosureJunction: boolean = false
4,776✔
208

209
    /**
210
     * Checks if entity's table has multiple primary columns.
211
     */
212
    hasMultiplePrimaryKeys: boolean = false
4,776✔
213

214
    /**
215
     * Indicates if this entity metadata has uuid generated columns.
216
     */
217
    hasUUIDGeneratedColumns: boolean = false
4,776✔
218

219
    /**
220
     * If this entity metadata is a child table of some table, it should have a discriminator value.
221
     * Used to store a value in a discriminator column.
222
     */
223
    discriminatorValue?: string
224

225
    /**
226
     * Entity's column metadatas defined by user.
227
     */
228
    ownColumns: ColumnMetadata[] = []
4,776✔
229

230
    /**
231
     * Columns of the entity, including columns that are coming from the embeddeds of this entity.
232
     */
233
    columns: ColumnMetadata[] = []
4,776✔
234

235
    /**
236
     * Ancestor columns used only in closure junction tables.
237
     */
238
    ancestorColumns: ColumnMetadata[] = []
4,776✔
239

240
    /**
241
     * Descendant columns used only in closure junction tables.
242
     */
243
    descendantColumns: ColumnMetadata[] = []
4,776✔
244

245
    /**
246
     * All columns except for virtual columns.
247
     */
248
    nonVirtualColumns: ColumnMetadata[] = []
4,776✔
249

250
    /**
251
     * In the case if this entity metadata is junction table's entity metadata,
252
     * this will contain all referenced columns of owner entity.
253
     */
254
    ownerColumns: ColumnMetadata[] = []
4,776✔
255

256
    /**
257
     * In the case if this entity metadata is junction table's entity metadata,
258
     * this will contain all referenced columns of inverse entity.
259
     */
260
    inverseColumns: ColumnMetadata[] = []
4,776✔
261

262
    /**
263
     * Gets the column with generated flag.
264
     */
265
    generatedColumns: ColumnMetadata[] = []
4,776✔
266

267
    /**
268
     * Gets the object id column used with mongodb database.
269
     */
270
    objectIdColumn?: ColumnMetadata
271

272
    /**
273
     * Gets entity column which contains a create date value.
274
     */
275
    createDateColumn?: ColumnMetadata
276

277
    /**
278
     * Gets entity column which contains an update date value.
279
     */
280
    updateDateColumn?: ColumnMetadata
281

282
    /**
283
     * Gets entity column which contains a delete date value.
284
     */
285
    deleteDateColumn?: ColumnMetadata
286

287
    /**
288
     * Gets entity column which contains an entity version.
289
     */
290
    versionColumn?: ColumnMetadata
291

292
    /**
293
     * Gets the discriminator column used to store entity identificator in single-table inheritance tables.
294
     */
295
    discriminatorColumn?: ColumnMetadata
296

297
    /**
298
     * Special column that stores tree level in tree entities.
299
     */
300
    treeLevelColumn?: ColumnMetadata
301

302
    /**
303
     * Nested set's left value column.
304
     * Used only in tree entities with nested set pattern applied.
305
     */
306
    nestedSetLeftColumn?: ColumnMetadata
307

308
    /**
309
     * Nested set's right value column.
310
     * Used only in tree entities with nested set pattern applied.
311
     */
312
    nestedSetRightColumn?: ColumnMetadata
313

314
    /**
315
     * Materialized path column.
316
     * Used only in tree entities with materialized path pattern applied.
317
     */
318
    materializedPathColumn?: ColumnMetadata
319

320
    /**
321
     * Gets the primary columns.
322
     */
323
    primaryColumns: ColumnMetadata[] = []
4,776✔
324

325
    /**
326
     * Entity's relation metadatas.
327
     */
328
    ownRelations: RelationMetadata[] = []
4,776✔
329

330
    /**
331
     * Relations of the entity, including relations that are coming from the embeddeds of this entity.
332
     */
333
    relations: RelationMetadata[] = []
4,776✔
334

335
    /**
336
     * List of eager relations this metadata has.
337
     */
338
    eagerRelations: RelationMetadata[] = []
4,776✔
339

340
    /**
341
     * List of eager relations this metadata has.
342
     */
343
    lazyRelations: RelationMetadata[] = []
4,776✔
344

345
    /**
346
     * Gets only one-to-one relations of the entity.
347
     */
348
    oneToOneRelations: RelationMetadata[] = []
4,776✔
349

350
    /**
351
     * Gets only owner one-to-one relations of the entity.
352
     */
353
    ownerOneToOneRelations: RelationMetadata[] = []
4,776✔
354

355
    /**
356
     * Gets only one-to-many relations of the entity.
357
     */
358
    oneToManyRelations: RelationMetadata[] = []
4,776✔
359

360
    /**
361
     * Gets only many-to-one relations of the entity.
362
     */
363
    manyToOneRelations: RelationMetadata[] = []
4,776✔
364

365
    /**
366
     * Gets only many-to-many relations of the entity.
367
     */
368
    manyToManyRelations: RelationMetadata[] = []
4,776✔
369

370
    /**
371
     * Gets only owner many-to-many relations of the entity.
372
     */
373
    ownerManyToManyRelations: RelationMetadata[] = []
4,776✔
374

375
    /**
376
     * Gets only owner one-to-one and many-to-one relations.
377
     */
378
    relationsWithJoinColumns: RelationMetadata[] = []
4,776✔
379

380
    /**
381
     * Tree parent relation. Used only in tree-tables.
382
     */
383
    treeParentRelation?: RelationMetadata
384

385
    /**
386
     * Tree children relation. Used only in tree-tables.
387
     */
388
    treeChildrenRelation?: RelationMetadata
389

390
    /**
391
     * Entity's relation id metadatas.
392
     */
393
    relationIds: RelationIdMetadata[] = []
4,776✔
394

395
    /**
396
     * Entity's relation id metadatas.
397
     */
398
    relationCounts: RelationCountMetadata[] = []
4,776✔
399

400
    /**
401
     * Entity's foreign key metadatas.
402
     */
403
    foreignKeys: ForeignKeyMetadata[] = []
4,776✔
404

405
    /**
406
     * Entity's embedded metadatas.
407
     */
408
    embeddeds: EmbeddedMetadata[] = []
4,776✔
409

410
    /**
411
     * All embeddeds - embeddeds from this entity metadata and from all child embeddeds, etc.
412
     */
413
    allEmbeddeds: EmbeddedMetadata[] = []
4,776✔
414

415
    /**
416
     * Entity's own indices.
417
     */
418
    ownIndices: IndexMetadata[] = []
4,776✔
419

420
    /**
421
     * Entity's index metadatas.
422
     */
423
    indices: IndexMetadata[] = []
4,776✔
424

425
    /**
426
     * Entity's unique metadatas.
427
     */
428
    uniques: UniqueMetadata[] = []
4,776✔
429

430
    /**
431
     * Entity's own uniques.
432
     */
433
    ownUniques: UniqueMetadata[] = []
4,776✔
434

435
    /**
436
     * Entity's check metadatas.
437
     */
438
    checks: CheckMetadata[] = []
4,776✔
439

440
    /**
441
     * Entity's exclusion metadatas.
442
     */
443
    exclusions: ExclusionMetadata[] = []
4,776✔
444

445
    /**
446
     * Entity's own listener metadatas.
447
     */
448
    ownListeners: EntityListenerMetadata[] = []
4,776✔
449

450
    /**
451
     * Entity listener metadatas.
452
     */
453
    listeners: EntityListenerMetadata[] = []
4,776✔
454

455
    /**
456
     * Listener metadatas with "AFTER LOAD" type.
457
     */
458
    afterLoadListeners: EntityListenerMetadata[] = []
4,776✔
459

460
    /**
461
     * Listener metadatas with "BEFORE INSERT" type.
462
     */
463
    beforeInsertListeners: EntityListenerMetadata[] = []
4,776✔
464

465
    /**
466
     * Listener metadatas with "AFTER INSERT" type.
467
     */
468
    afterInsertListeners: EntityListenerMetadata[] = []
4,776✔
469

470
    /**
471
     * Listener metadatas with "BEFORE UPDATE" type.
472
     */
473
    beforeUpdateListeners: EntityListenerMetadata[] = []
4,776✔
474

475
    /**
476
     * Listener metadatas with "AFTER UPDATE" type.
477
     */
478
    afterUpdateListeners: EntityListenerMetadata[] = []
4,776✔
479

480
    /**
481
     * Listener metadatas with "BEFORE REMOVE" type.
482
     */
483
    beforeRemoveListeners: EntityListenerMetadata[] = []
4,776✔
484

485
    /**
486
     * Listener metadatas with "BEFORE SOFT REMOVE" type.
487
     */
488
    beforeSoftRemoveListeners: EntityListenerMetadata[] = []
4,776✔
489

490
    /**
491
     * Listener metadatas with "BEFORE RECOVER" type.
492
     */
493
    beforeRecoverListeners: EntityListenerMetadata[] = []
4,776✔
494

495
    /**
496
     * Listener metadatas with "AFTER REMOVE" type.
497
     */
498
    afterRemoveListeners: EntityListenerMetadata[] = []
4,776✔
499

500
    /**
501
     * Listener metadatas with "AFTER SOFT REMOVE" type.
502
     */
503
    afterSoftRemoveListeners: EntityListenerMetadata[] = []
4,776✔
504

505
    /**
506
     * Listener metadatas with "AFTER RECOVER" type.
507
     */
508
    afterRecoverListeners: EntityListenerMetadata[] = []
4,776✔
509

510
    /**
511
     * Map of columns and relations of the entity.
512
     *
513
     * example: Post{ id: number, name: string, counterEmbed: { count: number }, category: Category }.
514
     * This method will create following object:
515
     * { id: "id", counterEmbed: { count: "counterEmbed.count" }, category: "category" }
516
     */
517
    propertiesMap: ObjectLiteral
518

519
    /**
520
     * Table comment. Not supported by all database types.
521
     */
522
    comment?: string
523

524
    // ---------------------------------------------------------------------
525
    // Constructor
526
    // ---------------------------------------------------------------------
527

528
    constructor(options: {
529
        connection: DataSource
530
        inheritanceTree?: Function[]
531
        inheritancePattern?: "STI" /*|"CTI"*/
532
        tableTree?: TreeMetadataArgs
533
        parentClosureEntityMetadata?: EntityMetadata
534
        args: TableMetadataArgs
535
    }) {
536
        this.connection = options.connection
4,776✔
537
        this.inheritanceTree = options.inheritanceTree || []
4,776✔
538
        this.inheritancePattern = options.inheritancePattern
4,776✔
539
        this.treeType = options.tableTree ? options.tableTree.type : undefined
4,776✔
540
        this.treeOptions = options.tableTree
4,776✔
541
            ? options.tableTree.options
542
            : undefined
543
        this.parentClosureEntityMetadata = options.parentClosureEntityMetadata!
4,776✔
544
        this.tableMetadataArgs = options.args
4,776✔
545
        this.target = this.tableMetadataArgs.target
4,776✔
546
        this.tableType = this.tableMetadataArgs.type
4,776✔
547
        this.expression = this.tableMetadataArgs.expression
4,776✔
548
        this.withoutRowid = this.tableMetadataArgs.withoutRowid
4,776✔
549
        this.dependsOn = this.tableMetadataArgs.dependsOn
4,776✔
550
    }
551

552
    // -------------------------------------------------------------------------
553
    // Public Methods
554
    // -------------------------------------------------------------------------
555

556
    /**
557
     * Creates a new entity.
558
     */
559
    create(
560
        queryRunner?: QueryRunner,
561
        options?: { fromDeserializer?: boolean; pojo?: boolean },
562
    ): any {
563
        const pojo = options && options.pojo === true ? true : false
29,655✔
564
        // if target is set to a function (e.g. class) that can be created then create it
565
        let ret: any
566
        if (typeof this.target === "function" && !pojo) {
29,655✔
567
            if (!options?.fromDeserializer || this.isAlwaysUsingConstructor) {
27,491✔
568
                ret = new (<any>this.target)()
27,485✔
569
            } else {
570
                ret = Object.create(this.target.prototype)
6✔
571
            }
572
        } else {
573
            // otherwise simply return a new empty object
574
            ret = {}
2,164✔
575
        }
576

577
        // add "typename" property
578
        if (this.connection.options.typename) {
29,651!
579
            ret[this.connection.options.typename] = this.targetName
×
580
        }
581

582
        this.lazyRelations.forEach((relation) =>
29,651✔
583
            this.connection.relationLoader.enableLazyLoad(
218✔
584
                relation,
585
                ret,
586
                queryRunner,
587
            ),
588
        )
589
        return ret
29,651✔
590
    }
591

592
    /**
593
     * Checks if given entity has an id.
594
     */
595
    hasId(entity: ObjectLiteral): boolean {
596
        if (!entity) return false
52✔
597

598
        return this.primaryColumns.every((primaryColumn) => {
44✔
599
            const value = primaryColumn.getEntityValue(entity)
44✔
600
            return value !== null && value !== undefined && value !== ""
44✔
601
        })
602
    }
603

604
    /**
605
     * Checks if given entity / object contains ALL primary keys entity must have.
606
     * Returns true if it contains all of them, false if at least one of them is not defined.
607
     */
608
    hasAllPrimaryKeys(entity: ObjectLiteral): boolean {
609
        return this.primaryColumns.every((primaryColumn) => {
12✔
610
            const value = primaryColumn.getEntityValue(entity)
12✔
611
            return value !== null && value !== undefined
12✔
612
        })
613
    }
614

615
    /**
616
     * Ensures that given object is an entity id map.
617
     * If given id is an object then it means its already id map.
618
     * If given id isn't an object then it means its a value of the id column
619
     * and it creates a new id map with this value and name of the primary column.
620
     */
621
    ensureEntityIdMap(id: any): ObjectLiteral {
622
        if (ObjectUtils.isObject(id)) return id
28,675✔
623

624
        if (this.hasMultiplePrimaryKeys)
160!
625
            throw new CannotCreateEntityIdMapError(this, id)
×
626

627
        return this.primaryColumns[0].createValueMap(id)
160✔
628
    }
629

630
    /**
631
     * Gets primary keys of the entity and returns them in a literal object.
632
     * For example, for Post{ id: 1, title: "hello" } where id is primary it will return { id: 1 }
633
     * For multiple primary keys it returns multiple keys in object.
634
     * For primary keys inside embeds it returns complex object literal with keys in them.
635
     */
636
    getEntityIdMap(
637
        entity: ObjectLiteral | undefined,
638
    ): ObjectLiteral | undefined {
639
        if (!entity) return undefined
124,387✔
640

641
        return EntityMetadata.getValueMap(entity, this.primaryColumns, {
124,087✔
642
            skipNulls: true,
643
        })
644
    }
645

646
    /**
647
     * Creates a "mixed id map".
648
     * If entity has multiple primary keys (ids) then it will return just regular id map, like what getEntityIdMap returns.
649
     * But if entity has a single primary key then it will return just value of the id column of the entity, just value.
650
     * This is called mixed id map.
651
     */
652
    getEntityIdMixedMap(
653
        entity: ObjectLiteral | undefined,
654
    ): ObjectLiteral | undefined {
655
        if (!entity) return entity
1,326✔
656

657
        const idMap = this.getEntityIdMap(entity)
581✔
658
        if (this.hasMultiplePrimaryKeys) {
581✔
659
            return idMap
32✔
660
        } else if (idMap) {
549✔
661
            return this.primaryColumns[0].getEntityValue(idMap) // todo: what about parent primary column?
549✔
662
        }
663

664
        return idMap
×
665
    }
666

667
    /**
668
     * Compares two different entities by their ids.
669
     * Returns true if they match, false otherwise.
670
     */
671
    compareEntities(
672
        firstEntity: ObjectLiteral,
673
        secondEntity: ObjectLiteral,
674
    ): boolean {
675
        const firstEntityIdMap = this.getEntityIdMap(firstEntity)
11,422✔
676
        if (!firstEntityIdMap) return false
11,422✔
677

678
        const secondEntityIdMap = this.getEntityIdMap(secondEntity)
1,674✔
679
        if (!secondEntityIdMap) return false
1,674✔
680

681
        return OrmUtils.compareIds(firstEntityIdMap, secondEntityIdMap)
1,550✔
682
    }
683

684
    /**
685
     * Finds column with a given property name.
686
     */
687
    findColumnWithPropertyName(
688
        propertyName: string,
689
    ): ColumnMetadata | undefined {
690
        return this.columns.find(
77✔
691
            (column) => column.propertyName === propertyName,
204✔
692
        )
693
    }
694

695
    /**
696
     * Finds column with a given database name.
697
     */
698
    findColumnWithDatabaseName(
699
        databaseName: string,
700
    ): ColumnMetadata | undefined {
701
        return this.columns.find(
4,731✔
702
            (column) => column.databaseName === databaseName,
9,035✔
703
        )
704
    }
705

706
    /**
707
     * Checks if there is a column or relationship with a given property path.
708
     */
709
    hasColumnWithPropertyPath(propertyPath: string): boolean {
710
        const hasColumn = this.columns.some(
×
711
            (column) => column.propertyPath === propertyPath,
×
712
        )
713
        return hasColumn || this.hasRelationWithPropertyPath(propertyPath)
×
714
    }
715

716
    /**
717
     * Finds column with a given property path.
718
     */
719
    findColumnWithPropertyPath(
720
        propertyPath: string,
721
    ): ColumnMetadata | undefined {
722
        const column = this.columns.find(
245✔
723
            (column) => column.propertyPath === propertyPath,
495✔
724
        )
725
        if (column) return column
245✔
726

727
        // in the case if column with property path was not found, try to find a relation with such property path
728
        // if we find relation and it has a single join column then its the column user was seeking
729
        const relation = this.relations.find(
8✔
730
            (relation) => relation.propertyPath === propertyPath,
×
731
        )
732
        if (relation && relation.joinColumns.length === 1)
8!
733
            return relation.joinColumns[0]
×
734

735
        return undefined
8✔
736
    }
737

738
    /**
739
     * Finds column with a given property path.
740
     * Does not search in relation unlike findColumnWithPropertyPath.
741
     */
742
    findColumnWithPropertyPathStrict(
743
        propertyPath: string,
744
    ): ColumnMetadata | undefined {
745
        return this.columns.find(
3,674✔
746
            (column) => column.propertyPath === propertyPath,
6,653✔
747
        )
748
    }
749

750
    /**
751
     * Finds columns with a given property path.
752
     * Property path can match a relation, and relations can contain multiple columns.
753
     */
754
    findColumnsWithPropertyPath(propertyPath: string): ColumnMetadata[] {
755
        const column = this.columns.find(
26,989✔
756
            (column) => column.propertyPath === propertyPath,
40,598✔
757
        )
758
        if (column) return [column]
26,989✔
759

760
        // in the case if column with property path was not found, try to find a relation with such property path
761
        // if we find relation and it has a single join column then its the column user was seeking
762
        const relation = this.findRelationWithPropertyPath(propertyPath)
1,236✔
763
        if (relation && relation.joinColumns) return relation.joinColumns
1,236✔
764

765
        return []
20✔
766
    }
767

768
    /**
769
     * Checks if there is a relation with the given property path.
770
     */
771
    hasRelationWithPropertyPath(propertyPath: string): boolean {
772
        return this.relations.some(
1,218✔
773
            (relation) => relation.propertyPath === propertyPath,
1,556✔
774
        )
775
    }
776

777
    /**
778
     * Finds relation with the given property path.
779
     */
780
    findRelationWithPropertyPath(
781
        propertyPath: string,
782
    ): RelationMetadata | undefined {
783
        return this.relations.find(
378,818✔
784
            (relation) => relation.propertyPath === propertyPath,
479,890✔
785
        )
786
    }
787

788
    /**
789
     * Checks if there is an embedded with a given property path.
790
     */
791
    hasEmbeddedWithPropertyPath(propertyPath: string): boolean {
792
        return this.allEmbeddeds.some(
3,002✔
793
            (embedded) => embedded.propertyPath === propertyPath,
2,940✔
794
        )
795
    }
796

797
    /**
798
     * Finds embedded with a given property path.
799
     */
800
    findEmbeddedWithPropertyPath(
801
        propertyPath: string,
802
    ): EmbeddedMetadata | undefined {
803
        return this.allEmbeddeds.find(
6,128✔
804
            (embedded) => embedded.propertyPath === propertyPath,
1,057✔
805
        )
806
    }
807

808
    /**
809
     * Returns an array of databaseNames mapped from provided propertyPaths
810
     */
811
    mapPropertyPathsToColumns(propertyPaths: string[]) {
812
        return propertyPaths.map((propertyPath) => {
61✔
813
            const column = this.findColumnWithPropertyPath(propertyPath)
61✔
814
            if (column == null) {
61!
815
                throw new EntityPropertyNotFoundError(propertyPath, this)
×
816
            }
817
            return column
61✔
818
        })
819
    }
820

821
    /**
822
     * Iterates through entity and finds and extracts all values from relations in the entity.
823
     * If relation value is an array its being flattened.
824
     */
825
    extractRelationValuesFromEntity(
826
        entity: ObjectLiteral,
827
        relations: RelationMetadata[],
828
    ): [RelationMetadata, any, EntityMetadata][] {
829
        const relationsAndValues: [RelationMetadata, any, EntityMetadata][] = []
41,787✔
830
        relations.forEach((relation) => {
41,787✔
831
            const value = relation.getEntityValue(entity)
22,776✔
832
            if (Array.isArray(value)) {
22,776✔
833
                value.forEach((subValue) =>
4,917✔
834
                    relationsAndValues.push([
9,597✔
835
                        relation,
836
                        subValue,
837
                        EntityMetadata.getInverseEntityMetadata(
838
                            subValue,
839
                            relation,
840
                        ),
841
                    ]),
842
                )
843
            } else if (value) {
17,859✔
844
                relationsAndValues.push([
2,877✔
845
                    relation,
846
                    value,
847
                    EntityMetadata.getInverseEntityMetadata(value, relation),
848
                ])
849
            }
850
        })
851
        return relationsAndValues
41,787✔
852
    }
853

854
    /**
855
     * In the case of SingleTableInheritance, find the correct metadata
856
     * for a given value.
857
     *
858
     * @param value The value to find the metadata for.
859
     * @returns The found metadata for the entity or the base metadata if no matching metadata
860
     *          was found in the whole inheritance tree.
861
     */
862
    findInheritanceMetadata(value: any): EntityMetadata {
863
        // Check for single table inheritance and find the correct metadata in that case.
864
        // Goal is to use the correct discriminator as we could have a repository
865
        // for an (abstract) base class and thus the target would not match.
866

867
        if (
49,124✔
868
            this.inheritancePattern === "STI" &&
49,422✔
869
            this.childEntityMetadatas.length > 0
870
        ) {
871
            // There could be a column on the base class that can manually be set to override the type.
872
            let manuallySetDiscriminatorValue: unknown
873
            if (this.discriminatorColumn) {
110✔
874
                manuallySetDiscriminatorValue =
110✔
875
                    value[this.discriminatorColumn.propertyName]
876
            }
877
            return (
110✔
878
                this.childEntityMetadatas.find(
126✔
879
                    (meta) =>
880
                        manuallySetDiscriminatorValue ===
168✔
881
                            meta.discriminatorValue ||
882
                        value.constructor === meta.target,
883
                ) || this
884
            )
885
        }
886
        return this
49,014✔
887
    }
888

889
    // -------------------------------------------------------------------------
890
    // Private Static Methods
891
    // -------------------------------------------------------------------------
892

893
    private static getInverseEntityMetadata(
894
        value: any,
895
        relation: RelationMetadata,
896
    ): EntityMetadata {
897
        return relation.inverseEntityMetadata.findInheritanceMetadata(value)
12,474✔
898
    }
899

900
    // -------------------------------------------------------------------------
901
    // Public Static Methods
902
    // -------------------------------------------------------------------------
903

904
    /**
905
     * Creates a property paths for a given entity.
906
     *
907
     * @deprecated
908
     */
909
    static createPropertyPath(
910
        metadata: EntityMetadata,
911
        entity: ObjectLiteral,
912
        prefix: string = "",
×
913
    ) {
914
        const paths: string[] = []
×
915
        Object.keys(entity).forEach((key) => {
×
916
            // check for function is needed in the cases when createPropertyPath used on values contain a function as a value
917
            // example: .update().set({ name: () => `SUBSTR('', 1, 2)` })
918
            const parentPath = prefix ? prefix + "." + key : key
×
919
            if (metadata.hasEmbeddedWithPropertyPath(parentPath)) {
×
920
                const subPaths = this.createPropertyPath(
×
921
                    metadata,
922
                    entity[key],
923
                    parentPath,
924
                )
925
                paths.push(...subPaths)
×
926
            } else {
927
                const path = prefix ? prefix + "." + key : key
×
928
                paths.push(path)
×
929
            }
930
        })
931
        return paths
×
932
    }
933

934
    /**
935
     * Finds difference between two entity id maps.
936
     * Returns items that exist in the first array and absent in the second array.
937
     */
938
    static difference(
939
        firstIdMaps: ObjectLiteral[],
940
        secondIdMaps: ObjectLiteral[],
941
    ): ObjectLiteral[] {
942
        return firstIdMaps.filter((firstIdMap) => {
1,638✔
943
            return !secondIdMaps.find((secondIdMap) =>
190✔
944
                OrmUtils.compareIds(firstIdMap, secondIdMap),
186✔
945
            )
946
        })
947
    }
948

949
    /**
950
     * Creates value map from the given values and columns.
951
     * Examples of usages are primary columns map and join columns map.
952
     */
953
    static getValueMap(
954
        entity: ObjectLiteral,
955
        columns: ColumnMetadata[],
956
        options?: { skipNulls?: boolean },
957
    ): ObjectLiteral | undefined {
958
        return columns.reduce((map, column) => {
128,889✔
959
            const value = column.getEntityValueMap(entity, options)
145,831✔
960

961
            // make sure that none of the values of the columns are not missing
962
            if (map === undefined || value === null || value === undefined)
145,831✔
963
                return undefined
33,800✔
964

965
            return OrmUtils.mergeDeep(map, value)
112,031✔
966
        }, {} as ObjectLiteral | undefined)
967
    }
968

969
    // ---------------------------------------------------------------------
970
    // Public Builder Methods
971
    // ---------------------------------------------------------------------
972

973
    build() {
974
        const namingStrategy = this.connection.namingStrategy
4,776✔
975
        const entityPrefix = this.connection.options.entityPrefix
4,776✔
976
        const entitySkipConstructor =
977
            this.connection.options.entitySkipConstructor
4,776✔
978

979
        this.engine = this.tableMetadataArgs.engine
4,776✔
980
        this.database =
4,776✔
981
            this.tableMetadataArgs.type === "entity-child" &&
9,744✔
982
            this.parentEntityMetadata
983
                ? this.parentEntityMetadata.database
984
                : this.tableMetadataArgs.database
985
        if (this.tableMetadataArgs.schema) {
4,776✔
986
            this.schema = this.tableMetadataArgs.schema
2✔
987
        } else if (
4,774✔
988
            this.tableMetadataArgs.type === "entity-child" &&
4,966✔
989
            this.parentEntityMetadata
990
        ) {
991
            this.schema = this.parentEntityMetadata.schema
192✔
992
        } else if (this.connection.options?.hasOwnProperty("schema")) {
4,582!
993
            this.schema = (this.connection.options as any).schema
×
994
        }
995
        this.givenTableName =
4,776✔
996
            this.tableMetadataArgs.type === "entity-child" &&
9,744✔
997
            this.parentEntityMetadata
998
                ? this.parentEntityMetadata.givenTableName
999
                : this.tableMetadataArgs.name
1000
        this.synchronize =
4,776✔
1001
            this.tableMetadataArgs.synchronize === false ? false : true
4,776✔
1002
        this.targetName =
4,776✔
1003
            typeof this.tableMetadataArgs.target === "function"
4,776✔
1004
                ? (this.tableMetadataArgs.target as any).name
1005
                : this.tableMetadataArgs.target
1006
        if (this.tableMetadataArgs.type === "closure-junction") {
4,776✔
1007
            this.tableNameWithoutPrefix =
67✔
1008
                namingStrategy.closureJunctionTableName(this.givenTableName!)
1009
        } else if (
4,709✔
1010
            this.tableMetadataArgs.type === "entity-child" &&
4,901✔
1011
            this.parentEntityMetadata
1012
        ) {
1013
            this.tableNameWithoutPrefix = namingStrategy.tableName(
192✔
1014
                this.parentEntityMetadata.targetName,
1015
                this.parentEntityMetadata.givenTableName,
1016
            )
1017
        } else {
1018
            this.tableNameWithoutPrefix = namingStrategy.tableName(
4,517✔
1019
                this.targetName,
1020
                this.givenTableName,
1021
            )
1022

1023
            if (
4,517✔
1024
                this.tableMetadataArgs.type === "junction" &&
5,186✔
1025
                this.connection.driver.maxAliasLength &&
1026
                this.connection.driver.maxAliasLength > 0 &&
1027
                this.tableNameWithoutPrefix.length >
1028
                    this.connection.driver.maxAliasLength
1029
            ) {
1030
                // note: we are not using DriverUtils.buildAlias here because we would like to avoid
1031
                // hashed table names. However, current algorithm also isn't perfect, but we cannot
1032
                // change it, since it's a big breaking change. Planned to 0.4.0
1033
                this.tableNameWithoutPrefix = shorten(
13✔
1034
                    this.tableNameWithoutPrefix,
1035
                    { separator: "_", segmentLength: 3 },
1036
                )
1037
            }
1038
        }
1039
        this.tableName = entityPrefix
4,776!
1040
            ? namingStrategy.prefixTableName(
1041
                  entityPrefix,
1042
                  this.tableNameWithoutPrefix,
1043
              )
1044
            : this.tableNameWithoutPrefix
1045
        this.target = this.target ? this.target : this.tableName
4,776✔
1046
        this.name = this.targetName ? this.targetName : this.tableName
4,776✔
1047
        this.expression = this.tableMetadataArgs.expression
4,776✔
1048
        this.withoutRowid =
4,776✔
1049
            this.tableMetadataArgs.withoutRowid === true ? true : false
4,776✔
1050
        this.tablePath = this.connection.driver.buildTableName(
4,776✔
1051
            this.tableName,
1052
            this.schema,
1053
            this.database,
1054
        )
1055
        this.orderBy =
4,776✔
1056
            typeof this.tableMetadataArgs.orderBy === "function"
4,776!
1057
                ? this.tableMetadataArgs.orderBy(this.propertiesMap)
1058
                : this.tableMetadataArgs.orderBy // todo: is propertiesMap available here? Looks like its not
1059

1060
        if (entitySkipConstructor !== undefined) {
4,776✔
1061
            this.isAlwaysUsingConstructor = !entitySkipConstructor
9✔
1062
        }
1063

1064
        this.isJunction =
4,776✔
1065
            this.tableMetadataArgs.type === "closure-junction" ||
9,485✔
1066
            this.tableMetadataArgs.type === "junction"
1067
        this.isClosureJunction =
4,776✔
1068
            this.tableMetadataArgs.type === "closure-junction"
1069

1070
        this.comment = this.tableMetadataArgs.comment
4,776✔
1071
    }
1072

1073
    /**
1074
     * Registers a new column in the entity and recomputes all depend properties.
1075
     */
1076
    registerColumn(column: ColumnMetadata) {
1077
        if (this.ownColumns.indexOf(column) !== -1) return
1,365!
1078

1079
        this.ownColumns.push(column)
1,365✔
1080
        this.columns = this.embeddeds.reduce(
1,365✔
1081
            (columns, embedded) => columns.concat(embedded.columnsFromTree),
132✔
1082
            this.ownColumns,
1083
        )
1084
        this.primaryColumns = this.columns.filter((column) => column.isPrimary)
5,857✔
1085
        this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1
1,365✔
1086
        this.hasUUIDGeneratedColumns =
1,365✔
1087
            this.columns.filter(
1088
                (column) =>
1089
                    column.isGenerated || column.generationStrategy === "uuid",
5,857✔
1090
            ).length > 0
1091
        this.propertiesMap = this.createPropertiesMap()
1,365✔
1092
        if (this.childEntityMetadatas)
1,365✔
1093
            this.childEntityMetadatas.forEach((entityMetadata) =>
1,365✔
1094
                entityMetadata.registerColumn(column),
32✔
1095
            )
1096
    }
1097

1098
    /**
1099
     * Creates a special object - all columns and relations of the object (plus columns and relations from embeds)
1100
     * in a special format - { propertyName: propertyName }.
1101
     *
1102
     * example: Post{ id: number, name: string, counterEmbed: { count: number }, category: Category }.
1103
     * This method will create following object:
1104
     * { id: "id", counterEmbed: { count: "counterEmbed.count" }, category: "category" }
1105
     */
1106
    createPropertiesMap(): { [name: string]: string | any } {
1107
        const map: { [name: string]: string | any } = {}
9,266✔
1108
        this.columns.forEach((column) =>
9,266✔
1109
            OrmUtils.mergeDeep(map, column.createValueMap(column.propertyPath)),
29,584✔
1110
        )
1111
        this.relations.forEach((relation) =>
9,266✔
1112
            OrmUtils.mergeDeep(
8,958✔
1113
                map,
1114
                relation.createValueMap(relation.propertyPath),
1115
            ),
1116
        )
1117
        return map
9,266✔
1118
    }
1119

1120
    /**
1121
     * Checks if entity has any column which rely on returning data,
1122
     * e.g. columns with auto generated value, DEFAULT values considered as dependant of returning data.
1123
     * For example, if we need to have RETURNING after INSERT (or we need returned id for DBs not supporting RETURNING),
1124
     * it means we cannot execute bulk inserts in some cases.
1125
     */
1126
    getInsertionReturningColumns(): ColumnMetadata[] {
1127
        return this.columns.filter((column) => {
107,568✔
1128
            return (
327,482✔
1129
                column.default !== undefined ||
2,093,592✔
1130
                column.asExpression !== undefined ||
1131
                column.isGenerated ||
1132
                column.isCreateDate ||
1133
                column.isUpdateDate ||
1134
                column.isDeleteDate ||
1135
                column.isVersion
1136
            )
1137
        })
1138
    }
1139
}
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