mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
179 lines
4.6 KiB
Markdown
179 lines
4.6 KiB
Markdown
|
Note: This RFC assumes computed fields for **Postgres** backend.
|
||
|
|
||
|
OSS issue: https://github.com/hasura/graphql-engine/issues/3772
|
||
|
|
||
|
Before we proceed to the specification, we shall address the known limitation
|
||
|
|
||
|
|
||
|
### Limitation
|
||
|
|
||
|
All the undermentioned implementations assumes computed field function has table row input and/or
|
||
|
session (`json` type) input arguments. But we support computed fields with functions having multiple arguments.
|
||
|
Input values for additional arguments are provided via GraphQL field [arguments](https://spec.graphql.org/June2018/#sec-Language.Arguments).
|
||
|
For example, defining `get_articles` computed field for `author` table with `get_articles(table_input author, search text)` SQL
|
||
|
function to return author's articles containing `search` keyword.
|
||
|
```graphql
|
||
|
query {
|
||
|
author {
|
||
|
id
|
||
|
name
|
||
|
get_articles(search: "Covid 19"){
|
||
|
title
|
||
|
content
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
It is complex to support such additional function inputs via GraphQL arguments.
|
||
|
So, here I'm assuming the following limitation:
|
||
|
|
||
|
**Allow filter/order by/permission for computed fields with no input arguments other than table row and session json input types**
|
||
|
|
||
|
### Filter
|
||
|
|
||
|
Reference OSS ticket: https://github.com/hasura/graphql-engine/issues/7100
|
||
|
|
||
|
Currently, we only support table columns and relationships (object and array) to filter rows in any GraphQL query. For example, to fetch an author whose `id` is `1` the query would be (using column in filter)
|
||
|
|
||
|
```graphql
|
||
|
query {
|
||
|
authors(where: {id: {_eq: 1}}){
|
||
|
id
|
||
|
first_name
|
||
|
last_name
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Similarly, to fetch articles whose `author`'s `first_name` is 'Bob', the query would be (using object relation in filter)
|
||
|
|
||
|
```graphql
|
||
|
query {
|
||
|
articles(where: {author: {first_name: {_eq: "Bob"}}}){
|
||
|
id
|
||
|
title
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Now, I defined a computed field `full_name` to the `author` table using the following SQL function
|
||
|
|
||
|
```sql
|
||
|
CREATE OR REPLACE FUNCTION public.full_name(author_table author)
|
||
|
RETURNS text
|
||
|
LANGUAGE sql
|
||
|
STABLE
|
||
|
AS $function$
|
||
|
SELECT author_table.first_name || ' ' || author_table.last_name
|
||
|
$function$
|
||
|
```
|
||
|
|
||
|
I should able to fetch an author whose `full_name` is 'Bob Morley'
|
||
|
|
||
|
```graphql
|
||
|
query {
|
||
|
authors(where: {full_name: {_eq: "Bob Morley"}}){
|
||
|
id
|
||
|
full_name
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The above query isn't possible currently and hence, we need to support computed fields in filter expression (`where` clause).
|
||
|
|
||
|
#### Approach
|
||
|
|
||
|
```sql
|
||
|
SELECT "base".* FROM "authors" AS "base" WHERE "full_name"("base") = 'Bob Morley'
|
||
|
```
|
||
|
|
||
|
Simply, our approach is to use the SQL function in the `WHERE` expression of the generated SQL. So, aforementioned GraphQL query
|
||
|
translates to
|
||
|
```sql
|
||
|
SELECT
|
||
|
coalesce(json_agg("root"), '[]') AS "root"
|
||
|
FROM
|
||
|
(
|
||
|
SELECT
|
||
|
row_to_json(
|
||
|
(
|
||
|
SELECT
|
||
|
"_2_e"
|
||
|
FROM
|
||
|
(
|
||
|
SELECT
|
||
|
"_1_root.base"."first_name" AS "first_name",
|
||
|
"_1_root.base"."last_name" AS "last_name",
|
||
|
"public"."user_full_name"("_1_root.base") AS "full_name"
|
||
|
) AS "_2_e"
|
||
|
)
|
||
|
) AS "root"
|
||
|
FROM
|
||
|
(
|
||
|
SELECT
|
||
|
*
|
||
|
FROM
|
||
|
"public"."user" AS "_0_base"
|
||
|
WHERE
|
||
|
(
|
||
|
("public"."user_full_name"("_0_base".*)) = (('Bob Morley') :: text)
|
||
|
)
|
||
|
) AS "_1_root.base"
|
||
|
) AS "_3_root"
|
||
|
```
|
||
|
|
||
|
### Permission
|
||
|
|
||
|
Reference OSS ticket: https://github.com/hasura/graphql-engine/issues/7102
|
||
|
|
||
|
Just like using computed fields in `where` expression, similarly we need have an option to include computed fields in permission
|
||
|
`check` and `filter` expression
|
||
|
|
||
|
For example, I need to define a select permission on `author` table whose `full_name` is like 'Bob'
|
||
|
|
||
|
```json
|
||
|
- type: create_select_permission
|
||
|
args:
|
||
|
table:
|
||
|
name: author
|
||
|
role: user
|
||
|
permission:
|
||
|
columns: '*'
|
||
|
filter:
|
||
|
full_name:
|
||
|
_like: 'Bob'
|
||
|
```
|
||
|
|
||
|
#### Approach
|
||
|
|
||
|
Similar to `filtering` approach, we generate `where` expression with the `filter`/`check` expression provided
|
||
|
in the permission metadata definition
|
||
|
|
||
|
### Order by
|
||
|
|
||
|
Reference OSS ticket: https://github.com/hasura/graphql-engine/issues/7103
|
||
|
|
||
|
Enable using computed fields in order by expresssion. For example, fetch authors ordering by `full_name`
|
||
|
|
||
|
```graphql
|
||
|
query {
|
||
|
authors(order_by: {full_name: desc}){
|
||
|
id
|
||
|
first_name
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
#### Approach
|
||
|
|
||
|
We extract computed fields with proper aliases and use the same in the `ORDER BY` SQL expression.
|
||
|
The generated SQL will look like
|
||
|
|
||
|
```sql
|
||
|
SELECT
|
||
|
computed_field_function("table_alias") AS "computed_field_alias"
|
||
|
(SELECT * FROM our_table) AS "table_alias"
|
||
|
ORDER BY
|
||
|
"computed_field_alias" DESC
|
||
|
```
|