Можно ли добавить ограничение для проверки каждого вставленного объекта в массив JSON с помощью Postgres? - PullRequest
0 голосов
/ 16 октября 2019

Я пытаюсь выяснить, как добавить ограничение в мою таблицу, которая содержит объект JSON в Postgres. Я хотел бы, чтобы это ограничение гарантировало, что jpg будет иметь ненулевое свойство md5.

Я попытался проверить подмассив, но не могу заставить его работать с массивом. Я могу проверить свойство, если использовать оператор стрелки разыменования (таблица -> 'jpg' -> 0? 'Md5'). Я попытался также использовать функцию jsonb_array_elements (), но это не разрешено внутри оператора ограничения.

Например:

{
    jpg: [
        {
            md5: "some md5",
            ...
        }
    ]
}

Я бы использовал это:

ALTER TABLE table ADD CONSTRAINT md5_is_defined CHECK(table->'jpg'->0 ? 'md5')

Но я не хочу проверять только первый вставленный элемент

Я хотел бы знать, есть ли какая-то хитрость для использования jsonb_array_elements в этом операторе проверки. И да, я понимаю, что должен нормализовать свои данные. Я пытался это сделать, но писать заявления для них стало чудовищно.

1 Ответ

2 голосов
/ 16 октября 2019

Массивы чрезвычайно громоздки в SQL, поскольку они в основном нарушают все, что делает SQL. Проверка каждого элемента в массиве на наличие чего-либо обычно является убедительным признаком того, что массив был неправильным выбором с самого начала.

С Postgres 12 это чрезвычайно легко сделать:

alter table the_table 
    add constraint md5_is_defined 
    check (jsonb_path_exists(the_column, '$.jpg[*].md5'));

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

create or replace function check_md5(p_input jsonb)
  returns boolean
as
$$
select exists (select *
               from jsonb_array_elements(p_input -> 'jpg') as t(e) 
               where e ? 'md5');
$$
language sql
immutable;

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

alter table the_table 
    add constraint md5_is_defined 
    check (check_md5(the_column));

Изменить

Чтобы проверить, еслиопределенный ключ содержит непустой массив, вы можете использовать что-то вроде этого:

alter table the_table
    add constraint non_empty_array
    check (jsonb_path_exists(the_column, '$.event ? (@.teams.type() == "array" && @.teams.size() > 0)'));

Проверка для @.teams.type() == "array" необходима, потому что простой {"teams": "yes"} также возвращает ненулевое значение для size()

...