Skip to content

Commit a9bdb37

Browse files
njampleerock
andauthored
perf: Optimized version of EntityMetadata#compareIds() for the common case (#5419)
* perf: Optimized version of EntityMetadata#compareIds() for the common case * Extract `compareIds()` into `OrmUtils` and use it instead of `.deepCompare()` where applicable * Use optimized path in compareIds() also when .id has type "number" * Add return type to signature of deepCompare() and compareIds() * made a proper check Co-authored-by: Umed Khudoiberdiev <pleerock.me@gmail.com>
1 parent 4ef6b65 commit a9bdb37

File tree

7 files changed

+29
-22
lines changed

7 files changed

+29
-22
lines changed

src/metadata/EntityMetadata.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ export class EntityMetadata {
618618
const secondEntityIdMap = this.getEntityIdMap(secondEntity);
619619
if (!secondEntityIdMap) return false;
620620

621-
return EntityMetadata.compareIds(firstEntityIdMap, secondEntityIdMap);
621+
return OrmUtils.compareIds(firstEntityIdMap, secondEntityIdMap);
622622
}
623623

624624
/**
@@ -739,21 +739,10 @@ export class EntityMetadata {
739739
*/
740740
static difference(firstIdMaps: ObjectLiteral[], secondIdMaps: ObjectLiteral[]): ObjectLiteral[] {
741741
return firstIdMaps.filter(firstIdMap => {
742-
return !secondIdMaps.find(secondIdMap => OrmUtils.deepCompare(firstIdMap, secondIdMap));
742+
return !secondIdMaps.find(secondIdMap => OrmUtils.compareIds(firstIdMap, secondIdMap));
743743
});
744744
}
745745

746-
/**
747-
* Compares ids of the two entities.
748-
* Returns true if they match, false otherwise.
749-
*/
750-
static compareIds(firstId: ObjectLiteral|undefined, secondId: ObjectLiteral|undefined): boolean {
751-
if (firstId === undefined || firstId === null || secondId === undefined || secondId === null)
752-
return false;
753-
754-
return OrmUtils.deepCompare(firstId, secondId);
755-
}
756-
757746
/**
758747
* Creates value map from the given values and columns.
759748
* Examples of usages are primary columns map and join columns map.

src/persistence/SubjectChangedColumnsComputer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {Subject} from "./Subject";
22
import {DateUtils} from "../util/DateUtils";
33
import {ObjectLiteral} from "../common/ObjectLiteral";
4-
import {EntityMetadata} from "../metadata/EntityMetadata";
54
import {OrmUtils} from "../util/OrmUtils";
65
import {ApplyValueTransformers} from "../util/ApplyValueTransformers";
76

@@ -173,7 +172,7 @@ export class SubjectChangedColumnsComputer {
173172
const databaseRelatedEntityRelationIdMap = relation.getEntityValue(subject.databaseEntity);
174173

175174
// if relation ids are equal then we don't need to update anything
176-
const areRelatedIdsEqual = EntityMetadata.compareIds(relatedEntityRelationIdMap, databaseRelatedEntityRelationIdMap);
175+
const areRelatedIdsEqual = OrmUtils.compareIds(relatedEntityRelationIdMap, databaseRelatedEntityRelationIdMap);
177176
if (areRelatedIdsEqual) {
178177
return;
179178
} else {

src/persistence/subject-builder/ManyToManySubjectBuilder.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {Subject} from "../Subject";
22
import {OrmUtils} from "../../util/OrmUtils";
33
import {ObjectLiteral} from "../../common/ObjectLiteral";
44
import {RelationMetadata} from "../../metadata/RelationMetadata";
5-
import {EntityMetadata} from "../../metadata/EntityMetadata";
65

76
/**
87
* Builds operations needs to be executed for many-to-many relations of the given subjects.
@@ -150,7 +149,7 @@ export class ManyToManySubjectBuilder {
150149
// try to find related entity in the database
151150
// by example: find post's category in the database post's categories
152151
const relatedEntityExistInDatabase = databaseRelatedEntityIds.find(databaseRelatedEntityRelationId => {
153-
return EntityMetadata.compareIds(databaseRelatedEntityRelationId, relatedEntityRelationIdMap);
152+
return OrmUtils.compareIds(databaseRelatedEntityRelationId, relatedEntityRelationIdMap);
154153
});
155154

156155
// if entity is found then don't do anything - it means binding in junction table already exist, we don't need to add anything
@@ -207,7 +206,7 @@ export class ManyToManySubjectBuilder {
207206
// now from all entities in the persisted entity find only those which aren't found in the db
208207
const removedJunctionEntityIds = databaseRelatedEntityIds.filter(existRelationId => {
209208
return !changedInverseEntityRelationIds.find(changedRelationId => {
210-
return EntityMetadata.compareIds(changedRelationId, existRelationId);
209+
return OrmUtils.compareIds(changedRelationId, existRelationId);
211210
});
212211
});
213212

src/persistence/subject-builder/OneToManySubjectBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class OneToManySubjectBuilder {
117117
// check if this binding really exist in the database
118118
// by example: find our category if its already bind in the database
119119
const relationIdInDatabaseSubjectRelation = relatedEntityDatabaseRelationIds.find(relatedDatabaseEntityRelationId => {
120-
return OrmUtils.deepCompare(relationIdMap, relatedDatabaseEntityRelationId);
120+
return OrmUtils.compareIds(relationIdMap, relatedDatabaseEntityRelationId);
121121
});
122122

123123
// if relationIdMap DOES NOT exist in the subject's relation in the database it means its a new relation and we need to "bind" them

src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class OneToOneInverseSideSubjectBuilder {
139139

140140
// check if this binding really exist in the database
141141
// by example: find our post if its already bind to category in the database and its not equal to what user tries to set
142-
const areRelatedIdEqualWithDatabase = relatedEntityDatabaseRelationId && OrmUtils.deepCompare(relationIdMap, relatedEntityDatabaseRelationId);
142+
const areRelatedIdEqualWithDatabase = relatedEntityDatabaseRelationId && OrmUtils.compareIds(relationIdMap, relatedEntityDatabaseRelationId);
143143

144144
// if they aren't equal it means its a new relation and we need to "bind" them
145145
// by example: this will tell category to insert into its post relation our post we are working with

src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export class RawSqlResultsToEntityTransformer {
213213

214214
const idMaps = rawRelationIdResult.results.map(result => {
215215
const entityPrimaryIds = this.extractEntityPrimaryIds(relation, result);
216-
if (EntityMetadata.compareIds(entityPrimaryIds, valueMap) === false)
216+
if (OrmUtils.compareIds(entityPrimaryIds, valueMap) === false)
217217
return;
218218

219219
let columns: ColumnMetadata[];

src/util/OrmUtils.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export class OrmUtils {
102102
*
103103
* @see http://stackoverflow.com/a/1144249
104104
*/
105-
static deepCompare(...args: any[]) {
105+
static deepCompare(...args: any[]): boolean {
106106
let i: any, l: any, leftChain: any, rightChain: any;
107107

108108
if (arguments.length < 1) {
@@ -123,6 +123,26 @@ export class OrmUtils {
123123
return true;
124124
}
125125

126+
/**
127+
* Check if two entity-id-maps are the same
128+
*/
129+
static compareIds(firstId: ObjectLiteral|undefined, secondId: ObjectLiteral|undefined): boolean {
130+
if (firstId === undefined || firstId === null || secondId === undefined || secondId === null)
131+
return false;
132+
133+
// Optimized version for the common case
134+
if (
135+
((typeof firstId.id === "string" && typeof secondId.id === "string") ||
136+
(typeof firstId.id === "number" && typeof secondId.id === "number")) &&
137+
Object.keys(firstId).length === 1 &&
138+
Object.keys(secondId).length === 1
139+
) {
140+
return firstId.id === secondId.id;
141+
}
142+
143+
return OrmUtils.deepCompare(firstId, secondId);
144+
}
145+
126146
/**
127147
* Transforms given value into boolean value.
128148
*/

0 commit comments

Comments
 (0)