• 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

95.08
/src/persistence/SubjectDatabaseEntityLoader.ts
1
import { Subject } from "./Subject"
2
import { ObjectLiteral } from "../common/ObjectLiteral"
3
import { QueryRunner } from "../query-runner/QueryRunner"
4
import { FindManyOptions } from "../find-options/FindManyOptions"
5
import { MongoRepository } from "../repository/MongoRepository"
6
import { OrmUtils } from "../util/OrmUtils"
6✔
7

8
/**
9
 * Loads database entities for all operate subjects which do not have database entity set.
10
 * All entities that we load database entities for are marked as updated or inserted.
11
 * To understand which of them really needs to be inserted or updated we need to load
12
 * their original representations from the database.
13
 */
14
export class SubjectDatabaseEntityLoader {
6✔
15
    // ---------------------------------------------------------------------
16
    // Constructor
17
    // ---------------------------------------------------------------------
18

19
    constructor(
20
        protected queryRunner: QueryRunner,
39,613✔
21
        protected subjects: Subject[],
39,613✔
22
    ) {}
23

24
    // ---------------------------------------------------------------------
25
    // Public Methods
26
    // ---------------------------------------------------------------------
27

28
    /**
29
     * Loads database entities for all subjects.
30
     *
31
     * loadAllRelations flag is used to load all relation ids of the object, no matter if they present in subject entity or not.
32
     * This option is used for deletion.
33
     */
34
    async load(
35
        operationType: "save" | "remove" | "soft-remove" | "recover",
36
    ): Promise<void> {
37
        // we are grouping subjects by target to perform more optimized queries using WHERE IN operator
38
        // go through the groups and perform loading of database entities of each subject in the group
39
        const promises = this.groupByEntityTargets().map(
39,613✔
40
            async (subjectGroup) => {
41
                // prepare entity ids of the subjects we need to load
42
                const allIds: ObjectLiteral[] = []
41,513✔
43
                const allSubjects: Subject[] = []
41,513✔
44
                subjectGroup.subjects.forEach((subject) => {
41,513✔
45
                    // we don't load if subject already has a database entity loaded
46
                    if (subject.databaseEntity || !subject.identifier) return
104,542✔
47

48
                    allIds.push(subject.identifier)
45,855✔
49
                    allSubjects.push(subject)
45,855✔
50
                })
51

52
                // if there no ids found (means all entities are new and have generated ids) - then nothing to load there
53
                if (!allIds.length) return
41,513✔
54

55
                const loadRelationPropertyPaths: string[] = []
25,042✔
56

57
                // for the save, soft-remove and recover operation
58
                // extract all property paths of the relations we need to load relation ids for
59
                // this is for optimization purpose - this way we don't load relation ids for entities
60
                // whose relations are undefined, and since they are undefined its really pointless to
61
                // load something for them, since undefined properties are skipped by the orm
62
                if (
25,042✔
63
                    operationType === "save" ||
25,792✔
64
                    operationType === "soft-remove" ||
65
                    operationType === "recover"
66
                ) {
67
                    subjectGroup.subjects.forEach((subject) => {
24,751✔
68
                        // gets all relation property paths that exist in the persisted entity.
69
                        subject.metadata.relations.forEach((relation) => {
45,691✔
70
                            const value = relation.getEntityValue(
14,247✔
71
                                subject.entityWithFulfilledIds!,
72
                            )
73
                            if (value === undefined) return
14,247✔
74

75
                            if (
6,322✔
76
                                loadRelationPropertyPaths.indexOf(
77
                                    relation.propertyPath,
78
                                ) === -1
79
                            )
80
                                loadRelationPropertyPaths.push(
6,186✔
81
                                    relation.propertyPath,
82
                                )
83
                        })
84
                    })
85
                } else {
86
                    // remove
87

88
                    // for remove operation
89
                    // we only need to load junction relation ids since only they are removed by cascades
90
                    loadRelationPropertyPaths.push(
291✔
91
                        ...subjectGroup.subjects[0].metadata.manyToManyRelations.map(
92
                            (relation) => relation.propertyPath,
90✔
93
                        ),
94
                    )
95
                }
96

97
                const findOptions: FindManyOptions<any> = {
25,042✔
98
                    loadEagerRelations: false,
99
                    loadRelationIds: {
100
                        relations: loadRelationPropertyPaths,
101
                        disableMixedMap: true,
102
                    },
103
                    // the soft-deleted entities should be included in the loaded entities for recover operation
104
                    withDeleted: true,
105
                }
106

107
                // load database entities for all given ids
108
                let entities: any[] = []
25,042✔
109
                if (
25,042!
110
                    this.queryRunner.connection.driver.options.type ===
111
                    "mongodb"
112
                ) {
113
                    const mongoRepo =
UNCOV
114
                        this.queryRunner.manager.getRepository<ObjectLiteral>(
×
115
                            subjectGroup.target,
116
                        ) as MongoRepository<ObjectLiteral>
UNCOV
117
                    entities = await mongoRepo.findByIds(allIds, findOptions)
×
118
                } else {
119
                    entities = await this.queryRunner.manager
25,042✔
120
                        .getRepository<ObjectLiteral>(subjectGroup.target)
121
                        .createQueryBuilder()
122
                        .setFindOptions(findOptions)
123
                        .whereInIds(allIds)
124
                        .getMany()
125
                }
126

127
                // Now when we have entities we need to find subject of each entity
128
                // and insert that entity into database entity of the found subjects.
129
                // A single entity can be applied to many subjects as there might be duplicates.
130
                // This will likely result in the same row being updated multiple times during a transaction.
131
                entities.forEach((entity) => {
25,042✔
132
                    const entityId =
133
                        allSubjects[0].metadata.getEntityIdMap(entity)
1,667✔
134
                    allSubjects.forEach((subject) => {
1,667✔
135
                        if (subject.databaseEntity) return
1,881✔
136
                        if (OrmUtils.compareIds(subject.identifier, entityId))
1,780✔
137
                            subject.databaseEntity = entity
1,679✔
138
                    })
139
                })
140

141
                // this way we tell what subjects we tried to load database entities of
142
                for (const subject of allSubjects) {
25,042✔
143
                    subject.databaseEntityLoaded = true
45,855✔
144
                }
145
            },
146
        )
147

148
        await Promise.all(promises)
39,613✔
149
    }
150

151
    // ---------------------------------------------------------------------
152
    // Protected Methods
153
    // ---------------------------------------------------------------------
154

155
    /**
156
     * Groups given Subject objects into groups separated by entity targets.
157
     */
158
    protected groupByEntityTargets(): {
159
        target: Function | string
160
        subjects: Subject[]
161
    }[] {
162
        return this.subjects.reduce((groups, operatedEntity) => {
39,613✔
163
            let group = groups.find(
104,542✔
164
                (group) => group.target === operatedEntity.metadata.target,
70,132✔
165
            )
166
            if (!group) {
104,542✔
167
                group = { target: operatedEntity.metadata.target, subjects: [] }
41,513✔
168
                groups.push(group)
41,513✔
169
            }
170
            group.subjects.push(operatedEntity)
104,542✔
171
            return groups
104,542✔
172
        }, [] as { target: Function | string; subjects: Subject[] }[])
173
    }
174
}
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