From ffff8dcf0bb6072503dda7fcd4d1aff488eabeaa Mon Sep 17 00:00:00 2001 From: Fernando Hurtado Date: Sun, 28 Jul 2024 15:09:50 +0200 Subject: [PATCH 1/6] refactor: rename groupingId to groupBy --- .../queryBuilder/createAugmentedQuery.ts | 2 +- .../backend/src/tests/e2e/payments.test.ts | 2 +- .../tests/e2e/store-with-customers.test.ts | 2 +- .../arbitraries/ArbitraryQueryBuilder.ts | 4 +- packages/backend/src/tests/queries.v2.ts | 2 +- .../static/reference/assets/navigation.js | 2 +- .../docs/static/reference/assets/search.js | 2 +- packages/queries/src/query.ts | 64 ++++++++++--------- packages/queries/src/types/types.ts | 2 +- packages/ui/public/tmp.ts | 2 +- 10 files changed, 44 insertions(+), 40 deletions(-) diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts index e01d16cb..60ec80f6 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts @@ -32,7 +32,7 @@ export function createAugmentedQuery( const wheres = collectWhere(rootQuery, defaultSchema); const joins = collectJoins(rootQuery, defaultSchema); const groupingColumns = - rootQuery.groupingId?.map((col) => rootTable.column(col)) ?? []; + rootQuery.groupBy?.map((col) => rootTable.column(col)) ?? []; return { selection: selectionColumn.concat(jsonbAggColumn), diff --git a/packages/backend/src/tests/e2e/payments.test.ts b/packages/backend/src/tests/e2e/payments.test.ts index e29d9221..e6c0d30d 100644 --- a/packages/backend/src/tests/e2e/payments.test.ts +++ b/packages/backend/src/tests/e2e/payments.test.ts @@ -11,7 +11,7 @@ import { sql } from '../postgres'; describe('e2e', () => { const q = from('payment') .columns('payment_id', 'amount', 'payment_date') - .groupingId('payment_id', 'payment_date') + .groupBy('payment_id', 'payment_date') .many(); test(describeQuery(q), async () => { diff --git a/packages/backend/src/tests/e2e/store-with-customers.test.ts b/packages/backend/src/tests/e2e/store-with-customers.test.ts index 62fe876d..c3a8cc08 100644 --- a/packages/backend/src/tests/e2e/store-with-customers.test.ts +++ b/packages/backend/src/tests/e2e/store-with-customers.test.ts @@ -15,7 +15,7 @@ import { sortRecursively } from '../sortRecursively'; // Skipping for now, not sure why this isn't working describe.skip('e2e', () => { const payments = from('payment') - .groupingId('payment_id', 'payment_date') + .groupBy('payment_id', 'payment_date') .columns('payment_id', 'amount') .where({ customer_id: col('customer.customer_id') }) .many(); diff --git a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts index 9bd8beea..98c0d189 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts @@ -114,7 +114,7 @@ export class ArbitraryQueryBuilder { return fc.constant({}); } - private arbGroupingId(tableName: Table): fc.Arbitrary { + private arbGroupBy(tableName: Table): fc.Arbitrary { return fc.constant(getPrimaryKeyColumns(this.schema, tableName)); } @@ -139,7 +139,7 @@ export class ArbitraryQueryBuilder { select: this.arbSelect(tableName), cardinality: this.arbCardinality(), where: this.arbWhere(tableName), - groupingId: this.arbGroupingId(tableName), + groupBy: this.arbGroupBy(tableName), limit: this.arbLimit(), }); }); diff --git a/packages/backend/src/tests/queries.v2.ts b/packages/backend/src/tests/queries.v2.ts index 52545ebe..829041cc 100644 --- a/packages/backend/src/tests/queries.v2.ts +++ b/packages/backend/src/tests/queries.v2.ts @@ -10,7 +10,7 @@ export function actor() { } export function filmActor() { - return from('film_actor').groupingId('actor_id', 'film_id').columns(); + return from('film_actor').groupBy('actor_id', 'film_id').columns(); } export function film() { diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index ad00d094..97c58f37 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA52WUW/bIBCA/wvP2bpma7flrcsirdu0ZEm0aaqqiuJLjUqwA7iqNe2/T8Z2bAMG0lfz8R3gO46bv0jBs0IzdI/JI/DkTAqCJijHKkUztM+SgoE8awbvpCCvU7VnaIIeKU/QbDpBJKUsEcDR7OYo+1mAKBf8gXLoZIRhKQ1ZDxyKz6cf/k1cvpXIctlJKVcgdpiMe/UEY9UXl6Z8xTCPs1akT7d4zgVIuSm5Sg/sC+YJA9GpVZkbVucEI8Kbj+/PL6ahKGs4FCDVycGaeS+MKfOMS3hB0HpiKCrJGAOivuP+xnYFJ4pmfBinhw6tl+/6QgFYQeA3jQQYn2oFvO2HZNRdWIRRR1H1ZlKuRCZzIMot6Maji/MBOAiswLVZQ9ei3s0dChAUpHt9zWD04uYZK/b8M+ycxdi3HUlfMR6h6hYAoSi47w6nuJsTFeIEs1e4ISnscVBWYz7RFt8ziDnKFvTJPlGORbnMzTrve1omVNItV+VWZt2NLmNNhrxzLBLKMaOq9El7WNCo/5hXpok4z7bMrXvSdlVUnO8XZkWEUGMh4zUnrEi8tgYJmb5mlPvzpCZCHt1tfRoNRFnWIAtm9UXLVWMh4xp2/u1pIGTZQNWqfJqaCHl07fo0GghZfqcgvBYNRFnmDBf2c8By1Vjwprj+cbX+c7dcLdZX2+V602mfsKDVzoz7wuCH/rfGu8LVAfs2kjHPO4JKIxPckgbziA7DRHdrNPTq3NuJBeCxd4Ieiu7CzdNmnnH9wdU/OuEQ9na2mtS19g2s2raULRjKEjmy3C5HOvWQNQ90kCHNKlYie6KJ+3FoLbmFvXkzeg4usYV71PIUsQE7cqsTFxKaVfidHRcls/6Y3+lMMV0Dt/8Bd3lmf8kOAAA=" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE52WUW/bIBCA/wvP2bpma7flrcsirdu0ZEm0aaqqiuJLjUqwA7iqNe2/T8Z2bAMG0lfz8R3gO46bv0jBs0IzdI/JI/DkTAqCJijHKkUztM+SgoE8awbvpCCvU7VnaIIeKU/QbDpBJKUsEcDR7OYo+1mAKBf8gXLoZIRhKQ1ZDxyKz6cf/k1cvpXIctlJKVcgdpiMe/UEY9UXl6Z8xTCPs1akT7d4zgVIuSm5Sg/sC+YJA9GpVZkbVucEI8Kbj+/PL6ahKGs4FCDVycGaeS+MKfOMS3hB0HpiKCrJGAOivuP+xnYFJ4pmfBinhw6tl+/6QgFYQeA3jQQYn2oFvO2HZNRdWIRRR1H1ZlKuRCZzIMot6Maji/MBOAiswLVZQ9ei3s0dChAUpHt9zWD04uYZK/b8M+ycxdi3HUlfMR6h6hYAoSi47w6nuJsTFeIEs1e4ISnscVBWYz7RFt8ziDnKFvTJPlGORbnMzTrve1omVNItV+VWZt2NLmNNhrxzLBLKMaOq9El7WNCo/5hXpok4z7bMrXvSdlVUnO8XZkWEUGMh4zUnrEi8tgYJmb5mlPvzpCZCHt1tfRoNRFnWIAtm9UXLVWMh4xp2/u1pIGTZQNWqfJqaCHl07fo0GghZfqcgvBYNRFnmDBf2c8By1Vjwprj+cbX+c7dcLdZX2+V602mfsKDVzoz7wuCH/rfGu8LVAfs2kjHPO4JKIxPckgbziA7DRHdrNPTq3NuJBeCxd4Ieiu7CzdNmnnH9wdU/OuEQ9na2mtS19g2s2raULRjKEjmy3C5HOvWQNQ90kCHNKlYie6KJ+3FoLbmFvXkzeg4usYV71PIUsQE7cqsTFxKaVfidHRcls/6Y3+lMMV0Dt/8Bd3lmf8kOAAA=" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 7c4b1e41..05ae85c8 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE71dW6+jSA7+KyvOPNKZVHE/b3OTtndXO33TjFZR64hJON3sJCQNpC/b6v++qqIAu2JDIJx5mfQZyvZX/mxX4UDlq1MeP1XO/ear82de7Jx76TpFesice+ePdPtnVuy+r8qt4zrncu/cO4fj7rzPqu/NtYeq3K7e14e94zrbfVpVWeXcO84391Lbdp+Tmrb7fIKWvKjLY3XKtjWprL88QeeHc1bmWUUqNNcmaCuzlAGnr4xqCv3eZcf9PtvW/0qrulP2eC62dX4sMAVgJKHcdU5pmRW1xSlAv04iEfRz+OXzqcyq6vWXon7/Yf/3tNjts7KDUH85WRFAjp8DJAwCL+xgPDwoU7Ps3nWyo+ZpLVO98yr7cM4AT1eCNWJPRtqrrDodi2qyE1u5WSSCCC6ztM6Gw4mJaF5ylrOCPqxenrPyy4t9WnQQ8qLOysd0a3mmGzjHolhLH3CTbc9qkn97kx+yaXbvOuEHIzwe0v0UGUDqWpEX7+bgaWUXhPM6q+u8eFdNRALE5oEYrDfTMKwmVJxeRTcDxjFvyvzdu6yc6hggtmCwzIyT5UDMADDb+EXF+KV4lxfZi/J4uo4NMP7m+qGszbF51whe6QE4QwZItX2fHdJ5WDrZ5eCcyuPHfHdtflwgguILgjoeZ7JlJG+DImMqbjtARjmHZk6oBgJs849FVZfnbX0sJ5m8w4KTXHAVEVeBuCCgrnbP8urZqcw/pnV2AxYrca5CQyTMYngyvak4gsy5ChIUWwaVXPuxhWpasN71QnOjhsLwQ7H7Pc3rWVB62UUQbY+HU76f6JVeaC6Gwc3RFAwr8/lMTN4kGW2WIjbJPkzMdw7gqtF0E8h2rlxxSsv0MDH7WLidsoURW4lx2qd5MTUjWqHZYdj77F1WZGUK6kN/72h1ftqRw8uZ1U3imjCEOdgX2h73w2Zgg4m0oQZ8GbGixzwTsywNJfK4tTv9x1jqAtONdPNfLpgey+NhLoaVEZ4EhApu3EB5iVhoGiUQj76+uP+H7Ix1seBUX1reRsUGefsKi9d7+CXtW1yWM9WcnGS/E1kGwaf3WTnJ5atWYhn7ebHdn3fTEPQyy2DY54d8GgmtxDL2j4+PVTYNQCeyDIJtWu7yIt3n9XVp3sLAcguxkf5vGggjsIz1d+XxfPpxGoBeZjYGry+2Pz7/9w+v/vPw64tfXv3w5tdXrzsoH9MyT/+wv3uxh88pw3a9/zEv0vLLrye1UTjaX28g42jgkqavMLqEuZ+uCnwwahGjx/35UAzZ0wOWM/Vbuj8PlVgwajmjb4bX8n7QEiafjy4jZsSN+9G8epU9guik92dm1BIT+8cxLwazoRmwhCldml5l1Xk/uhY1o5Ywit15aW62IyduLXs7U7aWDXpmKfmuzB6nWFw9NAJXmr1cRG6dsgZg1D4Tk5Gs9IQZZ9Rq5ZoPZtXKz4A0tvcZq8Zj0DoFy2M7XpcaNC4tvAgm+I3Pa/Z7DoitGTUnb3EK8d+qMNbuHka+TIFTN3PhQpb5ypOzPCFthg3vsmpb5ie1rkyxD8QWgXEqj6esrPOM/A6JQ9FLLQKizD6c8zLbTYHQyiwCIN3tcuXSdP9ilj8o+UWAfbfLHicheTACMwxf+zTAoOkpfTIj3MzxaXCsHh7yYpd9nrbgQWCWIrbFMSl4ByGutK6bUI6sOIt6dDWL72mAZxbLYdxY6VPCn1dkh9EjnU8Jfk5xHoYOND4l8MdjeUjr5WB3+p4S9K0r0fAUGO1PWmvyer9ksTHqnhRy9WbZAtnqe0rQh7zID+fDcqh7hU8KO/28MOxO4VPCzoolMRttCwOGN3Rv1J31z6BVwWBux918U3fNFgMZu7t6G9HN5fbNAkYwdUMwBuT6ZR/jmLi0j8G4dgHHICYt0mMQ5qxqGM7slYuABhOj6VD/fMVdVj/y5uRomklTLd71YuPTBvO67c6KATHlPg+oaKdwQ9UYBnT93cglqsV28SMQJ+b3ZKDXJvwIzEkVYDLIOSVhBPDsGnENeLJoXA34L1lPsbXrF9R+OrevqBaGqUvqKJQZSThrUR0FMjnJZiyroyBuSqIbF1YKHJkkM6D1In9t4lh2Z6QQmOzg42Dom7KpsJCKRcE1T13dBA6pWBScfijtJmxQw6LQivN+fxMyoGBZQqsXZX5Iyy//zL7MpxQruRWg/XjA67EHJJsBSzyJ8GbkG2p9fQlDv488cqmvL2bop316vnjL+cJcM+rGh2POVWbeRyaej+nf8e+HDdvrzwsgrVWNDv08CoxgyqQ1duxh8RHLefva9VW2L0ZPt32RFYz5htze9M2GwaSNrhfmxcDBKVtjb7LbR8tPx6LOPlMHPlCxZUbPcDZ82IG2DIrkxaRn27WaiLvTMS+mm7wDkiO2LfGh3WxW1c+LfAYaLDwbEHg8tqIp6R+Ovch6M3RC0r91neYb3fuvzsesrNQtyr0jV94qcVznMc/2O3UwTAPJVW+hHZSOt+bab9lWvwh4v2mGfL923M3a9YOVkP7bt+6mldAX9P/Qw4TjbgQ1TKBh0nE3khom0TDPcTceNcxDw3zH3fjUMB8NCxx3E1DDAjQsdNxNSA0L0bDIcTeRK4NV4IdoWISGxY67iSltMRqWOO4moYYl2L3K24LkQVhEaCZoKjAXQvlcSNeTK7EOXeG5Ml75ARbBvAjl/3agK3xKAlMkFBWC9L7ALImAc6zAPAnFhyCZEpgqoSgRvuslqyS2YGK2hGJFRKROTJhQxAiSWYE5k5ozklyJOZOKGLl2PX+1jgQeaeWPTiCSXYmpkooHKam5S0yRVDxIMt0kpkgqIiSZcRJzJEPeOuZIRvzcMUdSESHJUJKYI6mIkGSESMyRp4iQJO8e5sjTHJG8e5gjT3KB7FlVTlNEBoiHKfIUDx6Z/h6myFM8eGSAeJgiT/HgkcXYwxR5igePrseYIk/x4JEJ52GKvIR1EmbIVzR4AaXSxwz5gjXuY4Z8liEfM+R7vHFrKdIMheRIzJCvGYrIkZghXzMUkyMxQ75miIwkHzPkKxp8MpJ8zJCvePAFaR1TFCgefDKSAkxRoHjwyUgKMEWBIsInS02AOQoUET69vmOOAr1fIMtCYO0YAnbuAeYoUET4JJsB5ihQRPhkAQkwR4HmiGQzwBwFCRshAeYoVEQEJO8h5ihURARkBQkxR6EiIiB5DzFHoSIiIHkPMUehz6VmiCkK9a6ODJDQ2teFrErMUKhoCMhsDzFDYcyyHmKGQkVDQG8rMUPRml0II8xQxG8XIsxQpBmKXG+98uyRmKFIMxSTOjFDkeIhSNS2cR1IPBJTFCkewjU5ElMU6b23IEdau29FREjGXIQ5imJ2o44pitilKMIMxYqGkIziGDMUC9bvMWYolqzfY8xQ7LF+jzFDsc/6PcYMxZohMolizFDMMxRjhmLNEFmPY+sWSfEQkrkRY4piRURIbtNizFGiOSKrbII5ShQRIX3vhTlK9HaBrJ0J5ijxWN4TzFHis7wnmKMkYHlPMEdJyPKeYI6SiGUzwRwlOovI1SCx7mR1GpGZmdg3s2v2Vm9t3c2u+UxqrsGxkvVUcw2O9VhfNdfgWJ/1VnMNjtV3sWSNaK7BseHA3Kw72XXExktzDY6NB/xg3c2ukwE/WLTpTgPjh4suhOImIiuLsPsQgl+lhN2A0O2FiO4n2K0H3WCI6EaB3Xxoug90A8DuP+guQ0S3AOwOhO4zRGSVEXYPQnca6HsIYXchdK+B3qsIqw8hdLeBvuMQVidC6H5DTDeapN0/UtzEdKvJ6kYI3XOIyYaAsPoRQncdYjqHrI6E0H0HTq/Fm+48xHRMWl0JoXsPMR1nVl9C6O5DTPNmdSaE7j/EdJxZvQmhOxAxHWdWd0LoHgSDwfQndGf4Y1bW2e550yHebLqn5b465kWg+6htV391Iuf+6zfXEUHz6UnzmTSfvvk79M1n2HxGsfk041RRv//67VvfbVZ/KYj9UxrwOZYeSygAGKM9Np9Ji25tzKsaRJtBZ5732te9clZSn9fwcDSnRCBwvt/LB3JQgXqdFQiGQNAfEWzPsQDiARD3GHF0ugmQBQ71DT1BwOloD3fv5YG44KTU0Vq9hOf1Il7Ey6ivwvf6TPReFk6Ul9QvMIMpggAOTKyE8aD4Tj3yBjIAQFaL9ogoHboRiA614o0owbLAzckwdCwngVwyKGdnvZ8Arw2b/NicyAFkYyDLU2xOLuzlJJDzeA+BE0SBLHCvZCnSx41nzXHj5hu19+1B5SCo15BvRpfNUQh8HXGTRs8JgrgGE488U9ZMMU3aarbmlPbfkvYaYxD0ah3hJNULGWAOgPSEI6A7iBM4HxQw3lgjlxa7T/rUTCAOwHqcv7P2fHTElITTHJS0irUEtURy8d0dawjKF0hFj4uz0QgD/gqn6Cjb3x8ATPeqBqZBqWp/M6DXBelndLVvB4K5gMyLOY80J80BNwL3+2uzfeDWrv5ASKAAMO9xPtSHWf2BFzxQywMuxNWbyWCCQCTmwhM+DggKcAiLPre0d2fAgekBWn2zaAUcJ/bPpYAoB0HOCVen5qG3PzPkqCiB0Lnak1dl9oh3MwGoniHrrjYa1YNYtukYrreS89p/j3lhWQa5GXLcNiesgYAAPgo4uOaMPMAP8I7POad7Rw/EEuA14ax1ryQCOVAiYy4O+ocrAY0xpJFLlPYIPuAWwKLPFRbL/bCqcRjbM2yBK4EljyP7pA/5B4Ufrm8cvFP30wRAEG7DzFLLRndzUDhIJ5hPRlhyPqW3gQEInMgs7bFRlQTtUi/bf7BT68+0B/BA/Esu3+GTj5AFQMKAJEodD/jSM/d4Ppd3Wjozp9QDzMAwm+pA9tT8MgMgFAY4F3VawUUMwZuXQdilOUgM0AgiIeRcDX4kCiQWyCtWDt1+BEAk5BbYyzIMHBtylJr9RF7g6hZDn7I7sv4dD1CnQDJHJpxjE+ZJ2EZ1m3drjq/23CMQJ0CxFCb1zB1/ZDYQEUdie84sCF1Qq/yuX8BlG3wRAZTWAMYP5+Kq+/UaEHlQkosCfTg5cAAoHB5rrFlUt+0DhYBSuBORJlkFe5dlFJ26J2SBJlhB2eo3tLyjTXsbC+xN24XfA+C9sG36sKmvxe0behBMCZe9tf7dI0Aa2pW0DmSFaww6BKBjbr2pux/0AWYhcZy368q+fw+BWMzFtS0UwF6fmWJski3pkla0/2jzmb3z0r9LA6YCQlhyVeVcZVX74D2IGVir2bWtl6UyACad5CgwJ0KDSgF84rcLNIsevHkDwg1uVgQX51p0a95wALBhM0KS+frWdU75KdurpfV+8/bbt/8DJ+t8ZO9xAAA="; \ No newline at end of file diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index c2d5f5e5..70b7fca8 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -17,7 +17,7 @@ export class QueryBuilder< TOffset extends number | undefined, TCardinality extends 'one' | 'maybe' | 'many', TLazy extends true | undefined, - TGroupingId extends string[], + TGroupBy extends string[], > { constructor( private _from: TTable, @@ -28,7 +28,7 @@ export class QueryBuilder< private _offset: TOffset, private _cardinality: TCardinality, private _lazy: TLazy, - private _groupingId: TGroupingId, + private _groupBy: TGroupBy, ) {} private build(): { @@ -40,7 +40,7 @@ export class QueryBuilder< offset: TOffset; cardinality: TCardinality; lazy: TLazy; - groupingId: TGroupingId; + groupBy: TGroupBy; } { return { from: this._from, @@ -51,7 +51,7 @@ export class QueryBuilder< offset: this._offset, cardinality: this._cardinality ?? 'many', lazy: this._lazy, - groupingId: this._groupingId, + groupBy: this._groupBy, }; } @@ -69,7 +69,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -79,7 +79,7 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -97,7 +97,7 @@ export class QueryBuilder< TOffset, 'many', TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -107,7 +107,7 @@ export class QueryBuilder< this._offset, 'many', this._lazy, - this._groupingId, + this._groupBy, ).build(); } @@ -125,7 +125,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -135,7 +135,7 @@ export class QueryBuilder< offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -155,7 +155,7 @@ export class QueryBuilder< TOffset, 'one', TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -165,7 +165,7 @@ export class QueryBuilder< this._offset, 'one', this._lazy, - this._groupingId, + this._groupBy, ).build(); } @@ -183,7 +183,7 @@ export class QueryBuilder< TOffset, 'many', TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -193,7 +193,7 @@ export class QueryBuilder< this._offset, 'many', this._lazy, - this._groupingId, + this._groupBy, ).build(); } @@ -211,7 +211,7 @@ export class QueryBuilder< TOffset, 'maybe', TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -221,7 +221,7 @@ export class QueryBuilder< this._offset, 'maybe', this._lazy, - this._groupingId, + this._groupBy, ).build(); } @@ -236,7 +236,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -246,7 +246,7 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -283,7 +283,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -293,7 +293,7 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -308,7 +308,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -318,7 +318,7 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -333,7 +333,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, @@ -343,11 +343,15 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } where>(where: TWhere) { + return this.filter(where); + } + + filter>(where: TWhere) { return new QueryBuilder< DB, TTable, @@ -358,7 +362,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, where, @@ -368,7 +372,7 @@ export class QueryBuilder< this._offset, this._cardinality, this._lazy, - this._groupingId, + this._groupBy, ); } @@ -383,7 +387,7 @@ export class QueryBuilder< TOffset, TCardinality, true, - TGroupingId + TGroupBy >( this._from, this._where, @@ -393,11 +397,11 @@ export class QueryBuilder< this._offset, this._cardinality, true, - this._groupingId, + this._groupBy, ); } - groupingId[]>(...id: TGroupingId) { + groupBy[]>(...id: TGroupBy) { return new QueryBuilder< DB, TTable, @@ -408,7 +412,7 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupingId + TGroupBy >( this._from, this._where, diff --git a/packages/queries/src/types/types.ts b/packages/queries/src/types/types.ts index 539d23a1..725d2adc 100644 --- a/packages/queries/src/types/types.ts +++ b/packages/queries/src/types/types.ts @@ -12,5 +12,5 @@ export type Query = Table> = { offset?: number; cardinality?: 'one' | 'maybe' | 'many'; lazy?: true; - groupingId?: string[]; + groupBy?: string[]; }; diff --git a/packages/ui/public/tmp.ts b/packages/ui/public/tmp.ts index a5012956..c0922a16 100644 --- a/packages/ui/public/tmp.ts +++ b/packages/ui/public/tmp.ts @@ -79,7 +79,7 @@ export default from('information_schema.table_constraints') .include({ keys, }) - .groupingId( + .groupBy( 'constraint_schema', 'constraint_name', 'table_schema', From 39b407f7666859ec0879d42380dfb68328dd89df Mon Sep 17 00:00:00 2001 From: Fernando Hurtado Date: Sun, 28 Jul 2024 15:44:24 +0200 Subject: [PATCH 2/6] feat: add support for .count() --- packages/queries/src/expression/Exp.ts | 38 ++++ packages/queries/src/expression/Expression.ts | 173 ------------------ packages/queries/src/expression/dsl.ts | 121 ------------ packages/queries/src/expression/fns.ts | 9 + packages/queries/src/expression/ops.ts | 34 ++++ 5 files changed, 81 insertions(+), 294 deletions(-) create mode 100644 packages/queries/src/expression/Exp.ts delete mode 100644 packages/queries/src/expression/Expression.ts delete mode 100644 packages/queries/src/expression/dsl.ts create mode 100644 packages/queries/src/expression/fns.ts create mode 100644 packages/queries/src/expression/ops.ts diff --git a/packages/queries/src/expression/Exp.ts b/packages/queries/src/expression/Exp.ts new file mode 100644 index 00000000..56b9b627 --- /dev/null +++ b/packages/queries/src/expression/Exp.ts @@ -0,0 +1,38 @@ +import { BinaryOperator } from '../types/BinaryOp'; +import { FilterByValue } from '../types/FilterByValue'; + +export type BooleanExpressions = + | { type: 'const'; value: boolean; $returnType?: boolean } + | { + type: 'op'; + op: BinaryOperator; + args: Exp[]; + $returnType?: boolean; + } + | { type: 'op'; op: 'not'; arg: Exp; $returnType?: boolean } + | FilterByValue; + +export type NumberExpressions = + | number + | { type: 'const'; value: number; $returnType?: number } + | { type: 'op'; op: '+'; args: Exp[]; $returnType?: number } + | { type: 'fn'; fn: 'count'; args: Exp[]; $returnType?: number } + | FilterByValue; + +export type StringExpressions = + | FilterByValue + | { type: 'const'; value: string; $returnType?: string }; + +export type Exp = + // Case: boolean + ReturnType extends boolean + ? BooleanExpressions + : // Case: number + ReturnType extends number + ? NumberExpressions + : ReturnType extends string + ? StringExpressions + : // Case: no match + | BooleanExpressions + | NumberExpressions + | StringExpressions; diff --git a/packages/queries/src/expression/Expression.ts b/packages/queries/src/expression/Expression.ts deleted file mode 100644 index b998740a..00000000 --- a/packages/queries/src/expression/Expression.ts +++ /dev/null @@ -1,173 +0,0 @@ -export type Primitive = string | number | boolean | null | Date; - -export const unaryOperators = ['not', '-', 'exists', 'not exists'] as const; - -export type UnaryOperator = (typeof unaryOperators)[number]; - -export const binaryOperators = [ - '=', - '==', - '!=', - '<>', - '>', - '>=', - '<', - '<=', - 'in', - 'not in', - 'is', - 'is not', - 'like', - 'not like', - 'match', - 'ilike', - 'not ilike', - '@>', - '<@', - '&&', - '?', - '?&', - '!<', - '!>', - '<=>', - '!~', - '~', - '~*', - '!~*', - '@@', - '@@@', - '!!', - '<->', - 'regexp', - '+', - '-', - '*', - '/', - '%', - '^', - '&', - '|', - '#', - '<<', - '>>', - '&&', - '||', -] as const; - -export type BinaryOperator = (typeof binaryOperators)[number]; - -export const nAryOperators = [ - 'and', - 'or', - '=', - '==', - '!=', - '<>', - '>', - '>=', - '<', - '<=', - 'in', - 'not in', - 'is', - 'is not', - 'like', - '#>', - '#>>', - 'not like', - 'match', - 'ilike', - 'not ilike', - '@>', - '<@', - '&&', - '?', - '?&', - '!<', - '!>', - '<=>', - '!~', - '~', - '~*', - '!~*', - '@@', - '@@@', - '!!', - '<->', - 'regexp', - '+', - '-', - '*', - '/', - '%', - '^', - '&', - '|', - '#', - '<<', - '>>', - '&&', - '||', - '->', - '->>', - 'not', - '-', - 'exists', - 'not exists', - 'between', - 'between symmetric', -] as const; - -export type NAryOperator = (typeof nAryOperators)[number]; - -/** - * An expression that invokes a function - */ -export type ExpFunctionInvocation = [ - '>invoke', - functionName: string, - ...Exp[], -]; - -/** - * An expression that references a column in a table - */ -export type ExpColumnReference = Context; - -/** - * An expression that casts an expression to a different type - */ -export type ExpCast = ['>::', exp: Exp, type: string]; - -/** - * An expression that conditionally evaluates to one of two expressions - */ -export type ExpWhen = [ - '>when', - condition: Exp, - whenTrue: Exp, - whenFalse: Exp, -]; - -/** - * A literal is serialized as a value in the query - * e.g. `SELECT * FROM table WHERE column = 1` - */ -export type ExpLiteral = ['>literal', Primitive] | Exclude; - -/** - * A parameter is serialized as a placeholder in the query - * e.g. `SELECT * FROM table WHERE column = $1` - */ -export type ExpParam = ['>param', Primitive]; - -export type Exp = - | ExpLiteral - | ExpParam - | ExpFunctionInvocation - | ExpColumnReference - | ExpCast - | ExpWhen - | [UnaryOperator, Exp] - | [BinaryOperator, Exp, Exp] - | [NAryOperator, Exp, ...Exp[]]; diff --git a/packages/queries/src/expression/dsl.ts b/packages/queries/src/expression/dsl.ts deleted file mode 100644 index ea01d0fa..00000000 --- a/packages/queries/src/expression/dsl.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Exp, Primitive } from './Expression'; - -export function equals( - exp1: Exp, - exp2: Exp, -): Exp { - return ['=', exp1, exp2]; -} - -export function isPositive(exp: Exp): Exp { - return ['>', exp, 0]; -} - -export function isNegative(exp: Exp): Exp { - return ['<', exp, 0]; -} - -export function isZero(exp: Exp): Exp { - return ['=', exp, 0]; -} - -export function coalesce( - exp: Exp, - def: Exp, -): Exp { - return ['>invoke', 'coalesce', exp, def]; -} - -export function isNotNull(exp: Exp): Exp { - return ['is not', exp, null]; -} - -export function isNull(exp: Exp): Exp { - return ['is', exp, null]; -} - -export function sum(exp: Exp): Exp { - return ['>invoke', 'sum', exp]; -} - -export function param( - param: TParam, -): Exp { - return ['>param', param]; -} - -export function literal( - param: TParam, -): Exp { - return ['>literal', param]; -} - -export function when( - condition: Exp, - whenTrue: Exp, - whenFalse: Exp, -): Exp { - return ['>when', condition, whenTrue, whenFalse]; -} - -export const cast = { - asText: (exp: Exp): Exp => { - return ['>::', exp, 'text']; - }, - asNumeric: (exp: Exp): Exp => { - return ['>::', exp, 'numeric']; - }, - asInteger: (exp: Exp): Exp => { - return ['>::', exp, 'integer']; - }, - asUuid: (exp: Exp): Exp => { - return ['>::', exp, 'uuid']; - }, -}; - -export const json = { - get: (exp: Exp, key: string): Exp => { - return ['->', exp, literal(key)]; - }, - getAsText: (exp: Exp, key: string): Exp => { - return ['->>', exp, literal(key)]; - }, - getAsNumeric: (exp: Exp, key: string): Exp => { - const jsonText = json.getAsText(exp, key); - const withDefault = coalesce(jsonText, literal('0')); - - return cast.asNumeric(withDefault); - }, - agg: (exp: Exp): Exp => { - return ['>invoke', 'json_agg', exp]; - }, - buildObject: (...exp: Exp[]): Exp => { - return ['>invoke', 'json_build_object', ...exp]; - }, -}; - -export function distinct(exp: Exp): Exp { - return ['>invoke', 'distinct', exp]; -} - -export function count(exp: Exp): Exp { - return ['>invoke', 'count', exp]; -} - -export const jsonb = { - buildObject: (...exp: Exp[]): Exp => { - return ['>invoke', 'jsonb_build_object', ...exp]; - }, - agg: (exp: Exp): Exp => { - return ['>invoke', 'jsonb_agg', exp]; - }, -}; - -export function chain( - exp: Exp, - ...fns: Array<(exp: Exp) => Exp> -): Exp { - return fns.reduce((acc, fn): Exp => { - return fn(acc); - }, exp); -} diff --git a/packages/queries/src/expression/fns.ts b/packages/queries/src/expression/fns.ts new file mode 100644 index 00000000..c607b644 --- /dev/null +++ b/packages/queries/src/expression/fns.ts @@ -0,0 +1,9 @@ +import { Exp, NumberExpressions } from './Exp'; + +export function count(a: Exp): NumberExpressions { + return { + type: 'fn', + fn: 'count', + args: [a], + }; +} diff --git a/packages/queries/src/expression/ops.ts b/packages/queries/src/expression/ops.ts new file mode 100644 index 00000000..9be226f0 --- /dev/null +++ b/packages/queries/src/expression/ops.ts @@ -0,0 +1,34 @@ +import { BooleanExpressions, Exp, NumberExpressions } from './Exp'; + +export function add( + a: NumberExpressions, + b: NumberExpressions, +): NumberExpressions { + return { + type: 'op', + op: '+', + args: [a, b], + }; +} + +/** + * @see https://www.postgresql.org/docs/current/functions-comparison.html + */ +function eq(a: Exp, b: Exp): BooleanExpressions { + return { + type: 'op', + op: '=', + args: [a, b], + }; +} + +/** + * @see https://www.postgresql.org/docs/current/functions-logical.html + */ +function not(a: BooleanExpressions): BooleanExpressions { + return { + type: 'op', + op: 'not', + arg: a, + }; +} From 21898f22682b63a09386d54ccc4f83d2d1e54582 Mon Sep 17 00:00:00 2001 From: Fernando Hurtado Date: Sun, 28 Jul 2024 15:47:56 +0200 Subject: [PATCH 3/6] wip --- packages/queries/src/query.ts | 92 +++++++++++++++++---- packages/queries/src/types/FilterByValue.ts | 16 ++++ packages/queries/src/types/QueryResult.ts | 10 +-- packages/queries/src/types/Simplify.ts | 10 +++ 4 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 packages/queries/src/types/FilterByValue.ts create mode 100644 packages/queries/src/types/Simplify.ts diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index 70b7fca8..23402526 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -6,6 +6,7 @@ import { Table } from './types/Table'; import { Schema } from './types/Schema'; import { getTableSelectableColumns } from './schema/getTableSelectableColumns'; import { getTablePrimaryKeyColumns } from './schema/getTablePrimaryKeyColumns'; +import { Exp } from './expression/Exp'; export class QueryBuilder< DB, @@ -18,6 +19,7 @@ export class QueryBuilder< TCardinality extends 'one' | 'maybe' | 'many', TLazy extends true | undefined, TGroupBy extends string[], + TAggregates extends Record | undefined, > { constructor( private _from: TTable, @@ -29,6 +31,7 @@ export class QueryBuilder< private _cardinality: TCardinality, private _lazy: TLazy, private _groupBy: TGroupBy, + private _aggregates: TAggregates, ) {} private build(): { @@ -41,6 +44,7 @@ export class QueryBuilder< cardinality: TCardinality; lazy: TLazy; groupBy: TGroupBy; + aggregates: TAggregates; } { return { from: this._from, @@ -52,9 +56,41 @@ export class QueryBuilder< cardinality: this._cardinality ?? 'many', lazy: this._lazy, groupBy: this._groupBy, + aggregates: this._aggregates, }; } + count() { + return new QueryBuilder< + DB, + TTable, + TWhere, + { count: true }, + TInclude, + TLimit, + TOffset, + 'one', + TLazy, + TGroupBy, + { + count: { type: 'fn'; fn: 'count'; args: [1] }; + } + >( + this._from, + this._where, + { count: true }, + this._include, + this._limit, + this._offset, + 'one', + this._lazy, + this._groupBy, + { + count: { type: 'fn', fn: 'count', args: [1] }, + }, + ).build(); + } + /** * Sets the limit of the query. */ @@ -69,7 +105,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -80,6 +117,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -97,7 +135,8 @@ export class QueryBuilder< TOffset, 'many', TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -108,6 +147,7 @@ export class QueryBuilder< 'many', this._lazy, this._groupBy, + this._aggregates, ).build(); } @@ -125,7 +165,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -136,6 +177,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -155,7 +197,8 @@ export class QueryBuilder< TOffset, 'one', TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -166,6 +209,7 @@ export class QueryBuilder< 'one', this._lazy, this._groupBy, + this._aggregates, ).build(); } @@ -183,7 +227,8 @@ export class QueryBuilder< TOffset, 'many', TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -194,6 +239,7 @@ export class QueryBuilder< 'many', this._lazy, this._groupBy, + this._aggregates, ).build(); } @@ -211,7 +257,8 @@ export class QueryBuilder< TOffset, 'maybe', TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -222,6 +269,7 @@ export class QueryBuilder< 'maybe', this._lazy, this._groupBy, + this._aggregates, ).build(); } @@ -236,7 +284,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -247,6 +296,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -283,7 +333,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -294,6 +345,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -308,7 +360,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -319,6 +372,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -333,7 +387,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -344,6 +399,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -362,7 +418,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, where, @@ -373,6 +430,7 @@ export class QueryBuilder< this._cardinality, this._lazy, this._groupBy, + this._aggregates, ); } @@ -387,7 +445,8 @@ export class QueryBuilder< TOffset, TCardinality, true, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -398,6 +457,7 @@ export class QueryBuilder< this._cardinality, true, this._groupBy, + this._aggregates, ); } @@ -412,7 +472,8 @@ export class QueryBuilder< TOffset, TCardinality, TLazy, - TGroupBy + TGroupBy, + TAggregates >( this._from, this._where, @@ -423,6 +484,7 @@ export class QueryBuilder< this._cardinality, this._lazy, id, + this._aggregates, ); } } @@ -446,7 +508,8 @@ export function query(schema: Schema) { number | undefined, 'many', undefined, - typeof primaryKeys + typeof primaryKeys, + undefined >( table, {}, @@ -457,6 +520,7 @@ export function query(schema: Schema) { 'many', undefined, primaryKeys, + undefined, ); }, }; diff --git a/packages/queries/src/types/FilterByValue.ts b/packages/queries/src/types/FilterByValue.ts new file mode 100644 index 00000000..85524d99 --- /dev/null +++ b/packages/queries/src/types/FilterByValue.ts @@ -0,0 +1,16 @@ +/** + * Returns the keys of TRecord that extend TValue + * + * Example: + * ```ts + * type Record = { + * name: string; + * age: number; + * } + * + * FilterByValue // 'name' + * FilterByValue // 'age' + */ +export type FilterByValue = { + [Key in keyof TRecord]: TRecord[Key] extends TValue ? Key : never; +}[keyof TRecord]; diff --git a/packages/queries/src/types/QueryResult.ts b/packages/queries/src/types/QueryResult.ts index 984a02d6..d88c052c 100644 --- a/packages/queries/src/types/QueryResult.ts +++ b/packages/queries/src/types/QueryResult.ts @@ -2,6 +2,7 @@ import { Query } from './types'; import { ColumnValue } from './ColumnValue'; import { Column } from './Column'; import { Table } from './Table'; +import { Simplify } from './Simplify'; export type QueryResult = Simplify< TQuery extends Query @@ -9,15 +10,6 @@ export type QueryResult = Simplify< : never >; -type Simplify = - T extends Array - ? Simplify[] - : T extends Date - ? T - : T extends object - ? { [K in keyof T]: Simplify } - : T; - type QueryResultInner< DB, TTable extends Table, diff --git a/packages/queries/src/types/Simplify.ts b/packages/queries/src/types/Simplify.ts new file mode 100644 index 00000000..b08810ac --- /dev/null +++ b/packages/queries/src/types/Simplify.ts @@ -0,0 +1,10 @@ +export type Simplify = + T extends Array + ? Simplify[] + : T extends Date + ? T + : T extends object + ? { + [K in keyof T]: Simplify; + } + : T; From 8ab418d0646a8026dfa416da74a0cdc2e0f153a9 Mon Sep 17 00:00:00 2001 From: Fernando Hurtado Date: Sun, 28 Jul 2024 17:59:01 +0200 Subject: [PATCH 4/6] wip --- .../executors/PgExecutor/PgExecutor.test.ts | 23 +++ .../executors/PgExecutor/PgExecutor.ts | 2 - .../queryBuilder/AggregationsSelection.ts | 40 +++++ .../queryBuilder/createAugmentedQuery.ts | 4 +- .../executors/PgExecutor/queryBuilder/exp.ts | 1 - packages/backend/src/tests/e2e/nx1.test.ts | 8 +- .../arbitraries/ArbitraryQueryBuilder.ts | 18 ++- .../propertyBased/properties/count.test.ts | 54 +++++++ .../docs/static/reference/assets/search.js | 2 +- .../queries/src/ExperimentalQueryBuilder.ts | 147 ++++++++++++++++++ packages/queries/src/index.ts | 2 +- packages/queries/src/query.ts | 31 +++- packages/queries/src/types/Include.ts | 2 +- .../queries/src/types/{types.ts => Query.ts} | 4 + packages/queries/src/types/QueryResult.ts | 2 +- 15 files changed, 322 insertions(+), 18 deletions(-) create mode 100644 packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts create mode 100644 packages/backend/src/tests/propertyBased/properties/count.test.ts create mode 100644 packages/queries/src/ExperimentalQueryBuilder.ts rename packages/queries/src/types/{types.ts => Query.ts} (82%) diff --git a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts index f9609860..ba6fc35f 100644 --- a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts +++ b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts @@ -48,6 +48,29 @@ describe('PgExecutor', () => { `); }); + it('.count() ', async () => { + const q = from('film') + .where({ + film_id: { in: [1, 2, 3] }, + }) + .groupBy() + .count(); + const { sql } = executor.compile(q); + + expect(sql).toMatchInlineSnapshot(` + "select + count(1) as "count" + from + "public"."film" "public::film" + where + ("public::film".film_id = any ($1))" + `); + + const result = await executor.execute(q, executeProps); + + expect(result).toEqual([{ count: 3 }]); + }); + it('Film table SynthQL query executes to expected result', async () => { const result = await executor.execute(q1, executeProps); diff --git a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts index c25fb6c7..93a391a2 100644 --- a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts +++ b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts @@ -1,8 +1,6 @@ import { Pool, PoolClient } from 'pg'; import { format } from 'sql-formatter'; import { splitQueryAtBoundary } from '../../../query/splitQueryAtBoundary'; -import { ColumnRef } from '../../../refs/ColumnRef'; -import { RefContext, createRefContext } from '../../../refs/RefContext'; import { AnyQuery } from '../../../types'; import { QueryExecutor } from '../../types'; import { QueryProviderExecutor } from '../QueryProviderExecutor'; diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts new file mode 100644 index 00000000..3841432d --- /dev/null +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts @@ -0,0 +1,40 @@ +import { AnyQuery } from '../../../../types'; +import { Selection } from './types'; +import { SqlBuilder } from './exp'; + +// TODO(fhur): this is actually only supporting count(*) for now. +export class AggregationsSelection implements Selection { + public constructor() { } + + static fromQuery( + rootQuery: AnyQuery, + defaultSchema: string, + ): AggregationsSelection[] { + if (rootQuery.aggregates === undefined) { + return []; + } + if (Object.keys(rootQuery.aggregates).length === 0) { + return []; + } + return [new AggregationsSelection()] + } + + toSql() { + return new SqlBuilder().addAs(['as', ['fn', 'count', '1'], 'count']); + } + + extractFromRow(row: any, target: any): void { + // TODO assert prescence + const tmp = row['count']; + if (typeof tmp !== 'string') { + throw new Error( + `Expected count to be a string, got: ${JSON.stringify(tmp)}`, + ); + } + + // Note technically correct, but we're not going to support counting bigints + // this is only an issue if you return more than Integer.MAX_VALUE rows, + // which is unlikely. + target['count'] = parseInt(tmp); + } +} diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts index 60ec80f6..c828d671 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts @@ -6,6 +6,7 @@ import { SelectionJsonbAgg } from './SelectionJsonbAgg'; import { TableRef } from '../../../../refs/TableRef'; import { ColumnRef } from '../../../../refs/ColumnRef'; import { Join, Selection } from './types'; +import { AggregationsSelection } from './AggregationsSelection'; export interface AugmentedQuery { selection: Selection[]; @@ -29,13 +30,14 @@ export function createAugmentedQuery( rootQuery, defaultSchema, ); + const aggregates = AggregationsSelection.fromQuery(rootQuery, defaultSchema); const wheres = collectWhere(rootQuery, defaultSchema); const joins = collectJoins(rootQuery, defaultSchema); const groupingColumns = rootQuery.groupBy?.map((col) => rootTable.column(col)) ?? []; return { - selection: selectionColumn.concat(jsonbAggColumn), + selection: selectionColumn.concat(jsonbAggColumn).concat(aggregates), rootQuery, rootTable, joins, diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts index c84d29a9..844a561a 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts @@ -229,7 +229,6 @@ export class SqlBuilder { } addOperator(op: BinaryOperator) { - const unknownOp = op as unknown; if (!OPERATORS.includes(op as any)) { throw new Error(`Invalid operator: ${op}`); } diff --git a/packages/backend/src/tests/e2e/nx1.test.ts b/packages/backend/src/tests/e2e/nx1.test.ts index c4fe355b..c3cb8bc8 100644 --- a/packages/backend/src/tests/e2e/nx1.test.ts +++ b/packages/backend/src/tests/e2e/nx1.test.ts @@ -1,4 +1,4 @@ -import { Where, col } from '@synthql/queries'; +import { QueryResult, Where, col } from '@synthql/queries'; import { describe, expect, test } from 'vitest'; import { collectLast } from '../..'; import { execute } from '../../execution/execute'; @@ -166,4 +166,10 @@ describe('n x 1', () => { expect(c.expected).toEqual(c.expected); }); }); + + test('x', async () => { + const q = from('film').count(); + + type x = QueryResult; + }); }); diff --git a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts index 98c0d189..ebaa2119 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts @@ -19,7 +19,8 @@ export class ArbitraryQueryBuilder { private cardinalities: Cardinality[] = ['many', 'maybe', 'one'], private tables: Table[] = getTableNames(schema) as Table[], private hasResults: boolean = true, - ) {} + private groupBy?: string[], + ) { } /** * Creates a new ArbitraryQueryBuilder from the Pagila DB. @@ -37,6 +38,16 @@ export class ArbitraryQueryBuilder { ); } + withGroupBy(...groupBy: string[]) { + return new ArbitraryQueryBuilder( + this.schema, + this.cardinalities, + this.tables, + this.hasResults, + groupBy, + ); + } + withTable(...tables: Table[]) { return new ArbitraryQueryBuilder( this.schema, @@ -115,7 +126,10 @@ export class ArbitraryQueryBuilder { } private arbGroupBy(tableName: Table): fc.Arbitrary { - return fc.constant(getPrimaryKeyColumns(this.schema, tableName)); + if (this.groupBy === undefined) { + return fc.constant(getPrimaryKeyColumns(this.schema, tableName)); + } + return fc.constant(this.groupBy); } private arbCardinality(): fc.Arbitrary { diff --git a/packages/backend/src/tests/propertyBased/properties/count.test.ts b/packages/backend/src/tests/propertyBased/properties/count.test.ts new file mode 100644 index 00000000..9b7469ed --- /dev/null +++ b/packages/backend/src/tests/propertyBased/properties/count.test.ts @@ -0,0 +1,54 @@ +import { test } from '@fast-check/vitest'; +import { ArbitraryQueryBuilder } from '../arbitraries/ArbitraryQueryBuilder'; +import { queryEngine } from '../../queryEngine'; +import { describe, expect } from 'vitest'; +import { Query } from '@synthql/queries'; +import { DB } from '../../generated'; + +const queryBuilder = ArbitraryQueryBuilder.fromPagila(); + +const numRuns = 100; +const timeout = numRuns * 1000; +const endOnFailure = true; + +test.prop([queryBuilder.withCardinality('many').withGroupBy(...[]).withSomeResults().build()], { + verbose: true, + numRuns, + timeout, + endOnFailure, + seed: 1308343585, path: "2" +})( + 'execute(query).length should equal execute(query.count()).count', + async (query) => { + const queryResult = (await queryEngine.executeAndWait( + query, + )) as Array; + + const countQuery: Query = { + ...query, + select: {}, + aggregates: { + count: { + type: 'fn', + fn: 'count', + args: [1], + }, + }, + groupBy: [], + cardinality: 'one', + }; + + + const countResult = (await queryEngine.executeAndWait( + countQuery, + )) as any; + + console.log(queryEngine.compile(countQuery).sql) + + expect(typeof countResult.count).toEqual('number'); + console.log(query, countQuery); + + expect(queryResult.length).toEqual(countResult.count); + }, + timeout, +); diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 24c8bab8..04323c09 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/ExperimentalQueryBuilder.ts b/packages/queries/src/ExperimentalQueryBuilder.ts new file mode 100644 index 00000000..2b1534a0 --- /dev/null +++ b/packages/queries/src/ExperimentalQueryBuilder.ts @@ -0,0 +1,147 @@ +// ignore type errors in this file +// @ts-nocheck +import { Exp } from './expression/Exp'; +import { DB } from './generated'; +import { BinaryOperator } from './types/BinaryOp'; +import { Table } from './types/Table'; + +type Primitive = string | number | boolean | null | undefined | Date; + +type GetFromScope = Key extends keyof Scope ? Scope[Key] : never; + +type ExpressionType = E extends string + ? GetFromScope + : E extends { $returnType?: infer T } + ? T + : E extends Primitive + ? E + : never; + +type RowType = { + [col in keyof DB[T]['columns']]: DB[T]['columns'][col] extends { + type: infer TType; + } + ? TType + : never; +}; + +type Query = { + from: string; + columns: string[]; + where: BooleanExpressions; + groupBy: string[]; + aggregates: Record>; + having: BooleanExpressions; + orderBy: string[]; + limit: number; + offset: number; +}; + +/** + * @see https://www.postgresql.org/docs/current/functions-comparison.html + */ +function eq(a: Exp, b: Exp): BooleanExpressions { + return { + type: 'op', + op: '=', + args: [a, b], + }; +} + +/** + * @see https://www.postgresql.org/docs/current/functions-logical.html + */ +function not(a: BooleanExpressions): BooleanExpressions { + return { + type: 'op', + op: 'not', + arg: a, + }; +} + +function add( + a: NumberExpressions, + b: NumberExpressions, +): NumberExpressions { + return { + type: 'op', + op: '+', + args: [a, b], + }; +} + +function count(a: Exp): NumberExpressions { + return { + type: 'fn', + fn: 'count', + args: [a], + }; +} + +function str(value: string): StringExpressions { + return { + type: 'const', + value, + }; +} + +function or( + ...args: BooleanExpressions[] +): BooleanExpressions { + return { + type: 'op', + op: '||', + args, + }; +} + +export class ExperimentalQueryBuilder { + private _filter: Exp | undefined; + + constructor(private table: string) {} + + filter(exp: Exp): ExperimentalQueryBuilder { + this._filter = exp; + return this; + } + + count() { + return this.aggregate({ count: count(1) }) + .select('count') + .take(1); + } + + select( + ...columns: TSelection[] + ): ExperimentalQueryBuilder> { + return this; + } + + groupBy( + ...columns: TSelection[] + ): ExperimentalQueryBuilder { + return this; + } + + aggregate>>( + aggregates: TAggregates, + ): ExperimentalQueryBuilder { + return this; + } + + take(count: number): Scope { + return this; + } +} + +function from_experimental>(table: TTable) { + return new ExperimentalQueryBuilder>(table); +} + +from_experimental('actor').count(); + +from_experimental('actor') + .filter(or(eq(str('asdf'), 'first_name'), eq(str('asdf'), 'last_name'))) + .groupBy('last_name') + .aggregate({ count: count(1) }) + .select('count', 'last_name'); diff --git a/packages/queries/src/index.ts b/packages/queries/src/index.ts index 13c60ff0..f04b07b3 100644 --- a/packages/queries/src/index.ts +++ b/packages/queries/src/index.ts @@ -1,5 +1,5 @@ export { col } from './col'; -export * from './types/types'; +export * from './types/Query'; export * from './types/BinaryOp'; export * from './types/Cardinality'; export * from './types/Column'; diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index 4a5953a2..567d025b 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -32,7 +32,7 @@ export class QueryBuilder< private _lazy: TLazy, private _groupBy: TGroupBy, private _aggregates: TAggregates, - ) { } + ) {} private build(): { from: TTable; @@ -60,15 +60,32 @@ export class QueryBuilder< }; } + /** + * Returns a query that counts the number of rows that match the query. + * + * Example: + * + * ```ts + * const query = from('actor') + * .where({ actor_id: {in: [1,2,3]} }) + * .count() + * + * const { data } = useSynthql({query}) + * + * console.log(data.count) // 3 + * ``` + * + * This will return a query that counts the number of rows in the `actor` table where `actor_id = 1`. + */ count() { return new QueryBuilder< DB, TTable, TWhere, - { count: true }, + {}, TInclude, - TLimit, - TOffset, + undefined, + undefined, 'one', TLazy, TGroupBy, @@ -78,10 +95,10 @@ export class QueryBuilder< >( this._from, this._where, - { count: true }, + {}, this._include, - this._limit, - this._offset, + undefined, + undefined, 'one', this._lazy, this._groupBy, diff --git a/packages/queries/src/types/Include.ts b/packages/queries/src/types/Include.ts index 06d243f5..a459abe6 100644 --- a/packages/queries/src/types/Include.ts +++ b/packages/queries/src/types/Include.ts @@ -1,5 +1,5 @@ import { Table } from './Table'; -import { Query } from './types'; +import { Query } from './Query'; export type Include = { [k in string]: Query> extends Query diff --git a/packages/queries/src/types/types.ts b/packages/queries/src/types/Query.ts similarity index 82% rename from packages/queries/src/types/types.ts rename to packages/queries/src/types/Query.ts index 725d2adc..ccd4d7e4 100644 --- a/packages/queries/src/types/types.ts +++ b/packages/queries/src/types/Query.ts @@ -2,6 +2,7 @@ import { Table } from './Table'; import { Select } from './Select'; import { Where } from './Where'; import { Include } from './Include'; +import { Exp } from '../expression/Exp'; export type Query = Table> = { from: TTable; @@ -13,4 +14,7 @@ export type Query = Table> = { cardinality?: 'one' | 'maybe' | 'many'; lazy?: true; groupBy?: string[]; + aggregates?: { + [key: string]: Exp; + }; }; diff --git a/packages/queries/src/types/QueryResult.ts b/packages/queries/src/types/QueryResult.ts index d88c052c..573c6a34 100644 --- a/packages/queries/src/types/QueryResult.ts +++ b/packages/queries/src/types/QueryResult.ts @@ -1,4 +1,4 @@ -import { Query } from './types'; +import { Query } from './Query'; import { ColumnValue } from './ColumnValue'; import { Column } from './Column'; import { Table } from './Table'; From a6ba4c3b002f27323c6e5995dca114acf3ed18f2 Mon Sep 17 00:00:00 2001 From: Fernando Hurtado Date: Sun, 28 Jul 2024 20:43:38 +0200 Subject: [PATCH 5/6] fix --- .../arbitraries/ArbitraryQueryBuilder.ts | 26 ++++++++++++++++++- .../propertyBased/properties/count.test.ts | 6 +---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts index ebaa2119..2f8e1bc6 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts @@ -20,6 +20,7 @@ export class ArbitraryQueryBuilder { private tables: Table[] = getTableNames(schema) as Table[], private hasResults: boolean = true, private groupBy?: string[], + private skipLimit?: boolean, ) { } /** @@ -35,6 +36,8 @@ export class ArbitraryQueryBuilder { cardinality, this.tables, this.hasResults, + this.groupBy, + this.skipLimit ); } @@ -45,6 +48,7 @@ export class ArbitraryQueryBuilder { this.tables, this.hasResults, groupBy, + this.skipLimit ); } @@ -54,6 +58,8 @@ export class ArbitraryQueryBuilder { this.cardinalities, tables, this.hasResults, + this.groupBy, + this.skipLimit ); } @@ -68,6 +74,8 @@ export class ArbitraryQueryBuilder { this.cardinalities, this.tables, false, + this.groupBy, + this.skipLimit ); } @@ -82,6 +90,19 @@ export class ArbitraryQueryBuilder { this.cardinalities, this.tables, true, + this.groupBy, + this.skipLimit + ); + } + + withNoLimit() { + return new ArbitraryQueryBuilder( + this.schema, + this.cardinalities, + this.tables, + this.hasResults, + this.groupBy, + true ); } @@ -136,7 +157,10 @@ export class ArbitraryQueryBuilder { return fc.constantFrom(...this.cardinalities); } - private arbLimit(): fc.Arbitrary { + private arbLimit(): fc.Arbitrary { + if (this.skipLimit) { + return fc.constant(undefined); + } if (!this.hasResults) { return fc.constant(0); } diff --git a/packages/backend/src/tests/propertyBased/properties/count.test.ts b/packages/backend/src/tests/propertyBased/properties/count.test.ts index 9b7469ed..cd3887e7 100644 --- a/packages/backend/src/tests/propertyBased/properties/count.test.ts +++ b/packages/backend/src/tests/propertyBased/properties/count.test.ts @@ -11,7 +11,7 @@ const numRuns = 100; const timeout = numRuns * 1000; const endOnFailure = true; -test.prop([queryBuilder.withCardinality('many').withGroupBy(...[]).withSomeResults().build()], { +test.prop([queryBuilder.withCardinality('many').withNoLimit().withSomeResults().build()], { verbose: true, numRuns, timeout, @@ -38,15 +38,11 @@ test.prop([queryBuilder.withCardinality('many').withGroupBy(...[]).withSomeResul cardinality: 'one', }; - const countResult = (await queryEngine.executeAndWait( countQuery, )) as any; - console.log(queryEngine.compile(countQuery).sql) - expect(typeof countResult.count).toEqual('number'); - console.log(query, countQuery); expect(queryResult.length).toEqual(countResult.count); }, From ed67ca43f792a1e1e78311389f4a4b7dd5db07cc Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 29 Jul 2024 07:55:03 +0000 Subject: [PATCH 6/6] format Signed-off-by: Jim Ezesinachi --- .../queryBuilder/AggregationsSelection.ts | 4 +-- .../queryBuilder/createAugmentedQuery.ts | 5 +++- .../arbitraries/ArbitraryQueryBuilder.ts | 14 +++++----- .../propertyBased/properties/count.test.ts | 26 +++++++++++++------ 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts index 3841432d..5fc76d2c 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/AggregationsSelection.ts @@ -4,7 +4,7 @@ import { SqlBuilder } from './exp'; // TODO(fhur): this is actually only supporting count(*) for now. export class AggregationsSelection implements Selection { - public constructor() { } + public constructor() {} static fromQuery( rootQuery: AnyQuery, @@ -16,7 +16,7 @@ export class AggregationsSelection implements Selection { if (Object.keys(rootQuery.aggregates).length === 0) { return []; } - return [new AggregationsSelection()] + return [new AggregationsSelection()]; } toSql() { diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts index c828d671..8f8dc71f 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/createAugmentedQuery.ts @@ -30,7 +30,10 @@ export function createAugmentedQuery( rootQuery, defaultSchema, ); - const aggregates = AggregationsSelection.fromQuery(rootQuery, defaultSchema); + const aggregates = AggregationsSelection.fromQuery( + rootQuery, + defaultSchema, + ); const wheres = collectWhere(rootQuery, defaultSchema); const joins = collectJoins(rootQuery, defaultSchema); const groupingColumns = diff --git a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts index 2f8e1bc6..eeb0bba9 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/ArbitraryQueryBuilder.ts @@ -21,7 +21,7 @@ export class ArbitraryQueryBuilder { private hasResults: boolean = true, private groupBy?: string[], private skipLimit?: boolean, - ) { } + ) {} /** * Creates a new ArbitraryQueryBuilder from the Pagila DB. @@ -37,7 +37,7 @@ export class ArbitraryQueryBuilder { this.tables, this.hasResults, this.groupBy, - this.skipLimit + this.skipLimit, ); } @@ -48,7 +48,7 @@ export class ArbitraryQueryBuilder { this.tables, this.hasResults, groupBy, - this.skipLimit + this.skipLimit, ); } @@ -59,7 +59,7 @@ export class ArbitraryQueryBuilder { tables, this.hasResults, this.groupBy, - this.skipLimit + this.skipLimit, ); } @@ -75,7 +75,7 @@ export class ArbitraryQueryBuilder { this.tables, false, this.groupBy, - this.skipLimit + this.skipLimit, ); } @@ -91,7 +91,7 @@ export class ArbitraryQueryBuilder { this.tables, true, this.groupBy, - this.skipLimit + this.skipLimit, ); } @@ -102,7 +102,7 @@ export class ArbitraryQueryBuilder { this.tables, this.hasResults, this.groupBy, - true + true, ); } diff --git a/packages/backend/src/tests/propertyBased/properties/count.test.ts b/packages/backend/src/tests/propertyBased/properties/count.test.ts index cd3887e7..61dcfa37 100644 --- a/packages/backend/src/tests/propertyBased/properties/count.test.ts +++ b/packages/backend/src/tests/propertyBased/properties/count.test.ts @@ -1,7 +1,7 @@ import { test } from '@fast-check/vitest'; import { ArbitraryQueryBuilder } from '../arbitraries/ArbitraryQueryBuilder'; import { queryEngine } from '../../queryEngine'; -import { describe, expect } from 'vitest'; +import { expect } from 'vitest'; import { Query } from '@synthql/queries'; import { DB } from '../../generated'; @@ -11,13 +11,23 @@ const numRuns = 100; const timeout = numRuns * 1000; const endOnFailure = true; -test.prop([queryBuilder.withCardinality('many').withNoLimit().withSomeResults().build()], { - verbose: true, - numRuns, - timeout, - endOnFailure, - seed: 1308343585, path: "2" -})( +test.prop( + [ + queryBuilder + .withCardinality('many') + .withNoLimit() + .withSomeResults() + .build(), + ], + { + verbose: true, + numRuns, + timeout, + endOnFailure, + seed: 1308343585, + path: '2', + }, +)( 'execute(query).length should equal execute(query.count()).count', async (query) => { const queryResult = (await queryEngine.executeAndWait(