From f95e9d8f9a6c7a1117564b3e3f65b5294f8d5ff5 Mon Sep 17 00:00:00 2001 From: maddimax Date: Tue, 6 Oct 2020 10:32:28 +0200 Subject: [PATCH] fix: allow for complex jsonb primary key columns (#6834) * fix: allow for complex jsonb primary key columns When using PrimaryColumn with a 'jsonb' type column ( on a postgres database ) the RawSqlResultsToEntityTransformer would simply convert the key to '[object Object]' instead of a unique value based on the key value. It would then wrongly consider each entity as having the same key. To allow for complex keys the transformer now uses JSON.stringify() to convert the keys value if they are of type obejct to a unique string that can be compared to other entities. fixes #6833 * test: add tests for jsonb primarykey columns These tests verify that Entities with jsonb primary columns are correctly converted to Entites --- .../RawSqlResultsToEntityTransformer.ts | 4 + test/github-issues/6833/entity/test.ts | 12 +++ test/github-issues/6833/issue-6833.ts | 81 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 test/github-issues/6833/entity/test.ts create mode 100644 test/github-issues/6833/issue-6833.ts diff --git a/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts b/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts index 34adec20bb..e79fd24d63 100644 --- a/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts +++ b/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts @@ -70,6 +70,10 @@ export class RawSqlResultsToEntityTransformer { return keyValue.toString("hex"); } + if (typeof keyValue === "object") { + return JSON.stringify(keyValue); + } + return keyValue; }).join("_"); // todo: check partial diff --git a/test/github-issues/6833/entity/test.ts b/test/github-issues/6833/entity/test.ts new file mode 100644 index 0000000000..e0cb47ad6b --- /dev/null +++ b/test/github-issues/6833/entity/test.ts @@ -0,0 +1,12 @@ +import { Entity, PrimaryColumn } from "../../../../src"; + +export class MyId { + first: number; + second: number; +} + +@Entity({ name: "jsonb_key_tests" }) +export class JSONBKeyTest { + @PrimaryColumn("jsonb") + id: MyId; +} diff --git a/test/github-issues/6833/issue-6833.ts b/test/github-issues/6833/issue-6833.ts new file mode 100644 index 0000000000..4e1ed1179b --- /dev/null +++ b/test/github-issues/6833/issue-6833.ts @@ -0,0 +1,81 @@ +import "reflect-metadata"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { JSONBKeyTest } from "./entity/test"; + +describe("github issues > #6833 Entities with JSON key columns are incorrectly grouped", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [JSONBKeyTest], + dropSchema: true, + schemaCreate: true, + enabledDrivers: ["postgres"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("jsonB keys are correctly resolved", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 1, second: 2 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 1, second: 3 } })); + + const entities = await manager.createQueryBuilder(JSONBKeyTest, "json_test").select().getMany(); + expect(entities.length).to.equal(2); + }); + }))); + + it("jsonB keys can be found", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + + const entities = await manager.find(JSONBKeyTest, { where: { id: { first: 3, second: 3 } } } ); + expect(entities.length).to.equal(1); + expect(entities[0].id).to.deep.equal({ first: 3, second: 3 }); + }); + }))); + + it("jsonB keys can be found with IN", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 5, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 6, second: 4 } })); + + const entities = await manager + .createQueryBuilder(JSONBKeyTest, "json_test") + .select() + .where("id IN (:...ids)", { ids: [{first: 5, second: 3}, {first: 6, second: 4}]}) + .getMany(); + expect(entities.length).to.equal(2); + expect(entities[0].id).to.deep.equal({ first: 5, second: 3 }); + expect(entities[1].id).to.deep.equal({ first: 6, second: 4 }); + }); + }))); + + it("jsonB keys can be found regardless of order", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 5, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 6, second: 4 } })); + + + const payload = { second: 2, first: 1 }; + await manager.save(manager.create(JSONBKeyTest, { id: payload })); + const entities = await manager.find(JSONBKeyTest, { where: { id: payload } }); + expect(entities.length).to.equal(1); + expect(entities[0].id).to.deep.equal({ first: 1, second: 2 }); + + const entitiesOtherOrder = await manager.find(JSONBKeyTest, { where: { id: {first: 1, second: 2} } }); + expect(entitiesOtherOrder.length).to.equal(1); + expect(entitiesOtherOrder[0].id).to.deep.equal({ first: 1, second: 2 }); + + }); + }))); +});