• 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

1.53
/src/query-builder/RelationIdLoader.ts
1
import { RelationMetadata } from "../metadata/RelationMetadata"
2
import { ColumnMetadata } from "../metadata/ColumnMetadata"
3
import { DataSource } from "../data-source/DataSource"
4
import { ObjectLiteral } from "../common/ObjectLiteral"
5
import { SelectQueryBuilder } from "./SelectQueryBuilder"
6
import { DriverUtils } from "../driver/DriverUtils"
4✔
7
import { QueryRunner } from "../query-runner/QueryRunner"
8

9
/**
10
 * Loads relation ids for the given entities.
11
 */
12
export class RelationIdLoader {
4✔
13
    // -------------------------------------------------------------------------
14
    // Constructor
15
    // -------------------------------------------------------------------------
16

17
    constructor(
18
        private connection: DataSource,
1,852✔
19
        protected queryRunner?: QueryRunner | undefined,
1,852✔
20
    ) {}
21

22
    // -------------------------------------------------------------------------
23
    // Public Methods
24
    // -------------------------------------------------------------------------
25

26
    /**
27
     * Loads relation ids of the given entity or entities.
28
     */
29
    load(
30
        relation: RelationMetadata,
31
        entityOrEntities: ObjectLiteral | ObjectLiteral[],
32
        relatedEntityOrRelatedEntities?: ObjectLiteral | ObjectLiteral[],
33
    ): Promise<any[]> {
34
        const entities = Array.isArray(entityOrEntities)
×
35
            ? entityOrEntities
36
            : [entityOrEntities]
37
        const relatedEntities = Array.isArray(relatedEntityOrRelatedEntities)
×
38
            ? relatedEntityOrRelatedEntities
39
            : relatedEntityOrRelatedEntities
×
40
            ? [relatedEntityOrRelatedEntities]
41
            : undefined
42

43
        // load relation ids depend of relation type
44
        if (relation.isManyToMany) {
×
45
            return this.loadForManyToMany(relation, entities, relatedEntities)
×
46
        } else if (relation.isManyToOne || relation.isOneToOneOwner) {
×
47
            return this.loadForManyToOneAndOneToOneOwner(
×
48
                relation,
49
                entities,
50
                relatedEntities,
51
            )
52
        } else {
53
            // if (relation.isOneToMany || relation.isOneToOneNotOwner) {
54
            return this.loadForOneToManyAndOneToOneNotOwner(
×
55
                relation,
56
                entities,
57
                relatedEntities,
58
            )
59
        }
60
    }
61

62
    /**
63
     * Loads relation ids of the given entities and groups them into the object with parent and children.
64
     *
65
     * todo: extract this method?
66
     */
67
    async loadManyToManyRelationIdsAndGroup<
68
        E1 extends ObjectLiteral,
69
        E2 extends ObjectLiteral,
70
    >(
71
        relation: RelationMetadata,
72
        entitiesOrEntities: E1 | E1[],
73
        relatedEntityOrEntities?: E2 | E2[],
74
        queryBuilder?: SelectQueryBuilder<any>,
75
    ): Promise<{ entity: E1; related?: E2 | E2[] }[]> {
76
        // console.log("relation:", relation.propertyName);
77
        // console.log("entitiesOrEntities", entitiesOrEntities);
78
        const isMany = relation.isManyToMany || relation.isOneToMany
×
79
        const entities: E1[] = Array.isArray(entitiesOrEntities)
×
80
            ? entitiesOrEntities
81
            : [entitiesOrEntities]
82

83
        if (!relatedEntityOrEntities) {
×
84
            relatedEntityOrEntities = await this.connection.relationLoader.load(
×
85
                relation,
86
                entitiesOrEntities,
87
                this.queryRunner,
88
                queryBuilder,
89
            )
90
            if (!relatedEntityOrEntities.length)
×
91
                return entities.map((entity) => ({
×
92
                    entity: entity,
93
                    related: isMany ? [] : undefined,
×
94
                }))
95
        }
96
        // const relationIds = await this.load(relation, relatedEntityOrEntities!, entitiesOrEntities);
97
        const relationIds = await this.load(
×
98
            relation,
99
            entitiesOrEntities,
100
            relatedEntityOrEntities,
101
        )
102
        // console.log("entities", entities);
103
        // console.log("relatedEntityOrEntities", relatedEntityOrEntities);
104
        // console.log("relationIds", relationIds);
105

106
        const relatedEntities: E2[] = Array.isArray(relatedEntityOrEntities)
×
107
            ? relatedEntityOrEntities
108
            : [relatedEntityOrEntities!]
109

110
        let columns: ColumnMetadata[] = [],
×
111
            inverseColumns: ColumnMetadata[] = []
×
112
        if (relation.isManyToManyOwner) {
×
113
            columns = relation.junctionEntityMetadata!.inverseColumns.map(
×
114
                (column) => column.referencedColumn!,
×
115
            )
116
            inverseColumns = relation.junctionEntityMetadata!.ownerColumns.map(
×
117
                (column) => column.referencedColumn!,
×
118
            )
119
        } else if (relation.isManyToManyNotOwner) {
×
120
            columns = relation.junctionEntityMetadata!.ownerColumns.map(
×
121
                (column) => column.referencedColumn!,
×
122
            )
123
            inverseColumns =
×
124
                relation.junctionEntityMetadata!.inverseColumns.map(
125
                    (column) => column.referencedColumn!,
×
126
                )
127
        } else if (relation.isManyToOne || relation.isOneToOneOwner) {
×
128
            columns = relation.joinColumns.map(
×
129
                (column) => column.referencedColumn!,
×
130
            )
131
            inverseColumns = relation.entityMetadata.primaryColumns
×
132
        } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
×
133
            columns = relation.inverseRelation!.entityMetadata.primaryColumns
×
134
            inverseColumns = relation.inverseRelation!.joinColumns.map(
×
135
                (column) => column.referencedColumn!,
×
136
            )
137
        } else {
138
        }
139

140
        return entities.map((entity) => {
×
141
            const group: { entity: E1; related?: E2 | E2[] } = {
×
142
                entity: entity,
143
                related: isMany ? [] : undefined,
×
144
            }
145

146
            const entityRelationIds = relationIds.filter((relationId) => {
×
147
                return inverseColumns.every((column) => {
×
148
                    return column.compareEntityValue(
×
149
                        entity,
150
                        relationId[
151
                            column.entityMetadata.name +
152
                                "_" +
153
                                column.propertyAliasName
154
                        ],
155
                    )
156
                })
157
            })
158
            if (!entityRelationIds.length) return group
×
159

160
            relatedEntities.forEach((relatedEntity) => {
×
161
                entityRelationIds.forEach((relationId) => {
×
162
                    const relatedEntityMatched = columns.every((column) => {
×
163
                        return column.compareEntityValue(
×
164
                            relatedEntity,
165
                            relationId[
166
                                DriverUtils.buildAlias(
167
                                    this.connection.driver,
168
                                    undefined,
169
                                    column.entityMetadata.name +
170
                                        "_" +
171
                                        relation.propertyPath.replace(
172
                                            ".",
173
                                            "_",
174
                                        ) +
175
                                        "_" +
176
                                        column.propertyPath.replace(".", "_"),
177
                                )
178
                            ],
179
                        )
180
                    })
181
                    if (relatedEntityMatched) {
×
182
                        if (isMany) {
×
183
                            ;(group.related as E2[]).push(relatedEntity)
×
184
                        } else {
185
                            group.related = relatedEntity
×
186
                        }
187
                    }
188
                })
189
            })
190
            return group
×
191
        })
192
    }
193

194
    /**
195
     * Loads relation ids of the given entities and maps them into the given entity property.
196
     async loadManyToManyRelationIdsAndMap(
197
     relation: RelationMetadata,
198
     entityOrEntities: ObjectLiteral|ObjectLiteral[],
199
     mapToEntityOrEntities: ObjectLiteral|ObjectLiteral[],
200
     propertyName: string
201
     ): Promise<void> {
202
        const relationIds = await this.loadManyToManyRelationIds(relation, entityOrEntities, mapToEntityOrEntities);
203
        const mapToEntities = mapToEntityOrEntities instanceof Array ? mapToEntityOrEntities : [mapToEntityOrEntities];
204
        const junctionMetadata = relation.junctionEntityMetadata!;
205
        const mainAlias = junctionMetadata.name;
206
        const columns = relation.isOwning ? junctionMetadata.inverseColumns : junctionMetadata.ownerColumns;
207
        const inverseColumns = relation.isOwning ? junctionMetadata.ownerColumns : junctionMetadata.inverseColumns;
208
        mapToEntities.forEach(mapToEntity => {
209
            mapToEntity[propertyName] = [];
210
            relationIds.forEach(relationId => {
211
                const match = inverseColumns.every(column => {
212
                    return column.referencedColumn!.getEntityValue(mapToEntity) === relationId[mainAlias + "_" + column.propertyName];
213
                });
214
                if (match) {
215
                    if (columns.length === 1) {
216
                        mapToEntity[propertyName].push(relationId[mainAlias + "_" + columns[0].propertyName]);
217
                    } else {
218
                        const value = {};
219
                        columns.forEach(column => {
220
                            column.referencedColumn!.setEntityValue(value, relationId[mainAlias + "_" + column.propertyName]);
221
                        });
222
                        mapToEntity[propertyName].push(value);
223
                    }
224
                }
225
            });
226
        });
227
    }*/
228

229
    // -------------------------------------------------------------------------
230
    // Protected Methods
231
    // -------------------------------------------------------------------------
232

233
    /**
234
     * Loads relation ids for the many-to-many relation.
235
     */
236
    protected loadForManyToMany(
237
        relation: RelationMetadata,
238
        entities: ObjectLiteral[],
239
        relatedEntities?: ObjectLiteral[],
240
    ) {
241
        const junctionMetadata = relation.junctionEntityMetadata!
×
242
        const mainAlias = junctionMetadata.name
×
243
        const columns = relation.isOwning
×
244
            ? junctionMetadata.ownerColumns
245
            : junctionMetadata.inverseColumns
246
        const inverseColumns = relation.isOwning
×
247
            ? junctionMetadata.inverseColumns
248
            : junctionMetadata.ownerColumns
249
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
250

251
        // select all columns from junction table
252
        columns.forEach((column) => {
×
253
            const columnName = DriverUtils.buildAlias(
×
254
                this.connection.driver,
255
                undefined,
256
                column.referencedColumn!.entityMetadata.name +
257
                    "_" +
258
                    column.referencedColumn!.propertyPath.replace(".", "_"),
259
            )
260
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
261
        })
262
        inverseColumns.forEach((column) => {
×
263
            const columnName = DriverUtils.buildAlias(
×
264
                this.connection.driver,
265
                undefined,
266
                column.referencedColumn!.entityMetadata.name +
267
                    "_" +
268
                    relation.propertyPath.replace(".", "_") +
269
                    "_" +
270
                    column.referencedColumn!.propertyPath.replace(".", "_"),
271
            )
272
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
273
        })
274

275
        // add conditions for the given entities
276
        let condition1 = ""
×
277
        if (columns.length === 1) {
×
278
            const values = entities.map((entity) =>
×
279
                columns[0].referencedColumn!.getEntityValue(entity),
×
280
            )
281
            const areAllNumbers = values.every(
×
282
                (value) => typeof value === "number",
×
283
            )
284

285
            if (areAllNumbers) {
×
286
                condition1 = `${mainAlias}.${
×
287
                    columns[0].propertyPath
288
                } IN (${values.join(", ")})`
289
            } else {
290
                qb.setParameter("values1", values)
×
291
                condition1 =
×
292
                    mainAlias +
293
                    "." +
294
                    columns[0].propertyPath +
295
                    " IN (:...values1)" // todo: use ANY for postgres
296
            }
297
        } else {
298
            condition1 =
×
299
                "(" +
300
                entities
301
                    .map((entity, entityIndex) => {
302
                        return columns
×
303
                            .map((column) => {
304
                                const paramName =
305
                                    "entity1_" +
×
306
                                    entityIndex +
307
                                    "_" +
308
                                    column.propertyName
309
                                qb.setParameter(
×
310
                                    paramName,
311
                                    column.referencedColumn!.getEntityValue(
312
                                        entity,
313
                                    ),
314
                                )
315
                                return (
×
316
                                    mainAlias +
317
                                    "." +
318
                                    column.propertyPath +
319
                                    " = :" +
320
                                    paramName
321
                                )
322
                            })
323
                            .join(" AND ")
324
                    })
325
                    .map((condition) => "(" + condition + ")")
×
326
                    .join(" OR ") +
327
                ")"
328
        }
329

330
        // add conditions for the given inverse entities
331
        let condition2 = ""
×
332
        if (relatedEntities) {
×
333
            if (inverseColumns.length === 1) {
×
334
                const values = relatedEntities.map((entity) =>
×
335
                    inverseColumns[0].referencedColumn!.getEntityValue(entity),
×
336
                )
337
                const areAllNumbers = values.every(
×
338
                    (value) => typeof value === "number",
×
339
                )
340

341
                if (areAllNumbers) {
×
342
                    condition2 = `${mainAlias}.${
×
343
                        inverseColumns[0].propertyPath
344
                    } IN (${values.join(", ")})`
345
                } else {
346
                    qb.setParameter("values2", values)
×
347
                    condition2 =
×
348
                        mainAlias +
349
                        "." +
350
                        inverseColumns[0].propertyPath +
351
                        " IN (:...values2)" // todo: use ANY for postgres
352
                }
353
            } else {
354
                condition2 =
×
355
                    "(" +
356
                    relatedEntities
357
                        .map((entity, entityIndex) => {
358
                            return inverseColumns
×
359
                                .map((column) => {
360
                                    const paramName =
361
                                        "entity2_" +
×
362
                                        entityIndex +
363
                                        "_" +
364
                                        column.propertyName
365
                                    qb.setParameter(
×
366
                                        paramName,
367
                                        column.referencedColumn!.getEntityValue(
368
                                            entity,
369
                                        ),
370
                                    )
371
                                    return (
×
372
                                        mainAlias +
373
                                        "." +
374
                                        column.propertyPath +
375
                                        " = :" +
376
                                        paramName
377
                                    )
378
                                })
379
                                .join(" AND ")
380
                        })
381
                        .map((condition) => "(" + condition + ")")
×
382
                        .join(" OR ") +
383
                    ")"
384
            }
385
        }
386

387
        // qb.from(junctionMetadata.target, mainAlias)
388
        //     .where(condition1 + (condition2 ? " AND " + condition2 : ""));
389
        //
390
        // // execute query
391
        // const { values1, values2 } = qb.getParameters();
392
        // console.log(`I can do it`, { values1, values2 });
393
        // if (inverseColumns.length === 1 &&
394
        //     columns.length === 1 &&
395
        //     this.connection.driver instanceof SqliteDriver &&
396
        //     (values1.length + values2.length) > 500 &&
397
        //     values1.length === values2.length) {
398
        //     console.log(`I can do it`);
399
        //     return qb.getRawMany();
400
        //
401
        // } else {
402
        //     return qb.getRawMany();
403
        // }
404

405
        // execute query
406
        const condition = [condition1, condition2]
×
407
            .filter((v) => v.length > 0)
×
408
            .join(" AND ")
409
        return qb
×
410
            .from(junctionMetadata.target, mainAlias)
411
            .where(condition)
412
            .getRawMany()
413
    }
414

415
    /**
416
     * Loads relation ids for the many-to-one and one-to-one owner relations.
417
     */
418
    protected loadForManyToOneAndOneToOneOwner(
419
        relation: RelationMetadata,
420
        entities: ObjectLiteral[],
421
        relatedEntities?: ObjectLiteral[],
422
    ) {
423
        const mainAlias = relation.entityMetadata.targetName
×
424

425
        // console.log("entitiesx", entities);
426
        // console.log("relatedEntitiesx", relatedEntities);
427
        const hasAllJoinColumnsInEntity = relation.joinColumns.every(
×
428
            (joinColumn) => {
429
                return !!relation.entityMetadata.nonVirtualColumns.find(
×
430
                    (column) => column === joinColumn,
×
431
                )
432
            },
433
        )
434
        if (relatedEntities && hasAllJoinColumnsInEntity) {
×
435
            const relationIdMaps: ObjectLiteral[] = []
×
436
            entities.forEach((entity) => {
×
437
                const relationIdMap: ObjectLiteral = {}
×
438
                relation.entityMetadata.primaryColumns.forEach(
×
439
                    (primaryColumn) => {
440
                        const key =
441
                            primaryColumn.entityMetadata.name +
×
442
                            "_" +
443
                            primaryColumn.propertyPath.replace(".", "_")
444
                        relationIdMap[key] =
×
445
                            primaryColumn.getEntityValue(entity)
446
                    },
447
                )
448

449
                relatedEntities.forEach((relatedEntity) => {
×
450
                    relation.joinColumns.forEach((joinColumn) => {
×
451
                        const entityColumnValue =
452
                            joinColumn.getEntityValue(entity)
×
453
                        const relatedEntityColumnValue =
454
                            joinColumn.referencedColumn!.getEntityValue(
×
455
                                relatedEntity,
456
                            )
457
                        if (
×
458
                            entityColumnValue === undefined ||
×
459
                            relatedEntityColumnValue === undefined
460
                        )
461
                            return
×
462

463
                        if (entityColumnValue === relatedEntityColumnValue) {
×
464
                            const key =
465
                                joinColumn.referencedColumn!.entityMetadata
×
466
                                    .name +
467
                                "_" +
468
                                relation.propertyPath.replace(".", "_") +
469
                                "_" +
470
                                joinColumn.referencedColumn!.propertyPath.replace(
471
                                    ".",
472
                                    "_",
473
                                )
474
                            relationIdMap[key] = relatedEntityColumnValue
×
475
                        }
476
                    })
477
                })
478
                if (
×
479
                    Object.keys(relationIdMap).length ===
480
                    relation.entityMetadata.primaryColumns.length +
481
                        relation.joinColumns.length
482
                ) {
483
                    relationIdMaps.push(relationIdMap)
×
484
                }
485
            })
486
            // console.log("relationIdMap", relationIdMaps);
487
            // console.log("entities.length", entities.length);
488
            if (relationIdMaps.length === entities.length)
×
489
                return Promise.resolve(relationIdMaps)
×
490
        }
491

492
        // select all columns we need
493
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
494
        relation.entityMetadata.primaryColumns.forEach((primaryColumn) => {
×
495
            const columnName = DriverUtils.buildAlias(
×
496
                this.connection.driver,
497
                undefined,
498
                primaryColumn.entityMetadata.name +
499
                    "_" +
500
                    primaryColumn.propertyPath.replace(".", "_"),
501
            )
502
            qb.addSelect(
×
503
                mainAlias + "." + primaryColumn.propertyPath,
504
                columnName,
505
            )
506
        })
507
        relation.joinColumns.forEach((column) => {
×
508
            const columnName = DriverUtils.buildAlias(
×
509
                this.connection.driver,
510
                undefined,
511
                column.referencedColumn!.entityMetadata.name +
512
                    "_" +
513
                    relation.propertyPath.replace(".", "_") +
514
                    "_" +
515
                    column.referencedColumn!.propertyPath.replace(".", "_"),
516
            )
517
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
518
        })
519

520
        // add condition for entities
521
        let condition: string = ""
×
522
        if (relation.entityMetadata.primaryColumns.length === 1) {
×
523
            const values = entities.map((entity) =>
×
524
                relation.entityMetadata.primaryColumns[0].getEntityValue(
×
525
                    entity,
526
                ),
527
            )
528
            const areAllNumbers = values.every(
×
529
                (value) => typeof value === "number",
×
530
            )
531

532
            if (areAllNumbers) {
×
533
                condition = `${mainAlias}.${
×
534
                    relation.entityMetadata.primaryColumns[0].propertyPath
535
                } IN (${values.join(", ")})`
536
            } else {
537
                qb.setParameter("values", values)
×
538
                condition =
×
539
                    mainAlias +
540
                    "." +
541
                    relation.entityMetadata.primaryColumns[0].propertyPath +
542
                    " IN (:...values)" // todo: use ANY for postgres
543
            }
544
        } else {
545
            condition = entities
×
546
                .map((entity, entityIndex) => {
547
                    return relation.entityMetadata.primaryColumns
×
548
                        .map((column, columnIndex) => {
549
                            const paramName =
550
                                "entity" + entityIndex + "_" + columnIndex
×
551
                            qb.setParameter(
×
552
                                paramName,
553
                                column.getEntityValue(entity),
554
                            )
555
                            return (
×
556
                                mainAlias +
557
                                "." +
558
                                column.propertyPath +
559
                                " = :" +
560
                                paramName
561
                            )
562
                        })
563
                        .join(" AND ")
564
                })
565
                .map((condition) => "(" + condition + ")")
×
566
                .join(" OR ")
567
        }
568

569
        // execute query
570
        return qb
×
571
            .from(relation.entityMetadata.target, mainAlias)
572
            .where(condition)
573
            .getRawMany()
574
    }
575

576
    /**
577
     * Loads relation ids for the one-to-many and one-to-one not owner relations.
578
     */
579
    protected loadForOneToManyAndOneToOneNotOwner(
580
        relation: RelationMetadata,
581
        entities: ObjectLiteral[],
582
        relatedEntities?: ObjectLiteral[],
583
    ) {
584
        const originalRelation = relation
×
585
        relation = relation.inverseRelation!
×
586

587
        if (
×
588
            relation.entityMetadata.primaryColumns.length ===
589
            relation.joinColumns.length
590
        ) {
591
            const sameReferencedColumns =
592
                relation.entityMetadata.primaryColumns.every((column) => {
×
593
                    return relation.joinColumns.indexOf(column) !== -1
×
594
                })
595
            if (sameReferencedColumns) {
×
596
                return Promise.resolve(
×
597
                    entities.map((entity) => {
598
                        const result: ObjectLiteral = {}
×
599
                        relation.joinColumns.forEach(function (joinColumn) {
×
600
                            const value =
601
                                joinColumn.referencedColumn!.getEntityValue(
×
602
                                    entity,
603
                                )
604
                            const joinColumnName =
605
                                joinColumn.referencedColumn!.entityMetadata
×
606
                                    .name +
607
                                "_" +
608
                                joinColumn.referencedColumn!.propertyPath.replace(
609
                                    ".",
610
                                    "_",
611
                                )
612
                            const primaryColumnName =
613
                                joinColumn.entityMetadata.name +
×
614
                                "_" +
615
                                originalRelation.propertyPath.replace(
616
                                    ".",
617
                                    "_",
618
                                ) +
619
                                "_" +
620
                                joinColumn.propertyPath.replace(".", "_")
621
                            result[joinColumnName] = value
×
622
                            result[primaryColumnName] = value
×
623
                        })
624
                        return result
×
625
                    }),
626
                )
627
            }
628
        }
629

630
        const mainAlias = relation.entityMetadata.targetName
×
631

632
        // select all columns we need
633
        const qb = this.connection.createQueryBuilder(this.queryRunner)
×
634
        relation.entityMetadata.primaryColumns.forEach((primaryColumn) => {
×
635
            const columnName = DriverUtils.buildAlias(
×
636
                this.connection.driver,
637
                undefined,
638
                primaryColumn.entityMetadata.name +
639
                    "_" +
640
                    originalRelation.propertyPath.replace(".", "_") +
641
                    "_" +
642
                    primaryColumn.propertyPath.replace(".", "_"),
643
            )
644
            qb.addSelect(
×
645
                mainAlias + "." + primaryColumn.propertyPath,
646
                columnName,
647
            )
648
        })
649
        relation.joinColumns.forEach((column) => {
×
650
            const columnName = DriverUtils.buildAlias(
×
651
                this.connection.driver,
652
                undefined,
653
                column.referencedColumn!.entityMetadata.name +
654
                    "_" +
655
                    column.referencedColumn!.propertyPath.replace(".", "_"),
656
            )
657
            qb.addSelect(mainAlias + "." + column.propertyPath, columnName)
×
658
        })
659

660
        // add condition for entities
661
        let condition: string = ""
×
662
        if (relation.joinColumns.length === 1) {
×
663
            const values = entities.map((entity) =>
×
664
                relation.joinColumns[0].referencedColumn!.getEntityValue(
×
665
                    entity,
666
                ),
667
            )
668
            const areAllNumbers = values.every(
×
669
                (value) => typeof value === "number",
×
670
            )
671

672
            if (areAllNumbers) {
×
673
                condition = `${mainAlias}.${
×
674
                    relation.joinColumns[0].propertyPath
675
                } IN (${values.join(", ")})`
676
            } else {
677
                qb.setParameter("values", values)
×
678
                condition =
×
679
                    mainAlias +
680
                    "." +
681
                    relation.joinColumns[0].propertyPath +
682
                    " IN (:...values)" // todo: use ANY for postgres
683
            }
684
        } else {
685
            condition = entities
×
686
                .map((entity, entityIndex) => {
687
                    return relation.joinColumns
×
688
                        .map((joinColumn, joinColumnIndex) => {
689
                            const paramName =
690
                                "entity" + entityIndex + "_" + joinColumnIndex
×
691
                            qb.setParameter(
×
692
                                paramName,
693
                                joinColumn.referencedColumn!.getEntityValue(
694
                                    entity,
695
                                ),
696
                            )
697
                            return (
×
698
                                mainAlias +
699
                                "." +
700
                                joinColumn.propertyPath +
701
                                " = :" +
702
                                paramName
703
                            )
704
                        })
705
                        .join(" AND ")
706
                })
707
                .map((condition) => "(" + condition + ")")
×
708
                .join(" OR ")
709
        }
710

711
        // execute query
712
        return qb
×
713
            .from(relation.entityMetadata.target, mainAlias)
714
            .where(condition)
715
            .getRawMany()
716
    }
717
}
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