graphql-engine/rfcs/computed-fields-filters-perms-orderby.md

4.6 KiB

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. 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.

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)

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)

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

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'

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

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

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'

- 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

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

SELECT
  computed_field_function("table_alias") AS "computed_field_alias"
 (SELECT * FROM our_table) AS "table_alias"
ORDER BY
  "computed_field_alias" DESC