Отслеживание функций SQL в Hasura

#postgresql #hasura

#postgresql #hasura

Вопрос:

По сути, я пытаюсь сравнить две строки JSONB и вернуть числовое значение. Но я хочу иметь возможность запрашивать его. Я не уверен, следует ли мне использовать пользовательскую функцию SQL, вычисляемое поле или столбец, созданный Postgres, поэтому мне нужен небольшой совет.

У меня есть столбец jsonb для каждого пользователя, который хранит несколько сотен ключей / значений как таковых:

 USERS TABLE:
| username     | user_jsonb_column                          |
|-----------------------------------------------------------|
| 'user1'      |  {"key1":"value1", "key2":"value2" ... }   |
|--------------|--------------------------------------------|
| 'user2'      |  {"key2":"value2", "key3":"value3" ... }   |
  

Я пытаюсь вычислить сходство строк jsonb из 2 пользователей с помощью очень простого SQL-запроса как такового:

 SELECT ROUND ((

SELECT COUNT(*) from (
    SELECT jsonb_each(user_jsonb_column)
    FROM users WHERE username = 'johndoe'
      INTERSECT
    SELECT jsonb_each(user_jsonb_column)
    FROM users WHERE username = 'janedoe'
)::decimal AS SAME_PAIRS

/ --divide it by

SELECT COUNT(*) from (
    SELECT jsonb_object_keys(user_jsonb_column)
    FROM users WHERE username = 'johndoe'
      INTERSECT
    SELECT jsonb_object_keys(user_jsonb_column)
    FROM users WHERE username = 'janedoe'
  ) as SAME_KEYS

) * 100) as similarity_percentage
  

Это работает по назначению и дает мне результат сходства между 2 объектами json в процентах.

Я пытаюсь превратить это в функцию, чтобы я мог запрашивать процент сходства у 2 пользователей как таковых:

 query {
  calculate_similarity_percentage(
    args: {user1: "johndoe", user2: "janedoe"}
  ){
      similarity_percentage_value
  }
}
  

Но я застрял на этом этапе, потому что я не уверен, должен ли я думать в терминах a trackable custom SQL function (который должен возвращать SETOF <TABLE> , но мне нужно числовое значение), a computed field (который также может возвращать BASE тип) или, может быть, a Postgres generated column в моей ситуации.

Я читал https://hasura.io/docs/1.0/graphql/core/schema/custom-functions.html и https://hasura.io/docs/1.0/graphql/core/schema/computed-fields.html но я не мог до конца понять, как подойти к этому, поэтому буду признателен за любую помощь или комментарии.


Обновление: Да, как указал Лоренц Альбе, я могу создать такую функцию:

 CREATE OR REPLACE FUNCTION public.calculate_similarity_percentage(text, text)
 RETURNS numeric
 LANGUAGE sql
 STABLE
AS $function$

SELECT ROUND(
(select count(*) from (
    SELECT jsonb_each(user_jsonb_column) FROM users WHERE username = $1
    INTERSECT
    SELECT jsonb_each(user_jsonb_column) FROM users WHERE username = $2
    ) as SAME_PAIRS
)::decimal / (
    select count(*) from (
    SELECT jsonb_object_keys(user_jsonb_column) FROM users WHERE username = $1
    INTERSECT
    SELECT jsonb_object_keys(user_jsonb_column) FROM users WHERE username = $2
    ) as SAME_KEYS
    )
    * 100) as similarity_percentage

$function$
  

Тогда я могу выполнить эту функцию:

 SELECT calculate_similarity_percentage('johndoe','janedoe')
  

И он возвращает это без каких-либо проблем:

 similarity_percentage
62
  

Однако я хотел бы, чтобы Hasura отслеживала эту функцию, чтобы я мог запрашивать ее в GraphQL как:

 query MyQuery {
  calculate_similarity_percentage(args: {user1: "johndoe", user2: "janedoe"}) {
    similarity_percentage
  }
}
  

Но если я попытаюсь track выполнить указанную выше функцию, Хасура скажет:

 **SQL Execution Failed**
in function "calculate_similarity_percentage":
the function "calculate_similarity_percentage" cannot be tracked for the following reasons:
• the function does not return a "COMPOSITE" type
• the function does not return a SETOF
• the function does not return a SETOF table
  

Я понятия не имею, смогу ли я найти обходной путь и вернуть числовое значение в виде «СОСТАВНОЙ» или таблицы SETOF.

Комментарии:

1. @LaurenzAlbe: Извините, я был недостаточно ясен, я обновил свой вопрос дополнительной частью. Проблема в том, что я не могу заставить Hasura отслеживать функцию, чтобы она была доступна в GraphQL.

Ответ №1:

Вот как я решил свой случай. Но это не было оптимальным решением, поэтому я не принимаю это как ответ.

В итоге я создал еще одну таблицу, подобную этой:

 
USER_RELATION_TABLE:
| user1_col   | user2_col  |
|--------------------------|
| 'johndoe'   | 'janedoe'  |
|--------------------------|
| 'brad'      | 'angelina' |
|--------------------------|
|     ...     |     ...    |
  

Затем я добавил вычисляемое поле в таблицу отношений со следующей функцией:

 CREATE OR REPLACE FUNCTION public.calculate_similarity_percentage(user_relation_row user_relation_table)
 RETURNS numeric
 LANGUAGE sql
 STABLE
AS $function$

SELECT ROUND(
(select count(*) from (
    SELECT jsonb_each(user_jsonb_column) FROM users
    WHERE username = user_relation_row.user1_col
    INTERSECT
    SELECT jsonb_each(user_jsonb_column) FROM users
    WHERE username = user_relation_row.user2_col
    ) as SAME_PAIRS
)::decimal / (
    select count(*) from (
    SELECT jsonb_object_keys(user_jsonb_column) FROM users
    WHERE username = user_relation_row.user1_col
    INTERSECT
    SELECT jsonb_object_keys(user_jsonb_column) FROM users
    WHERE username = user_relation_row.user2_col
    ) as SAME_KEYS
    )
    * 100) as similarity_percentage

$function$
  

Теперь я могу запросить его в GraphQL следующим образом:

 query MyQuery {
  user_relation_table {
    similarity
  }
}