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

typeorm / typeorm / 15219332477

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

Pull #11332

github

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

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

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

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

2.8
/src/query-builder/RelationUpdater.ts
1
import { QueryBuilder } from "./QueryBuilder"
2
import { ObjectLiteral } from "../common/ObjectLiteral"
3
import { QueryExpressionMap } from "./QueryExpressionMap"
4
import { TypeORMError } from "../error"
1✔
5
import { ObjectUtils } from "../util/ObjectUtils"
1✔
6

7
/**
8
 * Allows to work with entity relations and perform specific operations with those relations.
9
 *
10
 * todo: add transactions everywhere
11
 */
12
export class RelationUpdater {
1✔
13
    // -------------------------------------------------------------------------
14
    // Constructor
15
    // -------------------------------------------------------------------------
16

17
    constructor(
UNCOV
18
        protected queryBuilder: QueryBuilder<any>,
×
UNCOV
19
        protected expressionMap: QueryExpressionMap,
×
20
    ) {}
21

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

26
    /**
27
     * Performs set or add operation on a relation.
28
     */
29
    async update(value: any | any[]): Promise<void> {
UNCOV
30
        const relation = this.expressionMap.relationMetadata
×
31

UNCOV
32
        if (relation.isManyToOne || relation.isOneToOneOwner) {
×
UNCOV
33
            const updateSet = relation.joinColumns.reduce(
×
34
                (updateSet, joinColumn) => {
UNCOV
35
                    const relationValue = ObjectUtils.isObject(value)
×
36
                        ? joinColumn.referencedColumn!.getEntityValue(value)
37
                        : value
UNCOV
38
                    joinColumn.setEntityValue(updateSet, relationValue)
×
UNCOV
39
                    return updateSet
×
40
                },
41
                {} as any,
42
            )
43

UNCOV
44
            if (
×
45
                !this.expressionMap.of ||
×
46
                (Array.isArray(this.expressionMap.of) &&
47
                    !this.expressionMap.of.length)
48
            )
49
                return
×
50

UNCOV
51
            await this.queryBuilder
×
52
                .createQueryBuilder()
53
                .update(relation.entityMetadata.target)
54
                .set(updateSet)
55
                .whereInIds(this.expressionMap.of)
56
                .execute()
UNCOV
57
        } else if (
×
58
            (relation.isOneToOneNotOwner || relation.isOneToMany) &&
×
59
            value === null
60
        ) {
61
            // we handle null a bit different way
62

UNCOV
63
            const updateSet: ObjectLiteral = {}
×
UNCOV
64
            relation.inverseRelation!.joinColumns.forEach((column) => {
×
UNCOV
65
                updateSet[column.propertyName] = null
×
66
            })
67

UNCOV
68
            const ofs = Array.isArray(this.expressionMap.of)
×
69
                ? this.expressionMap.of
70
                : [this.expressionMap.of]
UNCOV
71
            const parameters: ObjectLiteral = {}
×
UNCOV
72
            const conditions: string[] = []
×
UNCOV
73
            ofs.forEach((of, ofIndex) => {
×
UNCOV
74
                relation.inverseRelation!.joinColumns.map(
×
75
                    (column, columnIndex) => {
76
                        const parameterName =
UNCOV
77
                            "joinColumn_" + ofIndex + "_" + columnIndex
×
UNCOV
78
                        parameters[parameterName] = ObjectUtils.isObject(of)
×
79
                            ? column.referencedColumn!.getEntityValue(of)
80
                            : of
UNCOV
81
                        conditions.push(
×
82
                            `${column.propertyPath} = :${parameterName}`,
83
                        )
84
                    },
85
                )
86
            })
UNCOV
87
            const condition = conditions
×
UNCOV
88
                .map((str) => "(" + str + ")")
×
89
                .join(" OR ")
UNCOV
90
            if (!condition) return
×
91

UNCOV
92
            await this.queryBuilder
×
93
                .createQueryBuilder()
94
                .update(relation.inverseEntityMetadata.target)
95
                .set(updateSet)
96
                .where(condition)
97
                .setParameters(parameters)
98
                .execute()
UNCOV
99
        } else if (relation.isOneToOneNotOwner || relation.isOneToMany) {
×
UNCOV
100
            if (Array.isArray(this.expressionMap.of))
×
101
                throw new TypeORMError(
×
102
                    `You cannot update relations of multiple entities with the same related object. Provide a single entity into .of method.`,
103
                )
104

UNCOV
105
            const of = this.expressionMap.of
×
UNCOV
106
            const updateSet = relation.inverseRelation!.joinColumns.reduce(
×
107
                (updateSet, joinColumn) => {
UNCOV
108
                    const relationValue = ObjectUtils.isObject(of)
×
109
                        ? joinColumn.referencedColumn!.getEntityValue(of)
110
                        : of
UNCOV
111
                    joinColumn.setEntityValue(updateSet, relationValue)
×
UNCOV
112
                    return updateSet
×
113
                },
114
                {} as any,
115
            )
116

UNCOV
117
            if (!value || (Array.isArray(value) && !value.length)) return
×
118

UNCOV
119
            await this.queryBuilder
×
120
                .createQueryBuilder()
121
                .update(relation.inverseEntityMetadata.target)
122
                .set(updateSet)
123
                .whereInIds(value)
124
                .execute()
125
        } else {
126
            // many to many
UNCOV
127
            const junctionMetadata = relation.junctionEntityMetadata!
×
UNCOV
128
            const ofs = Array.isArray(this.expressionMap.of)
×
129
                ? this.expressionMap.of
130
                : [this.expressionMap.of]
UNCOV
131
            const values = Array.isArray(value) ? value : [value]
×
UNCOV
132
            const firstColumnValues = relation.isManyToManyOwner ? ofs : values
×
UNCOV
133
            const secondColumnValues = relation.isManyToManyOwner ? values : ofs
×
134

UNCOV
135
            const bulkInserted: ObjectLiteral[] = []
×
UNCOV
136
            firstColumnValues.forEach((firstColumnVal) => {
×
UNCOV
137
                secondColumnValues.forEach((secondColumnVal) => {
×
UNCOV
138
                    const inserted: ObjectLiteral = {}
×
UNCOV
139
                    junctionMetadata.ownerColumns.forEach((column) => {
×
UNCOV
140
                        inserted[column.databaseName] = ObjectUtils.isObject(
×
141
                            firstColumnVal,
142
                        )
143
                            ? column.referencedColumn!.getEntityValue(
144
                                  firstColumnVal,
145
                              )
146
                            : firstColumnVal
147
                    })
UNCOV
148
                    junctionMetadata.inverseColumns.forEach((column) => {
×
UNCOV
149
                        inserted[column.databaseName] = ObjectUtils.isObject(
×
150
                            secondColumnVal,
151
                        )
152
                            ? column.referencedColumn!.getEntityValue(
153
                                  secondColumnVal,
154
                              )
155
                            : secondColumnVal
156
                    })
UNCOV
157
                    bulkInserted.push(inserted)
×
158
                })
159
            })
160

UNCOV
161
            if (!bulkInserted.length) return
×
162

UNCOV
163
            if (
×
164
                this.queryBuilder.connection.driver.options.type === "oracle" ||
×
165
                this.queryBuilder.connection.driver.options.type === "sap"
166
            ) {
UNCOV
167
                await Promise.all(
×
168
                    bulkInserted.map((value) => {
UNCOV
169
                        return this.queryBuilder
×
170
                            .createQueryBuilder()
171
                            .insert()
172
                            .into(junctionMetadata.tableName)
173
                            .values(value)
174
                            .execute()
175
                    }),
176
                )
177
            } else {
UNCOV
178
                await this.queryBuilder
×
179
                    .createQueryBuilder()
180
                    .insert()
181
                    .into(junctionMetadata.tableName)
182
                    .values(bulkInserted)
183
                    .execute()
184
            }
185
        }
186
    }
187
}
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