diff --git a/app/__test__/data/specs/Repository.spec.ts b/app/__test__/data/specs/Repository.spec.ts index d9b2dc2..efb987d 100644 --- a/app/__test__/data/specs/Repository.spec.ts +++ b/app/__test__/data/specs/Repository.spec.ts @@ -124,6 +124,81 @@ describe("[Repository]", async () => { .then((r) => [r.count, r.total]), ).resolves.toEqual([undefined, undefined]); }); + + test("auto join", async () => { + const schema = $em( + { + posts: $entity("posts", { + title: $text(), + content: $text(), + }), + comments: $entity("comments", { + content: $text(), + }), + another: $entity("another", { + title: $text(), + }), + }, + ({ relation }, { posts, comments }) => { + relation(comments).manyToOne(posts); + }, + ); + const em = schema.proto.withConnection(getDummyConnection().dummyConnection); + await em.schema().sync({ force: true }); + + await em.mutator("posts").insertOne({ title: "post1", content: "content1" }); + await em + .mutator("comments") + .insertMany([{ content: "comment1", posts_id: 1 }, { content: "comment2" }] as any); + + const res = await em.repo("comments").findMany({ + where: { + "posts.title": "post1", + }, + }); + expect(res.data as any).toEqual([ + { + id: 1, + content: "comment1", + posts_id: 1, + }, + ]); + + { + // manual join should still work + const res = await em.repo("comments").findMany({ + join: ["posts"], + where: { + "posts.title": "post1", + }, + }); + expect(res.data as any).toEqual([ + { + id: 1, + content: "comment1", + posts_id: 1, + }, + ]); + } + + // inexistent should be detected and thrown + expect( + em.repo("comments").findMany({ + where: { + "random.title": "post1", + }, + }), + ).rejects.toThrow(/Invalid where field/); + + // existing alias, but not a relation should throw + expect( + em.repo("comments").findMany({ + where: { + "another.title": "post1", + }, + }), + ).rejects.toThrow(/Invalid where field/); + }); }); describe("[data] Repository (Events)", async () => { diff --git a/app/src/data/entities/query/Repository.ts b/app/src/data/entities/query/Repository.ts index 3d8f432..218f25e 100644 --- a/app/src/data/entities/query/Repository.ts +++ b/app/src/data/entities/query/Repository.ts @@ -103,6 +103,7 @@ export class Repository 0) { for (const entry of options.join) { const related = this.em.relationOf(entity.name, entry); @@ -127,12 +128,28 @@ export class Repository { if (field.includes(".")) { const [alias, prop] = field.split(".") as [string, string]; - if (!aliases.includes(alias)) { + // check aliases first (added joins) + if (aliases.includes(alias)) { + this.checkIndex(alias, prop, "where"); + return !this.em.entity(alias).getField(prop); + } + // check if alias (entity) exists + if (!this.em.hasEntity(alias)) { return true; } + // check related fields for auto join + const related = this.em.relationOf(entity.name, alias); + if (related) { + const other = related.other(entity); + if (other.entity.getField(prop)) { + // if related field is found, add join to validated options + validated.join?.push(alias); + this.checkIndex(alias, prop, "where"); + return false; + } + } - this.checkIndex(alias, prop, "where"); - return !this.em.entity(alias).getField(prop); + return true; } this.checkIndex(entity.name, field, "where"); diff --git a/app/src/data/prototype/index.ts b/app/src/data/prototype/index.ts index 43df6a1..0741f95 100644 --- a/app/src/data/prototype/index.ts +++ b/app/src/data/prototype/index.ts @@ -289,7 +289,7 @@ class EntityManagerPrototype> extends En super(Object.values(__entities), new DummyConnection(), relations, indices); } - withConnection(connection: Connection): EntityManager> { + withConnection(connection: Connection): EntityManager> { return new EntityManager(this.entities, connection, this.relations.all, this.indices); } }