• 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

94.22
/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts
1
import { Driver } from "../../driver/Driver"
2
import { RelationIdLoadResult } from "../relation-id/RelationIdLoadResult"
3
import { ObjectLiteral } from "../../common/ObjectLiteral"
4
import { ColumnMetadata } from "../../metadata/ColumnMetadata"
5
import { Alias } from "../Alias"
6
import { RelationCountLoadResult } from "../relation-count/RelationCountLoadResult"
7
import { RelationMetadata } from "../../metadata/RelationMetadata"
8
import { OrmUtils } from "../../util/OrmUtils"
4✔
9
import { QueryExpressionMap } from "../QueryExpressionMap"
10
import { EntityMetadata } from "../../metadata/EntityMetadata"
11
import { QueryRunner } from "../.."
12
import { DriverUtils } from "../../driver/DriverUtils"
4✔
13
import { ObjectUtils } from "../../util/ObjectUtils"
4✔
14

15
/**
16
 * Transforms raw sql results returned from the database into entity object.
17
 * Entity is constructed based on its entity metadata.
18
 */
19
export class RawSqlResultsToEntityTransformer {
4✔
20
    /**
21
     * Contains a hashmap for every rawRelationIdResults given.
22
     * In the hashmap you will find the idMaps of a result under the hash of this.hashEntityIds for the result.
23
     */
24
    private relationIdMaps: Array<{ [idHash: string]: any[] }>
25

26
    private pojo: boolean
27
    private selections: Set<string>
28
    private aliasCache: Map<string, Map<string, string>>
29
    private columnsCache: Map<
30
        string,
31
        Map<EntityMetadata, [string, ColumnMetadata][]>
32
    >
33

34
    // -------------------------------------------------------------------------
35
    // Constructor
36
    // -------------------------------------------------------------------------
37

38
    constructor(
39
        protected expressionMap: QueryExpressionMap,
8,295✔
40
        protected driver: Driver,
8,295✔
41
        protected rawRelationIdResults: RelationIdLoadResult[],
8,295✔
42
        protected rawRelationCountResults: RelationCountLoadResult[],
8,295✔
43
        protected queryRunner?: QueryRunner,
8,295✔
44
    ) {
45
        this.pojo = this.expressionMap.options.includes("create-pojo")
8,295✔
46
        this.selections = new Set(
8,295✔
47
            this.expressionMap.selects.map((s) => s.selection),
13,252✔
48
        )
49
        this.aliasCache = new Map()
8,295✔
50
        this.columnsCache = new Map()
8,295✔
51
    }
52

53
    // -------------------------------------------------------------------------
54
    // Public Methods
55
    // -------------------------------------------------------------------------
56

57
    /**
58
     * Since db returns a duplicated rows of the data where accuracies of the same object can be duplicated
59
     * we need to group our result and we must have some unique id (primary key in our case)
60
     */
61
    transform(rawResults: any[], alias: Alias): any[] {
62
        const group = this.group(rawResults, alias)
14,199✔
63
        const entities: any[] = []
14,199✔
64
        for (const results of group.values()) {
14,199✔
65
            const entity = this.transformRawResultsGroup(results, alias)
24,278✔
66
            if (entity !== undefined) entities.push(entity)
24,278✔
67
        }
68
        return entities
14,199✔
69
    }
70

71
    // -------------------------------------------------------------------------
72
    // Protected Methods
73
    // -------------------------------------------------------------------------
74

75
    /**
76
     * Build an alias from a name and column name.
77
     */
78
    protected buildAlias(aliasName: string, columnName: string) {
79
        let aliases = this.aliasCache.get(aliasName)
111,329✔
80
        if (!aliases) {
111,329✔
81
            aliases = new Map()
11,165✔
82
            this.aliasCache.set(aliasName, aliases)
11,165✔
83
        }
84
        let columnAlias = aliases.get(columnName)
111,329✔
85
        if (!columnAlias) {
111,329✔
86
            columnAlias = DriverUtils.buildAlias(
31,216✔
87
                this.driver,
88
                undefined,
89
                aliasName,
90
                columnName,
91
            )
92
            aliases.set(columnName, columnAlias)
31,216✔
93
        }
94
        return columnAlias
111,329✔
95
    }
96

97
    /**
98
     * Groups given raw results by ids of given alias.
99
     */
100
    protected group(rawResults: any[], alias: Alias): Map<string, any[]> {
101
        const map = new Map()
14,199✔
102
        const keys: string[] = []
14,199✔
103
        if (alias.metadata.tableType === "view") {
14,199✔
104
            keys.push(
19✔
105
                ...alias.metadata.columns.map((column) =>
106
                    this.buildAlias(alias.name, column.databaseName),
77✔
107
                ),
108
            )
109
        } else {
110
            keys.push(
14,180✔
111
                ...alias.metadata.primaryColumns.map((column) =>
112
                    this.buildAlias(alias.name, column.databaseName),
15,717✔
113
                ),
114
            )
115
        }
116
        for (const rawResult of rawResults) {
14,199✔
117
            const id = keys
46,415✔
118
                .map((key) => {
119
                    const keyValue = rawResult[key]
49,072✔
120

121
                    if (Buffer.isBuffer(keyValue)) {
49,072!
122
                        return keyValue.toString("hex")
×
123
                    }
124

125
                    if (ObjectUtils.isObject(keyValue)) {
49,072!
126
                        return JSON.stringify(keyValue)
×
127
                    }
128

129
                    return keyValue
49,072✔
130
                })
131
                .join("_") // todo: check partial
132

133
            const items = map.get(id)
46,415✔
134
            if (!items) {
46,415✔
135
                map.set(id, [rawResult])
24,278✔
136
            } else {
137
                items.push(rawResult)
22,137✔
138
            }
139
        }
140
        return map
14,199✔
141
    }
142

143
    /**
144
     * Transforms set of data results into single entity.
145
     */
146
    protected transformRawResultsGroup(
147
        rawResults: any[],
148
        alias: Alias,
149
    ): ObjectLiteral | undefined {
150
        // let hasColumns = false; // , hasEmbeddedColumns = false, hasParentColumns = false, hasParentEmbeddedColumns = false;
151
        let metadata = alias.metadata
24,278✔
152

153
        if (metadata.discriminatorColumn) {
24,278✔
154
            const discriminatorValues = rawResults.map(
333✔
155
                (result) =>
156
                    result[
405✔
157
                        this.buildAlias(
158
                            alias.name,
159
                            alias.metadata.discriminatorColumn!.databaseName,
160
                        )
161
                    ],
162
            )
163
            const discriminatorMetadata = metadata.childEntityMetadatas.find(
333✔
164
                (childEntityMetadata) => {
165
                    return (
326✔
166
                        typeof discriminatorValues.find(
167
                            (value) =>
168
                                value ===
390✔
169
                                childEntityMetadata.discriminatorValue,
170
                        ) !== "undefined"
171
                    )
172
                },
173
            )
174
            if (discriminatorMetadata) metadata = discriminatorMetadata
333✔
175
        }
176
        const entity: any = metadata.create(this.queryRunner, {
24,278✔
177
            fromDeserializer: true,
178
            pojo: this.pojo,
179
        })
180

181
        // get value from columns selections and put them into newly created entity
182
        const hasColumns = this.transformColumns(
24,278✔
183
            rawResults,
184
            alias,
185
            entity,
186
            metadata,
187
        )
188
        const hasRelations = this.transformJoins(
24,278✔
189
            rawResults,
190
            entity,
191
            alias,
192
            metadata,
193
        )
194
        const hasRelationIds = this.transformRelationIds(
24,278✔
195
            rawResults,
196
            alias,
197
            entity,
198
            metadata,
199
        )
200
        const hasRelationCounts = this.transformRelationCounts(
24,278✔
201
            rawResults,
202
            alias,
203
            entity,
204
        )
205

206
        // if we have at least one selected column then return this entity
207
        // since entity must have at least primary columns to be really selected and transformed into entity
208
        if (hasColumns) return entity
24,278✔
209

210
        // if we don't have any selected column we should not return entity,
211
        // except for the case when entity only contain a primary column as a relation to another entity
212
        // in this case its absolutely possible our entity to not have any columns except a single relation
213
        const hasOnlyVirtualPrimaryColumns = metadata.primaryColumns.every(
3,052✔
214
            (column) => column.isVirtual === true,
3,052✔
215
        ) // todo: create metadata.hasOnlyVirtualPrimaryColumns
216
        if (
3,052!
217
            hasOnlyVirtualPrimaryColumns &&
3,052!
218
            (hasRelations || hasRelationIds || hasRelationCounts)
219
        )
220
            return entity
×
221

222
        return undefined
3,052✔
223
    }
224

225
    // get value from columns selections and put them into object
226
    protected transformColumns(
227
        rawResults: any[],
228
        alias: Alias,
229
        entity: ObjectLiteral,
230
        metadata: EntityMetadata,
231
    ): boolean {
232
        let hasData = false
24,278✔
233
        const result = rawResults[0]
24,278✔
234
        for (const [key, column] of this.getColumnsToProcess(
24,278✔
235
            alias.name,
236
            metadata,
237
        )) {
238
            const value = result[key]
64,506✔
239

240
            if (value === undefined) continue
64,506✔
241
            // we don't mark it as has data because if we will have all nulls in our object - we don't need such object
242
            else if (value !== null && !column.isVirtualProperty) hasData = true
64,490✔
243

244
            column.setEntityValue(
64,490✔
245
                entity,
246
                this.driver.prepareHydratedValue(value, column),
247
            )
248
        }
249
        return hasData
24,278✔
250
    }
251

252
    /**
253
     * Transforms joined entities in the given raw results by a given alias and stores to the given (parent) entity
254
     */
255
    protected transformJoins(
256
        rawResults: any[],
257
        entity: ObjectLiteral,
258
        alias: Alias,
259
        metadata: EntityMetadata,
260
    ) {
261
        let hasData = false
24,278✔
262

263
        // let discriminatorValue: string = "";
264
        // if (metadata.discriminatorColumn)
265
        //     discriminatorValue = rawResults[0][this.buildAlias(alias.name, alias.metadata.discriminatorColumn!.databaseName)];
266

267
        for (const join of this.expressionMap.joinAttributes) {
24,278✔
268
            // todo: we have problem here - when inner joins are used without selects it still create empty array
269

270
            // skip joins without metadata
271
            if (!join.metadata) continue
20,627✔
272

273
            // if simple left or inner join was performed without selection then we don't need to do anything
274
            if (!join.isSelected) continue
20,599✔
275

276
            // this check need to avoid setting properties than not belong to entity when single table inheritance used. (todo: check if we still need it)
277
            // const metadata = metadata.childEntityMetadatas.find(childEntityMetadata => discriminatorValue === childEntityMetadata.discriminatorValue);
278
            if (
18,851✔
279
                join.relation &&
37,022✔
280
                !metadata.relations.find(
281
                    (relation) => relation === join.relation,
24,723✔
282
                )
283
            )
284
                continue
11,607✔
285

286
            // some checks to make sure this join is for current alias
287
            if (join.mapToProperty) {
7,244✔
288
                if (join.mapToPropertyParentAlias !== alias.name) continue
936✔
289
            } else {
290
                if (
6,308✔
291
                    !join.relation ||
18,256✔
292
                    join.parentAlias !== alias.name ||
293
                    join.relationPropertyPath !== join.relation!.propertyPath
294
                )
295
                    continue
668✔
296
            }
297

298
            // transform joined data into entities
299
            let result: any = this.transform(rawResults, join.alias)
5,904✔
300
            result = !join.isMany ? result[0] : result
5,904✔
301
            result = !join.isMany && result === undefined ? null : result // this is needed to make relations to return null when its joined but nothing was found in the database
5,904✔
302
            // if nothing was joined then simply continue
303
            if (result === undefined) continue
5,904!
304

305
            // if join was mapped to some property then save result to that property
306
            if (join.mapToPropertyPropertyName) {
5,904✔
307
                entity[join.mapToPropertyPropertyName] = result // todo: fix embeds
264✔
308
            } else {
309
                // otherwise set to relation
310
                join.relation!.setEntityValue(entity, result)
5,640✔
311
            }
312

313
            hasData = true
5,904✔
314
        }
315
        return hasData
24,278✔
316
    }
317

318
    protected transformRelationIds(
319
        rawSqlResults: any[],
320
        alias: Alias,
321
        entity: ObjectLiteral,
322
        metadata: EntityMetadata,
323
    ): boolean {
324
        let hasData = false
24,278✔
325
        for (const [
24,278✔
326
            index,
327
            rawRelationIdResult,
328
        ] of this.rawRelationIdResults.entries()) {
329
            if (
6,157✔
330
                rawRelationIdResult.relationIdAttribute.parentAlias !==
331
                alias.name
332
            )
333
                continue
3,025✔
334

335
            const relation = rawRelationIdResult.relationIdAttribute.relation
3,132✔
336
            const valueMap = this.createValueMapFromJoinColumns(
3,132✔
337
                relation,
338
                rawRelationIdResult.relationIdAttribute.parentAlias,
339
                rawSqlResults,
340
            )
341
            if (valueMap === undefined || valueMap === null) {
3,132!
342
                continue
×
343
            }
344

345
            // prepare common data for this call
346
            this.prepareDataForTransformRelationIds()
3,132✔
347

348
            // Extract idMaps from prepared data by hash
349
            const hash = this.hashEntityIds(relation, valueMap)
3,132✔
350
            const idMaps = this.relationIdMaps[index][hash] || []
3,132✔
351

352
            // Map data to properties
353
            const properties =
354
                rawRelationIdResult.relationIdAttribute.mapToPropertyPropertyPath.split(
3,132✔
355
                    ".",
356
                )
357
            const mapToProperty = (
3,132✔
358
                properties: string[],
359
                map: ObjectLiteral,
360
                value: any,
361
            ): any => {
362
                const property = properties.shift()
3,436✔
363
                if (property && properties.length === 0) {
3,436✔
364
                    map[property] = value
3,132✔
365
                    return map
3,132✔
366
                }
367
                if (property && properties.length > 0) {
304!
368
                    mapToProperty(properties, map[property], value)
304✔
369
                } else {
370
                    return map
×
371
                }
372
            }
373
            if (relation.isOneToOne || relation.isManyToOne) {
3,132✔
374
                if (idMaps[0] !== undefined) {
1,463✔
375
                    mapToProperty(properties, entity, idMaps[0])
1,463✔
376
                    hasData = true
1,463✔
377
                }
378
            } else {
379
                mapToProperty(properties, entity, idMaps)
1,669✔
380
                hasData = hasData || idMaps.length > 0
1,669✔
381
            }
382
        }
383

384
        return hasData
24,278✔
385
    }
386

387
    protected transformRelationCounts(
388
        rawSqlResults: any[],
389
        alias: Alias,
390
        entity: ObjectLiteral,
391
    ): boolean {
392
        let hasData = false
24,278✔
393
        for (const rawRelationCountResult of this.rawRelationCountResults) {
24,278✔
394
            if (
1,580✔
395
                rawRelationCountResult.relationCountAttribute.parentAlias !==
396
                alias.name
397
            )
398
                continue
584✔
399
            const relation =
400
                rawRelationCountResult.relationCountAttribute.relation
996✔
401
            let referenceColumnName: string
402

403
            if (relation.isOneToMany) {
996✔
404
                referenceColumnName =
132✔
405
                    relation.inverseRelation!.joinColumns[0].referencedColumn!
406
                        .databaseName // todo: fix joinColumns[0]
407
            } else {
408
                referenceColumnName = relation.isOwning
864✔
409
                    ? relation.joinColumns[0].referencedColumn!.databaseName
410
                    : relation.inverseRelation!.joinColumns[0].referencedColumn!
411
                          .databaseName
412
            }
413

414
            const referenceColumnValue =
415
                rawSqlResults[0][
996✔
416
                    this.buildAlias(alias.name, referenceColumnName)
417
                ] // we use zero index since its grouped data // todo: selection with alias for entity columns wont work
418
            if (
996✔
419
                referenceColumnValue !== undefined &&
1,992✔
420
                referenceColumnValue !== null
421
            ) {
422
                entity[
996✔
423
                    rawRelationCountResult.relationCountAttribute.mapToPropertyPropertyName
424
                ] = 0
425
                for (const result of rawRelationCountResult.results) {
996✔
426
                    if (result["parentId"] !== referenceColumnValue) continue
1,332✔
427
                    entity[
560✔
428
                        rawRelationCountResult.relationCountAttribute.mapToPropertyPropertyName
429
                    ] = parseInt(result["cnt"])
430
                    hasData = true
560✔
431
                }
432
            }
433
        }
434

435
        return hasData
24,278✔
436
    }
437

438
    private getColumnsToProcess(aliasName: string, metadata: EntityMetadata) {
439
        let metadatas = this.columnsCache.get(aliasName)
24,278✔
440
        if (!metadatas) {
24,278✔
441
            metadatas = new Map()
11,165✔
442
            this.columnsCache.set(aliasName, metadatas)
11,165✔
443
        }
444
        let columns = metadatas.get(metadata)
24,278✔
445
        if (!columns) {
24,278✔
446
            columns = metadata.columns
11,240✔
447
                .filter(
448
                    (column) =>
449
                        !column.isVirtual &&
43,037✔
450
                        // if user does not selected the whole entity or he used partial selection and does not select this particular column
451
                        // then we don't add this column and its value into the entity
452
                        (this.selections.has(aliasName) ||
453
                            this.selections.has(
454
                                `${aliasName}.${column.propertyPath}`,
455
                            )) &&
456
                        // if table inheritance is used make sure this column is not child's column
457
                        !metadata.childEntityMetadatas.some(
458
                            (childMetadata) =>
459
                                childMetadata.target === column.target,
140✔
460
                        ),
461
                )
462
                .map((column) => [
31,177✔
463
                    this.buildAlias(aliasName, column.databaseName),
464
                    column,
465
                ])
466
            metadatas.set(metadata, columns)
11,240✔
467
        }
468
        return columns
24,278✔
469
    }
470

471
    private createValueMapFromJoinColumns(
472
        relation: RelationMetadata,
473
        parentAlias: string,
474
        rawSqlResults: any[],
475
    ): ObjectLiteral {
476
        let columns: ColumnMetadata[]
477
        if (relation.isManyToOne || relation.isOneToOneOwner) {
3,132✔
478
            columns = relation.entityMetadata.primaryColumns.map(
1,391✔
479
                (joinColumn) => joinColumn,
1,657✔
480
            )
481
        } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
1,741✔
482
            columns = relation.inverseRelation!.joinColumns.map(
798✔
483
                (joinColumn) => joinColumn,
960✔
484
            )
485
        } else {
486
            if (relation.isOwning) {
943✔
487
                columns = relation.joinColumns.map((joinColumn) => joinColumn)
825✔
488
            } else {
489
                columns = relation.inverseRelation!.inverseJoinColumns.map(
290✔
490
                    (joinColumn) => joinColumn,
394✔
491
                )
492
            }
493
        }
494
        return columns.reduce((valueMap, column) => {
3,132✔
495
            for (const rawSqlResult of rawSqlResults) {
3,836✔
496
                if (relation.isManyToOne || relation.isOneToOneOwner) {
62,957✔
497
                    valueMap[column.databaseName] =
32,383✔
498
                        this.driver.prepareHydratedValue(
499
                            rawSqlResult[
500
                                this.buildAlias(
501
                                    parentAlias,
502
                                    column.databaseName,
503
                                )
504
                            ],
505
                            column,
506
                        )
507
                } else {
508
                    valueMap[column.databaseName] =
30,574✔
509
                        this.driver.prepareHydratedValue(
510
                            rawSqlResult[
511
                                this.buildAlias(
512
                                    parentAlias,
513
                                    column.referencedColumn!.databaseName,
514
                                )
515
                            ],
516
                            column.referencedColumn!,
517
                        )
518
                }
519
            }
520
            return valueMap
3,836✔
521
        }, {} as ObjectLiteral)
522
    }
523

524
    private extractEntityPrimaryIds(
525
        relation: RelationMetadata,
526
        relationIdRawResult: any,
527
    ) {
528
        let columns: ColumnMetadata[]
529
        if (relation.isManyToOne || relation.isOneToOneOwner) {
6,538✔
530
            columns = relation.entityMetadata.primaryColumns.map(
2,782✔
531
                (joinColumn) => joinColumn,
3,314✔
532
            )
533
        } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
3,756✔
534
            columns = relation.inverseRelation!.joinColumns.map(
1,852✔
535
                (joinColumn) => joinColumn,
2,264✔
536
            )
537
        } else {
538
            if (relation.isOwning) {
1,904✔
539
                columns = relation.joinColumns.map((joinColumn) => joinColumn)
1,740✔
540
            } else {
541
                columns = relation.inverseRelation!.inverseJoinColumns.map(
624✔
542
                    (joinColumn) => joinColumn,
884✔
543
                )
544
            }
545
        }
546
        return columns.reduce((data, column) => {
6,538✔
547
            data[column.databaseName] = relationIdRawResult[column.databaseName]
8,202✔
548
            return data
8,202✔
549
        }, {} as ObjectLiteral)
550
    }
551

552
    /*private removeVirtualColumns(entity: ObjectLiteral, alias: Alias) {
553
        const virtualColumns = this.expressionMap.selects
554
            .filter(select => select.virtual)
555
            .map(select => select.selection.replace(alias.name + ".", ""));
556

557
        virtualColumns.forEach(virtualColumn => delete entity[virtualColumn]);
558
    }*/
559

560
    /** Prepare data to run #transformRelationIds, as a lot of result independent data is needed in every call */
561
    private prepareDataForTransformRelationIds() {
562
        // Return early if the relationIdMaps were already calculated
563
        if (this.relationIdMaps) {
3,132✔
564
            return
2,188✔
565
        }
566

567
        // Ensure this prepare function is only called once
568
        this.relationIdMaps = this.rawRelationIdResults.map(
944✔
569
            (rawRelationIdResult) => {
570
                const relation =
571
                    rawRelationIdResult.relationIdAttribute.relation
1,462✔
572

573
                // Calculate column metadata
574
                let columns: ColumnMetadata[]
575
                if (relation.isManyToOne || relation.isOneToOneOwner) {
1,462✔
576
                    columns = relation.joinColumns
375✔
577
                } else if (
1,087✔
578
                    relation.isOneToMany ||
1,794✔
579
                    relation.isOneToOneNotOwner
580
                ) {
581
                    columns = relation.inverseEntityMetadata.primaryColumns
428✔
582
                } else {
583
                    // ManyToMany
584
                    if (relation.isOwning) {
659✔
585
                        columns = relation.inverseJoinColumns
477✔
586
                    } else {
587
                        columns = relation.inverseRelation!.joinColumns
182✔
588
                    }
589
                }
590

591
                // Calculate the idMaps for the rawRelationIdResult
592
                return rawRelationIdResult.results.reduce((agg, result) => {
1,462✔
593
                    let idMap = columns.reduce((idMap, column) => {
3,406✔
594
                        let value = result[column.databaseName]
4,247✔
595
                        if (
4,247✔
596
                            relation.isOneToMany ||
7,308✔
597
                            relation.isOneToOneNotOwner
598
                        ) {
599
                            if (
1,306!
600
                                column.isVirtual &&
1,306!
601
                                column.referencedColumn &&
602
                                column.referencedColumn.propertyName !==
603
                                    column.propertyName
604
                            ) {
605
                                // if column is a relation
606
                                value =
×
607
                                    column.referencedColumn.createValueMap(
608
                                        value,
609
                                    )
610
                            }
611

612
                            return OrmUtils.mergeDeep(
1,306✔
613
                                idMap,
614
                                column.createValueMap(value),
615
                            )
616
                        }
617
                        if (
2,941!
618
                            !column.isPrimary &&
4,459✔
619
                            column.referencedColumn!.referencedColumn
620
                        ) {
621
                            // if column is a relation
622
                            value =
×
623
                                column.referencedColumn!.referencedColumn!.createValueMap(
624
                                    value,
625
                                )
626
                        }
627

628
                        return OrmUtils.mergeDeep(
2,941✔
629
                            idMap,
630
                            column.referencedColumn!.createValueMap(value),
631
                        )
632
                    }, {} as ObjectLiteral)
633

634
                    if (
3,406✔
635
                        columns.length === 1 &&
6,061✔
636
                        !rawRelationIdResult.relationIdAttribute.disableMixedMap
637
                    ) {
638
                        if (
2,287✔
639
                            relation.isOneToMany ||
3,918✔
640
                            relation.isOneToOneNotOwner
641
                        ) {
642
                            idMap = columns[0].getEntityValue(idMap)
680✔
643
                        } else {
644
                            idMap =
1,607✔
645
                                columns[0].referencedColumn!.getEntityValue(
646
                                    idMap,
647
                                )
648
                        }
649
                    }
650

651
                    // If an idMap is found, set it in the aggregator under the correct hash
652
                    if (idMap !== undefined) {
3,406✔
653
                        const hash = this.hashEntityIds(relation, result)
3,406✔
654

655
                        if (agg[hash]) {
3,406✔
656
                            agg[hash].push(idMap)
938✔
657
                        } else {
658
                            agg[hash] = [idMap]
2,468✔
659
                        }
660
                    }
661

662
                    return agg
3,406✔
663
                }, {})
664
            },
665
        )
666
    }
667

668
    /**
669
     * Use a simple JSON.stringify to create a simple hash of the primary ids of an entity.
670
     * As this.extractEntityPrimaryIds always creates the primary id object in the same order, if the same relation is
671
     * given, a simple JSON.stringify should be enough to get a unique hash per entity!
672
     */
673
    private hashEntityIds(relation: RelationMetadata, data: ObjectLiteral) {
674
        const entityPrimaryIds = this.extractEntityPrimaryIds(relation, data)
6,538✔
675
        return JSON.stringify(entityPrimaryIds)
6,538✔
676
    }
677
}
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