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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

82.63
/src/query-builder/RelationQueryBuilder.ts
1
import { QueryBuilder } from "./QueryBuilder"
26✔
2
import { RelationUpdater } from "./RelationUpdater"
26✔
3
import { RelationRemover } from "./RelationRemover"
26✔
4
import { TypeORMError } from "../error"
26✔
5
import { ObjectUtils } from "../util/ObjectUtils"
26✔
6
import { ObjectLiteral } from "../common/ObjectLiteral"
26✔
7

26✔
8
/**
26✔
9
 * Allows to work with entity relations and perform specific operations with those relations.
26✔
10
 *
26✔
11
 * todo: add transactions everywhere
26✔
12
 */
26✔
13
export class RelationQueryBuilder<
3,556✔
14
    Entity extends ObjectLiteral,
3,556✔
15
> extends QueryBuilder<Entity> {
3,556✔
16
    readonly "@instanceof" = Symbol.for("RelationQueryBuilder")
3,556✔
17

3,556✔
18
    // -------------------------------------------------------------------------
3,556✔
19
    // Public Implemented Methods
3,556✔
20
    // -------------------------------------------------------------------------
3,556✔
21

3,556✔
22
    /**
3,556✔
23
     * Gets generated SQL query without parameters being replaced.
3,556✔
24
     */
3,556✔
25
    getQuery(): string {
3,556✔
26
        return ""
×
27
    }
×
28

3,556✔
29
    // -------------------------------------------------------------------------
3,556✔
30
    // Public Methods
3,556✔
31
    // -------------------------------------------------------------------------
3,556✔
32

3,556✔
33
    /**
3,556✔
34
     * Sets entity (target) which relations will be updated.
3,556✔
35
     */
3,556✔
36
    of(entity: any | any[]): this {
3,556✔
37
        this.expressionMap.of = entity
1,944✔
38
        return this
1,944✔
39
    }
1,944✔
40

3,556✔
41
    /**
3,556✔
42
     * Sets entity relation's value.
3,556✔
43
     * Value can be entity, entity id or entity id map (if entity has composite ids).
3,556✔
44
     * Works only for many-to-one and one-to-one relations.
3,556✔
45
     * For many-to-many and one-to-many relations use #add and #remove methods instead.
3,556✔
46
     */
3,556✔
47
    async set(value: any): Promise<void> {
3,556✔
48
        const relation = this.expressionMap.relationMetadata
616✔
49

616✔
50
        if (!this.expressionMap.of)
616✔
51
            // todo: move this check before relation query builder creation?
616✔
52
            throw new TypeORMError(
616!
53
                `Entity whose relation needs to be set is not set. Use .of method to define whose relation you want to set.`,
×
54
            )
×
55

616✔
56
        if (relation.isManyToMany || relation.isOneToMany)
616✔
57
            throw new TypeORMError(
616!
58
                `Set operation is only supported for many-to-one and one-to-one relations. ` +
×
59
                    `However given "${relation.propertyPath}" has ${relation.relationType} relation. ` +
×
60
                    `Use .add() method instead.`,
×
61
            )
×
62

616✔
63
        // if there are multiple join columns then user must send id map as "value" argument. check if he really did it
616✔
64
        if (
616✔
65
            relation.joinColumns &&
616✔
66
            relation.joinColumns.length > 1 &&
616!
67
            (!ObjectUtils.isObject(value) ||
×
68
                Object.keys(value).length < relation.joinColumns.length)
×
69
        )
616✔
70
            throw new TypeORMError(
616!
71
                `Value to be set into the relation must be a map of relation ids, for example: .set({ firstName: "...", lastName: "..." })`,
×
72
            )
×
73

616✔
74
        const updater = new RelationUpdater(this, this.expressionMap)
616✔
75
        return updater.update(value)
616✔
76
    }
616✔
77

3,556✔
78
    /**
3,556✔
79
     * Adds (binds) given value to entity relation.
3,556✔
80
     * Value can be entity, entity id or entity id map (if entity has composite ids).
3,556✔
81
     * Value also can be array of entities, array of entity ids or array of entity id maps (if entity has composite ids).
3,556✔
82
     * Works only for many-to-many and one-to-many relations.
3,556✔
83
     * For many-to-one and one-to-one use #set method instead.
3,556✔
84
     */
3,556✔
85
    async add(value: any | any[]): Promise<void> {
3,556✔
86
        if (Array.isArray(value) && value.length === 0) return
532✔
87

504✔
88
        const relation = this.expressionMap.relationMetadata
504✔
89

504✔
90
        if (!this.expressionMap.of)
504✔
91
            // todo: move this check before relation query builder creation?
504✔
92
            throw new TypeORMError(
532!
93
                `Entity whose relation needs to be set is not set. Use .of method to define whose relation you want to set.`,
×
94
            )
×
95

504✔
96
        if (relation.isManyToOne || relation.isOneToOne)
532✔
97
            throw new TypeORMError(
532!
98
                `Add operation is only supported for many-to-many and one-to-many relations. ` +
×
99
                    `However given "${relation.propertyPath}" has ${relation.relationType} relation. ` +
×
100
                    `Use .set() method instead.`,
×
101
            )
×
102

504✔
103
        // if there are multiple join columns then user must send id map as "value" argument. check if he really did it
504✔
104
        if (
504✔
105
            relation.joinColumns &&
504✔
106
            relation.joinColumns.length > 1 &&
532!
107
            (!ObjectUtils.isObject(value) ||
×
108
                Object.keys(value).length < relation.joinColumns.length)
×
109
        )
532✔
110
            throw new TypeORMError(
532!
111
                `Value to be set into the relation must be a map of relation ids, for example: .set({ firstName: "...", lastName: "..." })`,
×
112
            )
×
113

504✔
114
        const updater = new RelationUpdater(this, this.expressionMap)
504✔
115
        return updater.update(value)
504✔
116
    }
504✔
117

3,556✔
118
    /**
3,556✔
119
     * Removes (unbinds) given value from entity relation.
3,556✔
120
     * Value can be entity, entity id or entity id map (if entity has composite ids).
3,556✔
121
     * Value also can be array of entities, array of entity ids or array of entity id maps (if entity has composite ids).
3,556✔
122
     * Works only for many-to-many and one-to-many relations.
3,556✔
123
     * For many-to-one and one-to-one use #set method instead.
3,556✔
124
     */
3,556✔
125
    async remove(value: any | any[]): Promise<void> {
3,556✔
126
        if (Array.isArray(value) && value.length === 0) return
504✔
127

476✔
128
        const relation = this.expressionMap.relationMetadata
476✔
129

476✔
130
        if (!this.expressionMap.of)
476✔
131
            // todo: move this check before relation query builder creation?
476✔
132
            throw new TypeORMError(
504!
133
                `Entity whose relation needs to be set is not set. Use .of method to define whose relation you want to set.`,
×
134
            )
×
135

476✔
136
        if (relation.isManyToOne || relation.isOneToOne)
504✔
137
            throw new TypeORMError(
504!
138
                `Add operation is only supported for many-to-many and one-to-many relations. ` +
×
139
                    `However given "${relation.propertyPath}" has ${relation.relationType} relation. ` +
×
140
                    `Use .set(null) method instead.`,
×
141
            )
×
142

476✔
143
        const remover = new RelationRemover(this, this.expressionMap)
476✔
144
        return remover.remove(value)
476✔
145
    }
476✔
146

3,556✔
147
    /**
3,556✔
148
     * Adds (binds) and removes (unbinds) given values to/from entity relation.
3,556✔
149
     * Value can be entity, entity id or entity id map (if entity has composite ids).
3,556✔
150
     * Value also can be array of entities, array of entity ids or array of entity id maps (if entity has composite ids).
3,556✔
151
     * Works only for many-to-many and one-to-many relations.
3,556✔
152
     * For many-to-one and one-to-one use #set method instead.
3,556✔
153
     */
3,556✔
154
    async addAndRemove(
3,556✔
155
        added: any | any[],
56✔
156
        removed: any | any[],
56✔
157
    ): Promise<void> {
56✔
158
        await this.remove(removed)
56✔
159
        await this.add(added)
56✔
160
    }
56✔
161

3,556✔
162
    /**
3,556✔
163
     * Gets entity's relation id.
3,556✔
164
    async getId(): Promise<any> {
3,556✔
165

3,556✔
166
    }*/
3,556✔
167

3,556✔
168
    /**
3,556✔
169
     * Gets entity's relation ids.
3,556✔
170
    async getIds(): Promise<any[]> {
3,556✔
171
        return [];
3,556✔
172
    }*/
3,556✔
173

3,556✔
174
    /**
3,556✔
175
     * Loads a single entity (relational) from the relation.
3,556✔
176
     * You can also provide id of relational entity to filter by.
3,556✔
177
     */
3,556✔
178
    async loadOne<T = any>(): Promise<T | undefined> {
3,556✔
179
        return this.loadMany<T>().then((results) => results[0])
252✔
180
    }
252✔
181

3,556✔
182
    /**
3,556✔
183
     * Loads many entities (relational) from the relation.
3,556✔
184
     * You can also provide ids of relational entities to filter by.
3,556✔
185
     */
3,556✔
186
    async loadMany<T = any>(): Promise<T[]> {
3,556✔
187
        let of = this.expressionMap.of
348✔
188
        if (!ObjectUtils.isObject(of)) {
348✔
189
            const metadata = this.expressionMap.mainAlias!.metadata
124✔
190
            if (metadata.hasMultiplePrimaryKeys)
124✔
191
                throw new TypeORMError(
124!
192
                    `Cannot load entity because only one primary key was specified, however entity contains multiple primary keys`,
×
193
                )
×
194

124✔
195
            of = metadata.primaryColumns[0].createValueMap(of)
124✔
196
        }
124✔
197

348✔
198
        return this.connection.relationLoader.load(
348✔
199
            this.expressionMap.relationMetadata,
348✔
200
            of,
348✔
201
            this.queryRunner,
348✔
202
        )
348✔
203
    }
348✔
204
}
3,556✔
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

© 2026 Coveralls, Inc