Skip to content

Commit 7887b8c

Browse files
committed
fix(typeorm,#493): Fix uni-directional relation SQL
1 parent 60adfaa commit 7887b8c

File tree

7 files changed

+140
-7
lines changed

7 files changed

+140
-7
lines changed

packages/query-typeorm/.eslintrc.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ module.exports = {
2020
'assertUpdateSQL',
2121
'assertDeleteSQL',
2222
'assertSoftDeleteSQL',
23+
'assertManyToOneUniDirectionalSQL',
24+
'assertManyToManyUniDirectionalSQL'
2325
],
2426
},
2527
],

packages/query-typeorm/__tests__/__fixtures__/seeds.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ export const TEST_RELATIONS: TestRelation[] = TEST_ENTITIES.reduce((relations, t
2929
testRelationPk: `test-relations-${te.testEntityPk}-1`,
3030
relationName: `${te.stringType}-test-relation-one`,
3131
testEntityId: te.testEntityPk,
32+
uniDirectionalTestEntityId: te.testEntityPk,
3233
},
3334
{
3435
testRelationPk: `test-relations-${te.testEntityPk}-2`,
3536
relationName: `${te.stringType}-test-relation-two`,
3637
testEntityId: te.testEntityPk,
38+
uniDirectionalTestEntityId: te.testEntityPk,
3739
},
3840
{
3941
testRelationPk: `test-relations-${te.testEntityPk}-3`,
4042
relationName: `${te.stringType}-test-relation-three`,
4143
testEntityId: te.testEntityPk,
44+
uniDirectionalTestEntityId: te.testEntityPk,
4245
},
4346
];
4447
}, [] as TestRelation[]);
@@ -56,6 +59,14 @@ export const seed = async (connection: Connection = getConnection()): Promise<vo
5659
testEntities.map((te) => {
5760
// eslint-disable-next-line no-param-reassign
5861
te.oneTestRelation = testRelations.find((tr) => tr.testRelationPk === `test-relations-${te.testEntityPk}-1`);
62+
if (te.numberType % 2 === 0) {
63+
// eslint-disable-next-line no-param-reassign
64+
te.manyTestRelations = testRelations.filter((tr) => tr.relationName.endsWith('two'));
65+
}
66+
if (te.numberType % 3 === 0) {
67+
// eslint-disable-next-line no-param-reassign
68+
te.manyToManyUniDirectional = testRelations.filter((tr) => tr.relationName.endsWith('three'));
69+
}
5970
return testEntityRepo.save(te);
6071
}),
6172
);

packages/query-typeorm/__tests__/__fixtures__/test-relation.entity.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ export class TestRelation {
1313
@Column({ name: 'test_entity_id', nullable: true })
1414
testEntityId?: string;
1515

16+
@Column({ name: 'uni_directional_test_entity_id', nullable: true })
17+
uniDirectionalTestEntityId?: string;
18+
1619
@ManyToOne(() => TestEntity, (te) => te.testRelations, { onDelete: 'CASCADE' })
1720
@JoinColumn({ name: 'test_entity_id' })
1821
testEntity?: TestEntity;
1922

23+
@ManyToOne(() => TestEntity, { onDelete: 'CASCADE' })
24+
@JoinColumn({ name: 'uni_directional_test_entity_id' })
25+
testEntityUniDirectional?: TestEntity;
26+
2027
@ManyToMany(() => TestEntity, (te) => te.manyTestRelations, { onDelete: 'CASCADE', nullable: false })
2128
manyTestEntities?: TestEntity[];
2229

packages/query-typeorm/__tests__/__fixtures__/test.entity.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export class TestEntity {
2626
@JoinTable()
2727
manyTestRelations?: TestRelation[];
2828

29+
@ManyToMany(() => TestRelation, { onDelete: 'CASCADE', nullable: false })
30+
@JoinTable()
31+
manyToManyUniDirectional?: TestRelation[];
32+
2933
@OneToOne(() => TestRelation, (relation) => relation.oneTestEntity)
3034
@JoinColumn()
3135
oneTestRelation?: TestRelation;

packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ describe('RelationQueryBuilder', (): void => {
2121
` INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."test_entity_id" = "testEntity"."test_entity_pk"` +
2222
` WHERE ("TestRelation"."test_relation_pk" = ?)`;
2323

24+
const manyToOneSelectUniDirectional =
25+
'SELECT "testEntityUniDirectional"."test_entity_pk" AS "testEntityUniDirectional_test_entity_pk",' +
26+
' "testEntityUniDirectional"."string_type" AS "testEntityUniDirectional_string_type",' +
27+
' "testEntityUniDirectional"."bool_type" AS "testEntityUniDirectional_bool_type",' +
28+
' "testEntityUniDirectional"."number_type" AS "testEntityUniDirectional_number_type",' +
29+
' "testEntityUniDirectional"."date_type" AS "testEntityUniDirectional_date_type",' +
30+
' "testEntityUniDirectional"."oneTestRelationTestRelationPk" AS "testEntityUniDirectional_oneTestRelationTestRelationPk"' +
31+
' FROM "test_entity" "testEntityUniDirectional"' +
32+
' INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."uni_directional_test_entity_id" = "testEntityUniDirectional"."test_entity_pk"' +
33+
' WHERE ("TestRelation"."test_relation_pk" = ?)';
34+
2435
const manyToManyNonOwnerSelectQuery =
2536
`SELECT` +
2637
` "manyTestEntities"."test_entity_pk" AS "manyTestEntities_test_entity_pk",` +
@@ -37,15 +48,17 @@ describe('RelationQueryBuilder', (): void => {
3748
`SELECT` +
3849
` "testRelations"."test_relation_pk" AS "testRelations_test_relation_pk",` +
3950
` "testRelations"."relation_name" AS "testRelations_relation_name",` +
40-
` "testRelations"."test_entity_id" AS "testRelations_test_entity_id"` +
51+
` "testRelations"."test_entity_id" AS "testRelations_test_entity_id",` +
52+
` "testRelations"."uni_directional_test_entity_id" AS "testRelations_uni_directional_test_entity_id"` +
4153
` FROM "test_relation" "testRelations"` +
4254
' WHERE ("testRelations"."test_entity_id" = ?)';
4355

4456
const manyToManyOwnerSelect =
4557
'SELECT ' +
4658
`"manyTestRelations"."test_relation_pk" AS "manyTestRelations_test_relation_pk",` +
4759
' "manyTestRelations"."relation_name" AS "manyTestRelations_relation_name",' +
48-
' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id"' +
60+
' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id",' +
61+
' "manyTestRelations"."uni_directional_test_entity_id" AS "manyTestRelations_uni_directional_test_entity_id"' +
4962
` FROM "test_relation" "manyTestRelations"` +
5063
` INNER JOIN "test_entity_many_test_relations_test_relation" "test_entity_many_test_relations_test_relation" ON "test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" = "manyTestRelations"."test_relation_pk"` +
5164
' WHERE ("test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = ?)';
@@ -54,7 +67,8 @@ describe('RelationQueryBuilder', (): void => {
5467
`SELECT` +
5568
` "oneTestRelation"."test_relation_pk" AS "oneTestRelation_test_relation_pk",` +
5669
` "oneTestRelation"."relation_name" AS "oneTestRelation_relation_name",` +
57-
` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id"` +
70+
` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id",` +
71+
` "oneTestRelation"."uni_directional_test_entity_id" AS "oneTestRelation_uni_directional_test_entity_id"` +
5872
` FROM "test_relation" "oneTestRelation"` +
5973
` INNER JOIN "test_entity" "TestEntity" ON "TestEntity"."oneTestRelationTestRelationPk" = "oneTestRelation"."test_relation_pk"` +
6074
' WHERE ("TestEntity"."test_entity_pk" = ?)';
@@ -77,6 +91,16 @@ describe('RelationQueryBuilder', (): void => {
7791
` FROM "test_entity_relation_entity" "testEntityRelation"` +
7892
` WHERE ("testEntityRelation"."test_entity_id" = ?)`;
7993

94+
const manyToManyUniDirectionalSelect =
95+
'SELECT' +
96+
' "manyToManyUniDirectional"."test_relation_pk" AS "manyToManyUniDirectional_test_relation_pk",' +
97+
' "manyToManyUniDirectional"."relation_name" AS "manyToManyUniDirectional_relation_name",' +
98+
' "manyToManyUniDirectional"."test_entity_id" AS "manyToManyUniDirectional_test_entity_id",' +
99+
' "manyToManyUniDirectional"."uni_directional_test_entity_id" AS "manyToManyUniDirectional_uni_directional_test_entity_id"' +
100+
' FROM "test_relation" "manyToManyUniDirectional" ' +
101+
'INNER JOIN "test_entity_many_to_many_uni_directional_test_relation" "test_entity_many_to_many_uni_directional_test_relation" ON "test_entity_many_to_many_uni_directional_test_relation"."testRelationTestRelationPk" = "manyToManyUniDirectional"."test_relation_pk" ' +
102+
'WHERE ("test_entity_many_to_many_uni_directional_test_relation"."testEntityTestEntityPk" = ?)';
103+
80104
const getRelationQueryBuilder = <Entity, Relation>(
81105
EntityClass: Class<Entity>,
82106
relationName: string,
@@ -105,6 +129,7 @@ describe('RelationQueryBuilder', (): void => {
105129
const assertManyToManyOwnerSQL = createSQLAsserter(TestEntity, manyToManyOwnerSelect);
106130

107131
const assertManyToOneSQL = createSQLAsserter(TestRelation, manyToOneSelect);
132+
const assertManyToOneUniDirectionalSQL = createSQLAsserter(TestRelation, manyToOneSelectUniDirectional);
108133

109134
const assertManyToManyNonOwnerSQL = createSQLAsserter(TestRelation, manyToManyNonOwnerSelectQuery);
110135

@@ -113,6 +138,7 @@ describe('RelationQueryBuilder', (): void => {
113138
const assertOneToOneNonOwnerSQL = createSQLAsserter(TestRelation, oneToOneNonOwnerSelect);
114139

115140
const assertManyToManyCustomJoinSQL = createSQLAsserter(TestEntity, manyToManyCustomJoinSelect);
141+
const assertManyToManyUniDirectionalSQL = createSQLAsserter(TestEntity, manyToManyUniDirectionalSelect);
116142

117143
describe('#select', () => {
118144
const testEntity: TestEntity = {
@@ -144,6 +170,12 @@ describe('RelationQueryBuilder', (): void => {
144170
it('should work with one entity', () => {
145171
assertManyToOneSQL(testRelation, 'testEntity', {}, ``, [testRelation.testRelationPk]);
146172
});
173+
174+
it('should work with a uni-directional relationship', () => {
175+
assertManyToOneUniDirectionalSQL(testRelation, 'testEntityUniDirectional', {}, ``, [
176+
testRelation.testRelationPk,
177+
]);
178+
});
147179
});
148180

149181
describe('many to many', () => {
@@ -164,6 +196,12 @@ describe('RelationQueryBuilder', (): void => {
164196
assertManyToManyCustomJoinSQL(testEntity, 'testEntityRelation', {}, ``, [testEntity.testEntityPk]);
165197
});
166198
});
199+
200+
describe('uni-directional many to many', () => {
201+
it('should create the correct sql', () => {
202+
assertManyToManyUniDirectionalSQL(testEntity, 'manyToManyUniDirectional', {}, ``, [testEntity.testEntityPk]);
203+
});
204+
});
167205
});
168206

169207
describe('one to one', () => {

packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ describe('TypeOrmQueryService', (): void => {
9797
});
9898
expect(queryResults).toEqual(TEST_RELATIONS.slice(0, 6));
9999
});
100+
101+
it('should allow filtering on a uni directional many to one relation', async () => {
102+
const queryService = moduleRef.get(TestRelationService);
103+
const queryResults = await queryService.query({
104+
filter: {
105+
testEntityUniDirectional: {
106+
testEntityPk: {
107+
in: [TEST_ENTITIES[0].testEntityPk, TEST_ENTITIES[1].testEntityPk],
108+
},
109+
},
110+
},
111+
});
112+
expect(queryResults).toEqual(TEST_RELATIONS.slice(0, 6));
113+
});
100114
});
101115

102116
describe('oneToMany', () => {
@@ -115,6 +129,42 @@ describe('TypeOrmQueryService', (): void => {
115129
expect(queryResult).toEqual([entity]);
116130
});
117131
});
132+
133+
describe('manyToMany', () => {
134+
it('should allow filtering on a many to many relation', async () => {
135+
const queryService = moduleRef.get(TestEntityService);
136+
const queryResult = await queryService.query({
137+
filter: {
138+
manyTestRelations: {
139+
relationName: {
140+
in: [TEST_RELATIONS[1].relationName, TEST_RELATIONS[4].relationName],
141+
},
142+
},
143+
},
144+
});
145+
expect(queryResult).toEqual([
146+
TEST_ENTITIES[1],
147+
TEST_ENTITIES[3],
148+
TEST_ENTITIES[5],
149+
TEST_ENTITIES[7],
150+
TEST_ENTITIES[9],
151+
]);
152+
});
153+
154+
it('should allow filtering on a many to many uni-directional relation', async () => {
155+
const queryService = moduleRef.get(TestEntityService);
156+
const queryResult = await queryService.query({
157+
filter: {
158+
manyToManyUniDirectional: {
159+
relationName: {
160+
in: [TEST_RELATIONS[2].relationName, TEST_RELATIONS[5].relationName],
161+
},
162+
},
163+
},
164+
});
165+
expect(queryResult).toEqual([TEST_ENTITIES[2], TEST_ENTITIES[5], TEST_ENTITIES[8]]);
166+
});
167+
});
118168
});
119169
});
120170

@@ -281,6 +331,17 @@ describe('TypeOrmQueryService', (): void => {
281331
TEST_RELATIONS[2].testRelationPk,
282332
]);
283333
});
334+
335+
describe('manyToMany', () => {
336+
it('call select and return the with a uni-directional relation', async () => {
337+
const entity = TEST_ENTITIES[2];
338+
const queryService = moduleRef.get(TestEntityService);
339+
const queryResult = await queryService.queryRelations(TestRelation, 'manyToManyUniDirectional', entity, {});
340+
TEST_RELATIONS.filter((tr) => tr.relationName.endsWith('three')).forEach((tr) => {
341+
expect(queryResult).toContainEqual(tr);
342+
});
343+
});
344+
});
284345
});
285346

286347
describe('with multiple entities', () => {
@@ -660,6 +721,16 @@ describe('TypeOrmQueryService', (): void => {
660721
'Unable to find relation badRelation on TestEntity',
661722
);
662723
});
724+
725+
describe('manyToOne', () => {
726+
it('call select and return the with a uni-directional relation', async () => {
727+
const entity = TEST_RELATIONS[0];
728+
const queryService = moduleRef.get(TestRelationService);
729+
const queryResult = await queryService.findRelation(TestEntity, 'testEntityUniDirectional', entity);
730+
731+
expect(queryResult).toEqual(TEST_ENTITIES[0]);
732+
});
733+
});
663734
});
664735

665736
describe('with multiple entities', () => {

packages/query-typeorm/src/query/relation-query.builder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export class RelationQueryBuilder<Entity, Relation> {
221221
})),
222222
},
223223
];
224-
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
224+
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
225225
selectPath: `${relation.propertyName}.${pk.propertyName}`,
226226
databasePath: pk.databasePath,
227227
propertyName: pk.propertyName,
@@ -249,7 +249,7 @@ export class RelationQueryBuilder<Entity, Relation> {
249249
getOneToManyOrOneToOneNotOwnerMeta(relation: RelationMetadata): RelationQuery<Relation, Entity> {
250250
const aliasName = relation.propertyName;
251251
const columns = relation.inverseRelation!.joinColumns;
252-
const fromPrimaryKeys: PrimaryKey[] = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
252+
const fromPrimaryKeys: PrimaryKey[] = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
253253
selectPath: `${aliasName}.${pk.propertyName}`,
254254
databasePath: pk.databasePath,
255255
propertyName: pk.propertyName,
@@ -287,7 +287,7 @@ export class RelationQueryBuilder<Entity, Relation> {
287287
})),
288288
},
289289
];
290-
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
290+
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
291291
selectPath: `${mainAlias}.${pk.propertyName}`,
292292
databasePath: pk.databasePath,
293293
propertyName: pk.propertyName,
@@ -325,7 +325,7 @@ export class RelationQueryBuilder<Entity, Relation> {
325325
})),
326326
},
327327
];
328-
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
328+
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
329329
selectPath: `${mainAlias}.${pk.propertyName}`,
330330
databasePath: pk.databasePath,
331331
propertyName: pk.propertyName,

0 commit comments

Comments
 (0)