From 2f6687ababadde684f9ca1e6dfb2912bd658a95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Tue, 5 May 2020 23:03:45 +0200 Subject: [PATCH] feat(sql): add `groupBy` to `FindOptions` --- ROADMAP.md | 2 +- packages/core/src/drivers/IDatabaseDriver.ts | 2 ++ packages/knex/src/AbstractSqlDriver.ts | 8 +++++++- tests/EntityManager.mysql.test.ts | 20 ++++++++++++++------ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 84396d219d8e..7e8a73d502dd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -30,13 +30,13 @@ discuss specifics. - [x] Nested conditions in `em.remove()` via subqueries (#492) - [x] Use custom errors for specific cases (unique constraint violation, db not accessible, ...) - [x] Paginator helper or something similar ([doctrine docs](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/pagination.html)) +- [x] Add `groupBy` and `distinct` to `FindOptions` and `FindOneOptions` - [ ] Lazy scalar properties (allow having props that won't be loaded by default, but can be populated) - [ ] Support computed properties - [ ] Association scopes/filters ([hibernate docs](https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/filters.html)) - [ ] Support external hooks when using EntitySchema (hooks outside of entity) - [ ] Cache metadata only with ts-morph provider - [ ] Diffing entity level indexes in schema generator -- [ ] Add `groupBy` and `distinct` to `FindOptions` and `FindOneOptions` - [ ] Add custom types for blob, array, json - [ ] Seeds (#251) - [ ] Static checking of population (#214) diff --git a/packages/core/src/drivers/IDatabaseDriver.ts b/packages/core/src/drivers/IDatabaseDriver.ts index 4083c9aa1387..c65017a3b493 100644 --- a/packages/core/src/drivers/IDatabaseDriver.ts +++ b/packages/core/src/drivers/IDatabaseDriver.ts @@ -83,11 +83,13 @@ export interface FindOptions { fields?: string[]; schema?: string; flags?: QueryFlag[]; + groupBy?: string | string[]; } export interface FindOneOptions { populate?: string[] | boolean; orderBy?: QueryOrderMap; + groupBy?: string | string[]; lockMode?: LockMode; lockVersion?: number | Date; refresh?: boolean; diff --git a/packages/knex/src/AbstractSqlDriver.ts b/packages/knex/src/AbstractSqlDriver.ts index da793b1e52af..4d1fe63fc0d4 100644 --- a/packages/knex/src/AbstractSqlDriver.ts +++ b/packages/knex/src/AbstractSqlDriver.ts @@ -39,7 +39,12 @@ export abstract class AbstractSqlDriver { await orm.em.flush(); orm.em.clear(); + const mock = jest.fn(); + const logger = new Logger(mock, true); + Object.assign(orm.em.config, { logger }); + // without paginate flag it fails to get 5 records const res1 = await orm.em.find(Author2, { books: { title: /^Bible/ } }, { orderBy: { name: QueryOrder.ASC, books: { title: QueryOrder.ASC } }, limit: 5, + groupBy: ['id', 'name', 'e1.title'], }); expect(res1).toHaveLength(2); expect(res1.map(a => a.name)).toEqual(['God 01', 'God 02']); - - const mock = jest.fn(); - const logger = new Logger(mock, true); - Object.assign(orm.em.config, { logger }); + expect(mock.mock.calls[0][0]).toMatch('select `e0`.*, `e2`.`author_id` as `address_author_id` ' + + 'from `author2` as `e0` ' + + 'left join `book2` as `e1` on `e0`.`id` = `e1`.`author_id` ' + + 'left join `address2` as `e2` on `e0`.`id` = `e2`.`author_id` ' + + 'where `e1`.`title` like ? ' + + 'group by `e0`.`id`, `e0`.`name`, `e1`.`title` ' + + 'order by `e0`.`name` asc, `e1`.`title` asc limit ?'); // with paginate flag (and a bit of dark sql magic) we get what we want const res2 = await orm.em.find(Author2, { books: { title: /^Bible/ } }, { @@ -2000,7 +2008,7 @@ describe('EntityManagerMySql', () => { expect(res2).toHaveLength(5); expect(res2.map(a => a.name)).toEqual(['God 04', 'God 05', 'God 06', 'God 07', 'God 08']); - expect(mock.mock.calls[0][0]).toMatch('select `e0`.*, `e2`.`author_id` as `address_author_id` ' + + expect(mock.mock.calls[1][0]).toMatch('select `e0`.*, `e2`.`author_id` as `address_author_id` ' + 'from `author2` as `e0` ' + 'left join `book2` as `e1` on `e0`.`id` = `e1`.`author_id` ' + 'left join `address2` as `e2` on `e0`.`id` = `e2`.`author_id` where `e0`.`id` in (select `e0`.`id` ' +