Skip to content

Commit

Permalink
fix(typeorm,#493): Fix uni-directional relation SQL
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-martin committed Sep 2, 2020
1 parent 60adfaa commit 7887b8c
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/query-typeorm/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ module.exports = {
'assertUpdateSQL',
'assertDeleteSQL',
'assertSoftDeleteSQL',
'assertManyToOneUniDirectionalSQL',
'assertManyToManyUniDirectionalSQL'
],
},
],
Expand Down
11 changes: 11 additions & 0 deletions packages/query-typeorm/__tests__/__fixtures__/seeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ export const TEST_RELATIONS: TestRelation[] = TEST_ENTITIES.reduce((relations, t
testRelationPk: `test-relations-${te.testEntityPk}-1`,
relationName: `${te.stringType}-test-relation-one`,
testEntityId: te.testEntityPk,
uniDirectionalTestEntityId: te.testEntityPk,
},
{
testRelationPk: `test-relations-${te.testEntityPk}-2`,
relationName: `${te.stringType}-test-relation-two`,
testEntityId: te.testEntityPk,
uniDirectionalTestEntityId: te.testEntityPk,
},
{
testRelationPk: `test-relations-${te.testEntityPk}-3`,
relationName: `${te.stringType}-test-relation-three`,
testEntityId: te.testEntityPk,
uniDirectionalTestEntityId: te.testEntityPk,
},
];
}, [] as TestRelation[]);
Expand All @@ -56,6 +59,14 @@ export const seed = async (connection: Connection = getConnection()): Promise<vo
testEntities.map((te) => {
// eslint-disable-next-line no-param-reassign
te.oneTestRelation = testRelations.find((tr) => tr.testRelationPk === `test-relations-${te.testEntityPk}-1`);
if (te.numberType % 2 === 0) {
// eslint-disable-next-line no-param-reassign
te.manyTestRelations = testRelations.filter((tr) => tr.relationName.endsWith('two'));
}
if (te.numberType % 3 === 0) {
// eslint-disable-next-line no-param-reassign
te.manyToManyUniDirectional = testRelations.filter((tr) => tr.relationName.endsWith('three'));
}
return testEntityRepo.save(te);
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ export class TestRelation {
@Column({ name: 'test_entity_id', nullable: true })
testEntityId?: string;

@Column({ name: 'uni_directional_test_entity_id', nullable: true })
uniDirectionalTestEntityId?: string;

@ManyToOne(() => TestEntity, (te) => te.testRelations, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'test_entity_id' })
testEntity?: TestEntity;

@ManyToOne(() => TestEntity, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'uni_directional_test_entity_id' })
testEntityUniDirectional?: TestEntity;

@ManyToMany(() => TestEntity, (te) => te.manyTestRelations, { onDelete: 'CASCADE', nullable: false })
manyTestEntities?: TestEntity[];

Expand Down
4 changes: 4 additions & 0 deletions packages/query-typeorm/__tests__/__fixtures__/test.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export class TestEntity {
@JoinTable()
manyTestRelations?: TestRelation[];

@ManyToMany(() => TestRelation, { onDelete: 'CASCADE', nullable: false })
@JoinTable()
manyToManyUniDirectional?: TestRelation[];

@OneToOne(() => TestRelation, (relation) => relation.oneTestEntity)
@JoinColumn()
oneTestRelation?: TestRelation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ describe('RelationQueryBuilder', (): void => {
` INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."test_entity_id" = "testEntity"."test_entity_pk"` +
` WHERE ("TestRelation"."test_relation_pk" = ?)`;

const manyToOneSelectUniDirectional =
'SELECT "testEntityUniDirectional"."test_entity_pk" AS "testEntityUniDirectional_test_entity_pk",' +
' "testEntityUniDirectional"."string_type" AS "testEntityUniDirectional_string_type",' +
' "testEntityUniDirectional"."bool_type" AS "testEntityUniDirectional_bool_type",' +
' "testEntityUniDirectional"."number_type" AS "testEntityUniDirectional_number_type",' +
' "testEntityUniDirectional"."date_type" AS "testEntityUniDirectional_date_type",' +
' "testEntityUniDirectional"."oneTestRelationTestRelationPk" AS "testEntityUniDirectional_oneTestRelationTestRelationPk"' +
' FROM "test_entity" "testEntityUniDirectional"' +
' INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."uni_directional_test_entity_id" = "testEntityUniDirectional"."test_entity_pk"' +
' WHERE ("TestRelation"."test_relation_pk" = ?)';

const manyToManyNonOwnerSelectQuery =
`SELECT` +
` "manyTestEntities"."test_entity_pk" AS "manyTestEntities_test_entity_pk",` +
Expand All @@ -37,15 +48,17 @@ describe('RelationQueryBuilder', (): void => {
`SELECT` +
` "testRelations"."test_relation_pk" AS "testRelations_test_relation_pk",` +
` "testRelations"."relation_name" AS "testRelations_relation_name",` +
` "testRelations"."test_entity_id" AS "testRelations_test_entity_id"` +
` "testRelations"."test_entity_id" AS "testRelations_test_entity_id",` +
` "testRelations"."uni_directional_test_entity_id" AS "testRelations_uni_directional_test_entity_id"` +
` FROM "test_relation" "testRelations"` +
' WHERE ("testRelations"."test_entity_id" = ?)';

const manyToManyOwnerSelect =
'SELECT ' +
`"manyTestRelations"."test_relation_pk" AS "manyTestRelations_test_relation_pk",` +
' "manyTestRelations"."relation_name" AS "manyTestRelations_relation_name",' +
' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id"' +
' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id",' +
' "manyTestRelations"."uni_directional_test_entity_id" AS "manyTestRelations_uni_directional_test_entity_id"' +
` FROM "test_relation" "manyTestRelations"` +
` 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"` +
' WHERE ("test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = ?)';
Expand All @@ -54,7 +67,8 @@ describe('RelationQueryBuilder', (): void => {
`SELECT` +
` "oneTestRelation"."test_relation_pk" AS "oneTestRelation_test_relation_pk",` +
` "oneTestRelation"."relation_name" AS "oneTestRelation_relation_name",` +
` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id"` +
` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id",` +
` "oneTestRelation"."uni_directional_test_entity_id" AS "oneTestRelation_uni_directional_test_entity_id"` +
` FROM "test_relation" "oneTestRelation"` +
` INNER JOIN "test_entity" "TestEntity" ON "TestEntity"."oneTestRelationTestRelationPk" = "oneTestRelation"."test_relation_pk"` +
' WHERE ("TestEntity"."test_entity_pk" = ?)';
Expand All @@ -77,6 +91,16 @@ describe('RelationQueryBuilder', (): void => {
` FROM "test_entity_relation_entity" "testEntityRelation"` +
` WHERE ("testEntityRelation"."test_entity_id" = ?)`;

const manyToManyUniDirectionalSelect =
'SELECT' +
' "manyToManyUniDirectional"."test_relation_pk" AS "manyToManyUniDirectional_test_relation_pk",' +
' "manyToManyUniDirectional"."relation_name" AS "manyToManyUniDirectional_relation_name",' +
' "manyToManyUniDirectional"."test_entity_id" AS "manyToManyUniDirectional_test_entity_id",' +
' "manyToManyUniDirectional"."uni_directional_test_entity_id" AS "manyToManyUniDirectional_uni_directional_test_entity_id"' +
' FROM "test_relation" "manyToManyUniDirectional" ' +
'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" ' +
'WHERE ("test_entity_many_to_many_uni_directional_test_relation"."testEntityTestEntityPk" = ?)';

const getRelationQueryBuilder = <Entity, Relation>(
EntityClass: Class<Entity>,
relationName: string,
Expand Down Expand Up @@ -105,6 +129,7 @@ describe('RelationQueryBuilder', (): void => {
const assertManyToManyOwnerSQL = createSQLAsserter(TestEntity, manyToManyOwnerSelect);

const assertManyToOneSQL = createSQLAsserter(TestRelation, manyToOneSelect);
const assertManyToOneUniDirectionalSQL = createSQLAsserter(TestRelation, manyToOneSelectUniDirectional);

const assertManyToManyNonOwnerSQL = createSQLAsserter(TestRelation, manyToManyNonOwnerSelectQuery);

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

const assertManyToManyCustomJoinSQL = createSQLAsserter(TestEntity, manyToManyCustomJoinSelect);
const assertManyToManyUniDirectionalSQL = createSQLAsserter(TestEntity, manyToManyUniDirectionalSelect);

describe('#select', () => {
const testEntity: TestEntity = {
Expand Down Expand Up @@ -144,6 +170,12 @@ describe('RelationQueryBuilder', (): void => {
it('should work with one entity', () => {
assertManyToOneSQL(testRelation, 'testEntity', {}, ``, [testRelation.testRelationPk]);
});

it('should work with a uni-directional relationship', () => {
assertManyToOneUniDirectionalSQL(testRelation, 'testEntityUniDirectional', {}, ``, [
testRelation.testRelationPk,
]);
});
});

describe('many to many', () => {
Expand All @@ -164,6 +196,12 @@ describe('RelationQueryBuilder', (): void => {
assertManyToManyCustomJoinSQL(testEntity, 'testEntityRelation', {}, ``, [testEntity.testEntityPk]);
});
});

describe('uni-directional many to many', () => {
it('should create the correct sql', () => {
assertManyToManyUniDirectionalSQL(testEntity, 'manyToManyUniDirectional', {}, ``, [testEntity.testEntityPk]);
});
});
});

describe('one to one', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ describe('TypeOrmQueryService', (): void => {
});
expect(queryResults).toEqual(TEST_RELATIONS.slice(0, 6));
});

it('should allow filtering on a uni directional many to one relation', async () => {
const queryService = moduleRef.get(TestRelationService);
const queryResults = await queryService.query({
filter: {
testEntityUniDirectional: {
testEntityPk: {
in: [TEST_ENTITIES[0].testEntityPk, TEST_ENTITIES[1].testEntityPk],
},
},
},
});
expect(queryResults).toEqual(TEST_RELATIONS.slice(0, 6));
});
});

describe('oneToMany', () => {
Expand All @@ -115,6 +129,42 @@ describe('TypeOrmQueryService', (): void => {
expect(queryResult).toEqual([entity]);
});
});

describe('manyToMany', () => {
it('should allow filtering on a many to many relation', async () => {
const queryService = moduleRef.get(TestEntityService);
const queryResult = await queryService.query({
filter: {
manyTestRelations: {
relationName: {
in: [TEST_RELATIONS[1].relationName, TEST_RELATIONS[4].relationName],
},
},
},
});
expect(queryResult).toEqual([
TEST_ENTITIES[1],
TEST_ENTITIES[3],
TEST_ENTITIES[5],
TEST_ENTITIES[7],
TEST_ENTITIES[9],
]);
});

it('should allow filtering on a many to many uni-directional relation', async () => {
const queryService = moduleRef.get(TestEntityService);
const queryResult = await queryService.query({
filter: {
manyToManyUniDirectional: {
relationName: {
in: [TEST_RELATIONS[2].relationName, TEST_RELATIONS[5].relationName],
},
},
},
});
expect(queryResult).toEqual([TEST_ENTITIES[2], TEST_ENTITIES[5], TEST_ENTITIES[8]]);
});
});
});
});

Expand Down Expand Up @@ -281,6 +331,17 @@ describe('TypeOrmQueryService', (): void => {
TEST_RELATIONS[2].testRelationPk,
]);
});

describe('manyToMany', () => {
it('call select and return the with a uni-directional relation', async () => {
const entity = TEST_ENTITIES[2];
const queryService = moduleRef.get(TestEntityService);
const queryResult = await queryService.queryRelations(TestRelation, 'manyToManyUniDirectional', entity, {});
TEST_RELATIONS.filter((tr) => tr.relationName.endsWith('three')).forEach((tr) => {
expect(queryResult).toContainEqual(tr);
});
});
});
});

describe('with multiple entities', () => {
Expand Down Expand Up @@ -660,6 +721,16 @@ describe('TypeOrmQueryService', (): void => {
'Unable to find relation badRelation on TestEntity',
);
});

describe('manyToOne', () => {
it('call select and return the with a uni-directional relation', async () => {
const entity = TEST_RELATIONS[0];
const queryService = moduleRef.get(TestRelationService);
const queryResult = await queryService.findRelation(TestEntity, 'testEntityUniDirectional', entity);

expect(queryResult).toEqual(TEST_ENTITIES[0]);
});
});
});

describe('with multiple entities', () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/query-typeorm/src/query/relation-query.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class RelationQueryBuilder<Entity, Relation> {
})),
},
];
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
selectPath: `${relation.propertyName}.${pk.propertyName}`,
databasePath: pk.databasePath,
propertyName: pk.propertyName,
Expand Down Expand Up @@ -249,7 +249,7 @@ export class RelationQueryBuilder<Entity, Relation> {
getOneToManyOrOneToOneNotOwnerMeta(relation: RelationMetadata): RelationQuery<Relation, Entity> {
const aliasName = relation.propertyName;
const columns = relation.inverseRelation!.joinColumns;
const fromPrimaryKeys: PrimaryKey[] = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
const fromPrimaryKeys: PrimaryKey[] = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
selectPath: `${aliasName}.${pk.propertyName}`,
databasePath: pk.databasePath,
propertyName: pk.propertyName,
Expand Down Expand Up @@ -287,7 +287,7 @@ export class RelationQueryBuilder<Entity, Relation> {
})),
},
];
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
selectPath: `${mainAlias}.${pk.propertyName}`,
databasePath: pk.databasePath,
propertyName: pk.propertyName,
Expand Down Expand Up @@ -325,7 +325,7 @@ export class RelationQueryBuilder<Entity, Relation> {
})),
},
];
const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({
const fromPrimaryKeys = relation.inverseEntityMetadata.primaryColumns.map((pk) => ({
selectPath: `${mainAlias}.${pk.propertyName}`,
databasePath: pk.databasePath,
propertyName: pk.propertyName,
Expand Down

0 comments on commit 7887b8c

Please sign in to comment.