diff --git a/docs/changelog.md b/docs/changelog.md index f55f68c7..4edee810 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -96,3 +96,4 @@ ## master - feature: Add support for aggregate functions (count, sum, avg, min, max) on collection types +- feature: Add support for per table and view `max_row` directives diff --git a/docs/configuration.md b/docs/configuration.md index 395df1f0..7177a394 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -60,13 +60,21 @@ For more fine grained adjustments to reflected names, see [renaming](#renaming). ### Max Rows -The default page size for collections is 30 entries. To adjust the number of entries on each page, set a `max_rows` directive on the relevant schema entity. +The default page size for collections is 30 entries. To adjust the number of entries on each page, set a `max_rows` directive on the relevant schema entity, table or view. For example, to increase the max rows per page for each table in the `public` schema: ```sql comment on schema public is e'@graphql({"max_rows": 100})'; ``` +To limit the max rows per page for the `blog_post` table and `Person` view: +```sql +comment on table blog_post is e'@graphql({"max_rows": 20})'; +comment on view "Person" is e'@graphql({"primary_key_columns": ["id"], "max_rows": 10})'; +``` + +The `max_rows` value falls back to the parent object if it is missing on the current object. For example, if a table doesn't have `max_rows` set, the value set on the table's schema will be used. If the schema also doesn't have `max_rows` set, then it falls back to default value 30. The parent object of a view is the schema, not the table on which the view is created. + ### totalCount `totalCount` is an opt-in field that extends a table's Connection type. It provides a count of the rows that match the query's filters, and ignores pagination arguments. diff --git a/test/expected/max_rows_directive.out b/test/expected/max_rows_directive.out index 6627eb9c..7205d93b 100644 --- a/test/expected/max_rows_directive.out +++ b/test/expected/max_rows_directive.out @@ -2,9 +2,15 @@ begin; create table account( id int primary key ); + create view "accountView" as + select * from account; + comment on view "accountView" is e'@graphql({"primary_key_columns": ["id"]})'; + create view "accountViewWrapper" as + select * from "accountView"; + comment on view "accountViewWrapper" is e'@graphql({"primary_key_columns": ["id"]})'; insert into public.account(id) select * from generate_series(1, 100); - -- expect default 30 rows on first page + -- expect 30 rows on first page because of fallback to default select graphql.resolve($$ { accountCollection @@ -22,8 +28,42 @@ begin; {"data": {"accountCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}]}}} (1 row) + -- expect 30 rows on first page because of fallback to default + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {"data": {"accountViewCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}]}}} +(1 row) + + -- expect 30 rows on first page because of fallback to default + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + {"data": {"accountViewWrapperCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}]}}} +(1 row) + comment on schema public is e'@graphql({"max_rows": 5})'; - -- expect 5 rows on first page + -- expect 5 rows on first page because of fallback to schema max_rows select graphql.resolve($$ { accountCollection @@ -41,8 +81,42 @@ begin; {"data": {"accountCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}]}}} (1 row) + -- expect 5 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +----------------------------------------------------------------------------------------------------------------------------------------------------------- + {"data": {"accountViewCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}]}}} +(1 row) + + -- expect 5 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + {"data": {"accountViewWrapperCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}]}}} +(1 row) + comment on schema public is e'@graphql({"max_rows": 40})'; - -- expect 40 rows on first page + -- expect 40 rows on first page because of fallback to schema max_rows select graphql.resolve($$ { accountCollection @@ -60,9 +134,43 @@ begin; {"data": {"accountCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}, {"node": {"id": 31}}, {"node": {"id": 32}}, {"node": {"id": 33}}, {"node": {"id": 34}}, {"node": {"id": 35}}, {"node": {"id": 36}}, {"node": {"id": 37}}, {"node": {"id": 38}}, {"node": {"id": 39}}, {"node": {"id": 40}}]}}} (1 row) + -- expect 40 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {"data": {"accountViewCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}, {"node": {"id": 31}}, {"node": {"id": 32}}, {"node": {"id": 33}}, {"node": {"id": 34}}, {"node": {"id": 35}}, {"node": {"id": 36}}, {"node": {"id": 37}}, {"node": {"id": 38}}, {"node": {"id": 39}}, {"node": {"id": 40}}]}}} +(1 row) + + -- expect 40 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + resolve +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {"data": {"accountViewWrapperCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}, {"node": {"id": 4}}, {"node": {"id": 5}}, {"node": {"id": 6}}, {"node": {"id": 7}}, {"node": {"id": 8}}, {"node": {"id": 9}}, {"node": {"id": 10}}, {"node": {"id": 11}}, {"node": {"id": 12}}, {"node": {"id": 13}}, {"node": {"id": 14}}, {"node": {"id": 15}}, {"node": {"id": 16}}, {"node": {"id": 17}}, {"node": {"id": 18}}, {"node": {"id": 19}}, {"node": {"id": 20}}, {"node": {"id": 21}}, {"node": {"id": 22}}, {"node": {"id": 23}}, {"node": {"id": 24}}, {"node": {"id": 25}}, {"node": {"id": 26}}, {"node": {"id": 27}}, {"node": {"id": 28}}, {"node": {"id": 29}}, {"node": {"id": 30}}, {"node": {"id": 31}}, {"node": {"id": 32}}, {"node": {"id": 33}}, {"node": {"id": 34}}, {"node": {"id": 35}}, {"node": {"id": 36}}, {"node": {"id": 37}}, {"node": {"id": 38}}, {"node": {"id": 39}}, {"node": {"id": 40}}]}}} +(1 row) + -- table-specific max_rows comment on table account is e'@graphql({"max_rows": 5})'; - -- expect 5 rows on first page + -- expect 5 rows on first page because of table max_rows select graphql.resolve($$ { accountCollection { @@ -80,13 +188,11 @@ begin; (1 row) -- view-specific max_rows - create view person as - select * from account; - comment on view person is e'@graphql({"primary_key_columns": ["id"], "max_rows": 3})'; - -- expect 3 rows on first page + comment on view "accountView" is e'@graphql({"primary_key_columns": ["id"], "max_rows": 3})'; + -- expect 3 rows on first page because of view max_rows select graphql.resolve($$ { - personCollection { + accountViewCollection { edges { node { id @@ -95,19 +201,17 @@ begin; } } $$); - resolve ------------------------------------------------------------------------------------------------------------- - {"data": {"personCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}]}}} + resolve +----------------------------------------------------------------------------------------------------------------- + {"data": {"accountViewCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}, {"node": {"id": 3}}]}}} (1 row) -- nested view with max_rows - create view parent as - select * from person; - comment on view parent is e'@graphql({"primary_key_columns": ["id"], "max_rows": 2})'; - -- expect 2 rows on first page + comment on view "accountViewWrapper" is e'@graphql({"primary_key_columns": ["id"], "max_rows": 2})'; + -- expect 2 rows on first page because of view max_rows select graphql.resolve($$ { - parentCollection { + accountViewWrapperCollection { edges { node { id @@ -116,9 +220,9 @@ begin; } } $$); - resolve ---------------------------------------------------------------------------------------- - {"data": {"parentCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}]}}} + resolve +--------------------------------------------------------------------------------------------------- + {"data": {"accountViewWrapperCollection": {"edges": [{"node": {"id": 1}}, {"node": {"id": 2}}]}}} (1 row) rollback; diff --git a/test/sql/max_rows_directive.sql b/test/sql/max_rows_directive.sql index 83177146..5405cdcf 100644 --- a/test/sql/max_rows_directive.sql +++ b/test/sql/max_rows_directive.sql @@ -3,10 +3,18 @@ begin; id int primary key ); + create view "accountView" as + select * from account; + comment on view "accountView" is e'@graphql({"primary_key_columns": ["id"]})'; + + create view "accountViewWrapper" as + select * from "accountView"; + comment on view "accountViewWrapper" is e'@graphql({"primary_key_columns": ["id"]})'; + insert into public.account(id) select * from generate_series(1, 100); - -- expect default 30 rows on first page + -- expect 30 rows on first page because of fallback to default select graphql.resolve($$ { accountCollection @@ -20,9 +28,35 @@ begin; } $$); + -- expect 30 rows on first page because of fallback to default + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + + -- expect 30 rows on first page because of fallback to default + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + comment on schema public is e'@graphql({"max_rows": 5})'; - -- expect 5 rows on first page + -- expect 5 rows on first page because of fallback to schema max_rows select graphql.resolve($$ { accountCollection @@ -36,9 +70,35 @@ begin; } $$); + -- expect 5 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + + -- expect 5 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + comment on schema public is e'@graphql({"max_rows": 40})'; - -- expect 40 rows on first page + -- expect 40 rows on first page because of fallback to schema max_rows select graphql.resolve($$ { accountCollection @@ -52,10 +112,36 @@ begin; } $$); + -- expect 40 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewCollection { + edges { + node { + id + } + } + } + } + $$); + + -- expect 40 rows on first page because of fallback to schema max_rows + select graphql.resolve($$ + { + accountViewWrapperCollection { + edges { + node { + id + } + } + } + } + $$); + -- table-specific max_rows comment on table account is e'@graphql({"max_rows": 5})'; - -- expect 5 rows on first page + -- expect 5 rows on first page because of table max_rows select graphql.resolve($$ { accountCollection { @@ -69,14 +155,12 @@ begin; $$); -- view-specific max_rows - create view person as - select * from account; - comment on view person is e'@graphql({"primary_key_columns": ["id"], "max_rows": 3})'; + comment on view "accountView" is e'@graphql({"primary_key_columns": ["id"], "max_rows": 3})'; - -- expect 3 rows on first page + -- expect 3 rows on first page because of view max_rows select graphql.resolve($$ { - personCollection { + accountViewCollection { edges { node { id @@ -87,14 +171,12 @@ begin; $$); -- nested view with max_rows - create view parent as - select * from person; - comment on view parent is e'@graphql({"primary_key_columns": ["id"], "max_rows": 2})'; + comment on view "accountViewWrapper" is e'@graphql({"primary_key_columns": ["id"], "max_rows": 2})'; - -- expect 2 rows on first page + -- expect 2 rows on first page because of view max_rows select graphql.resolve($$ { - parentCollection { + accountViewWrapperCollection { edges { node { id