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

typeorm / typeorm / 15089093306

17 May 2025 09:03PM UTC coverage: 50.109% (-26.2%) from 76.346%
15089093306

Pull #11437

github

naorpeled
add comment about vector <#>
Pull Request #11437: feat(postgres): support vector data type

5836 of 12767 branches covered (45.71%)

Branch coverage included in aggregate %.

16 of 17 new or added lines in 4 files covered. (94.12%)

6283 existing lines in 64 files now uncovered.

12600 of 24025 relevant lines covered (52.45%)

28708.0 hits per line

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

78.21
/src/persistence/SubjectChangedColumnsComputer.ts
1
import { Subject } from "./Subject"
2
import { DateUtils } from "../util/DateUtils"
6✔
3
import { ObjectLiteral } from "../common/ObjectLiteral"
4
import { OrmUtils } from "../util/OrmUtils"
6✔
5
import { ApplyValueTransformers } from "../util/ApplyValueTransformers"
6✔
6
import { ObjectUtils } from "../util/ObjectUtils"
6✔
7

8
/**
9
 * Finds what columns are changed in the subject entities.
10
 */
11
export class SubjectChangedColumnsComputer {
6✔
12
    // -------------------------------------------------------------------------
13
    // Public Methods
14
    // -------------------------------------------------------------------------
15

16
    /**
17
     * Finds what columns are changed in the subject entities.
18
     */
19
    compute(subjects: Subject[]) {
20
        subjects.forEach((subject) => {
39,730✔
21
            this.computeDiffColumns(subject)
117,666✔
22
            this.computeDiffRelationalColumns(subjects, subject)
117,666✔
23
        })
24
    }
25

26
    // -------------------------------------------------------------------------
27
    // Protected Methods
28
    // -------------------------------------------------------------------------
29

30
    /**
31
     * Differentiate columns from the updated entity and entity stored in the database.
32
     */
33
    protected computeDiffColumns(subject: Subject): void {
34
        // if there is no persisted entity then nothing to compute changed in it
35
        if (!subject.entity) return
117,666✔
36

37
        subject.metadata.columns.forEach((column) => {
104,683✔
38
            // ignore special columns
39
            if (
376,017✔
40
                column.isVirtual ||
734,484✔
41
                column.isDiscriminator // ||
42
                // column.isUpdateDate ||
43
                // column.isVersion ||
44
                // column.isCreateDate
45
            )
46
                return
17,640✔
47

48
            const changeMap = subject.changeMaps.find(
358,377✔
49
                (changeMap) => changeMap.column === column,
415,718✔
50
            )
51
            if (changeMap) {
358,377✔
52
                subject.changeMaps.splice(
258✔
53
                    subject.changeMaps.indexOf(changeMap),
54
                    1,
55
                )
56
            }
57

58
            // get user provided value - column value from the user provided persisted entity
59
            const entityValue = column.getEntityValue(subject.entity!)
358,377✔
60

61
            // we don't perform operation over undefined properties (but we DO need null properties!)
62
            if (entityValue === undefined) return
358,377✔
63

64
            // if there is no database entity then all columns are treated as new, e.g. changed
65
            if (subject.databaseEntity) {
295,808✔
66
                // skip transform database value for json / jsonb for comparison later on
67
                const shouldTransformDatabaseEntity =
68
                    column.type !== "json" && column.type !== "jsonb"
5,826✔
69

70
                // get database value of the column
71
                let databaseValue = column.getEntityValue(
5,826✔
72
                    subject.databaseEntity,
73
                    shouldTransformDatabaseEntity,
74
                )
75

76
                // filter out "relational columns" only in the case if there is a relation object in entity
77
                if (column.relationMetadata) {
5,826✔
78
                    const value = column.relationMetadata.getEntityValue(
122✔
79
                        subject.entity!,
80
                    )
81
                    if (value !== null && value !== undefined) return
122✔
82
                }
83
                let normalizedValue = entityValue
5,810✔
84
                // normalize special values to make proper comparision
85
                if (entityValue !== null) {
5,810✔
86
                    switch (column.type) {
5,561!
87
                        case "date":
88
                            normalizedValue = column.isArray
6✔
89
                                ? entityValue.map((date: Date) =>
90
                                      DateUtils.mixedDateToDateString(date),
5✔
91
                                  )
92
                                : DateUtils.mixedDateToDateString(entityValue)
93
                            databaseValue = column.isArray
6✔
94
                                ? databaseValue.map((date: Date) =>
95
                                      DateUtils.mixedDateToDateString(date),
5✔
96
                                  )
97
                                : DateUtils.mixedDateToDateString(databaseValue)
98
                            break
6✔
99

100
                        case "time":
101
                        case "time with time zone":
102
                        case "time without time zone":
103
                        case "timetz":
104
                            normalizedValue = column.isArray
×
105
                                ? entityValue.map((date: Date) =>
106
                                      DateUtils.mixedDateToTimeString(date),
×
107
                                  )
108
                                : DateUtils.mixedDateToTimeString(entityValue)
109
                            databaseValue = column.isArray
×
110
                                ? databaseValue.map((date: Date) =>
111
                                      DateUtils.mixedDateToTimeString(date),
×
112
                                  )
113
                                : DateUtils.mixedDateToTimeString(databaseValue)
114
                            break
×
115

116
                        case "datetime":
117
                        case "datetime2":
118
                        case Date:
119
                        case "timestamp":
120
                        case "timestamp without time zone":
121
                        case "timestamp with time zone":
122
                        case "timestamp with local time zone":
123
                        case "timestamptz":
124
                            normalizedValue = column.isArray
175!
125
                                ? entityValue.map((date: Date) =>
126
                                      DateUtils.mixedDateToUtcDatetimeString(
×
127
                                          date,
128
                                      ),
129
                                  )
130
                                : DateUtils.mixedDateToUtcDatetimeString(
131
                                      entityValue,
132
                                  )
133

134
                            databaseValue = column.isArray
175!
135
                                ? databaseValue.map((date: Date) =>
136
                                      DateUtils.mixedDateToUtcDatetimeString(
×
137
                                          date,
138
                                      ),
139
                                  )
140
                                : DateUtils.mixedDateToUtcDatetimeString(
141
                                      databaseValue,
142
                                  )
143

144
                            break
175✔
145

146
                        case "json":
147
                        case "jsonb":
148
                            // JSON.stringify doesn't work because postgresql sorts jsonb before save.
149
                            // If you try to save json '[{"messages": "", "attribute Key": "", "level":""}] ' as jsonb,
150
                            // then postgresql will save it as '[{"level": "", "message":"", "attributeKey": ""}]'
151
                            if (
4✔
152
                                OrmUtils.deepCompare(entityValue, databaseValue)
153
                            )
154
                                return
3✔
155
                            break
1✔
156

157
                        case "simple-array":
158
                            normalizedValue =
×
159
                                DateUtils.simpleArrayToString(entityValue)
160
                            databaseValue =
×
161
                                DateUtils.simpleArrayToString(databaseValue)
162
                            break
×
163
                        case "simple-enum":
164
                            normalizedValue =
×
165
                                DateUtils.simpleEnumToString(entityValue)
166
                            databaseValue =
×
167
                                DateUtils.simpleEnumToString(databaseValue)
168
                            break
×
169
                        case "simple-json":
170
                            normalizedValue =
×
171
                                DateUtils.simpleJsonToString(entityValue)
172
                            databaseValue =
×
173
                                DateUtils.simpleJsonToString(databaseValue)
174
                            break
×
175
                    }
176

177
                    if (column.transformer) {
5,558✔
178
                        normalizedValue = ApplyValueTransformers.transformTo(
63✔
179
                            column.transformer,
180
                            entityValue,
181
                        )
182
                    }
183
                }
184

185
                // if value is not changed - then do nothing
186
                if (column.isArray) {
5,807✔
187
                    if (OrmUtils.deepCompare(normalizedValue, databaseValue))
7✔
188
                        return
5✔
189
                } else if (
5,800!
190
                    Buffer.isBuffer(normalizedValue) &&
5,800!
191
                    Buffer.isBuffer(databaseValue)
192
                ) {
UNCOV
193
                    if (normalizedValue.equals(databaseValue)) {
×
UNCOV
194
                        return
×
195
                    }
196
                } else {
197
                    if (normalizedValue === databaseValue) return
5,800✔
198
                }
199
            }
200

201
            if (!subject.diffColumns.includes(column))
290,873✔
202
                subject.diffColumns.push(column)
290,621✔
203

204
            subject.changeMaps.push({
290,873✔
205
                column: column,
206
                value: entityValue,
207
            })
208
        })
209
    }
210

211
    /**
212
     * Difference columns of the owning one-to-one and many-to-one columns.
213
     */
214
    protected computeDiffRelationalColumns(
215
        allSubjects: Subject[],
216
        subject: Subject,
217
    ): void {
218
        // if there is no persisted entity then nothing to compute changed in it
219
        if (!subject.entity) return
117,666✔
220

221
        subject.metadata.relationsWithJoinColumns.forEach((relation) => {
104,683✔
222
            // get the related entity from the persisted entity
223
            let relatedEntity = relation.getEntityValue(subject.entity!)
14,553✔
224

225
            // we don't perform operation over undefined properties (but we DO need null properties!)
226
            if (relatedEntity === undefined) return
14,553✔
227

228
            // if there is no database entity then all relational columns are treated as new, e.g. changed
229
            if (subject.databaseEntity) {
4,751✔
230
                // here we cover two scenarios:
231
                // 1. related entity can be another entity which is natural way
232
                // 2. related entity can be just an entity id
233
                // if relation entity is just a relation id set (for example post.tag = 1)
234
                // then we create an id map from it to make a proper comparision
235
                let relatedEntityRelationIdMap: ObjectLiteral = relatedEntity
276✔
236
                if (
276✔
237
                    relatedEntityRelationIdMap !== null &&
522✔
238
                    ObjectUtils.isObject(relatedEntityRelationIdMap)
239
                )
240
                    relatedEntityRelationIdMap = relation.getRelationIdMap(
234✔
241
                        relatedEntityRelationIdMap,
242
                    )!
243

244
                // get database related entity. Since loadRelationIds are used on databaseEntity
245
                // related entity will contain only its relation ids
246
                const databaseRelatedEntityRelationIdMap =
247
                    relation.getEntityValue(subject.databaseEntity)
276✔
248

249
                // if relation ids are equal then we don't need to update anything
250
                const areRelatedIdsEqual = OrmUtils.compareIds(
276✔
251
                    relatedEntityRelationIdMap,
252
                    databaseRelatedEntityRelationIdMap,
253
                )
254
                if (areRelatedIdsEqual) {
276✔
255
                    return
65✔
256
                } else {
257
                    subject.diffRelations.push(relation)
211✔
258
                }
259
            }
260

261
            // if there is an inserted subject for the related entity of the persisted entity then use it as related entity
262
            // this code is used for related entities without ids to be properly inserted (and then updated if needed)
263
            const valueSubject = allSubjects.find(
4,686✔
264
                (subject) =>
265
                    subject.mustBeInserted && subject.entity === relatedEntity,
8,679✔
266
            )
267
            if (valueSubject) relatedEntity = valueSubject
4,686✔
268

269
            // find if there is already a relation to be changed
270
            const changeMap = subject.changeMaps.find(
4,686✔
271
                (changeMap) => changeMap.relation === relation,
10,190✔
272
            )
273
            if (changeMap) {
4,686✔
274
                // and update its value if it was found
275
                changeMap.value = relatedEntity
36✔
276
            } else {
277
                // if it wasn't found add a new relation for change
278
                subject.changeMaps.push({
4,650✔
279
                    relation: relation,
280
                    value: relatedEntity,
281
                })
282
            }
283
        })
284
    }
285
}
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