Преобразование столбца jsonb в пользовательский тип

#postgresql #postgresql-13

Вопрос:

Я пытаюсь преобразовать каждую строку в столбце jsonb в тип, который я определил, и, похоже, у меня не получается.

У меня есть приложение, которое удаляет статьи с открытой платформы Guardian и сбрасывает ответы (как jsonb) в таблицу приема, в столбец с именем ‘body’. Другие столбцы представляют собой последовательный идентификатор и временную метку, извлеченную из полезной нагрузки ответа, которая помогает моему приложению только очищать новые данные.

Я хотел бы переместить данные дампа ответа в правильно определенную таблицу, и, поскольку я знаю схему ответа, я определил тип ( my_type ) .

Я ссылался на функции и операторы 9.16. JSON в документах Postgres. Я могу получить одну запись в качестве своего типа:

select * from jsonb_populate_record(null::my_type, (select body from data_ingestion limit 1));

производит

ID Тип Идентификатор раздела
example_id example_type example_section_id

(сокращенно для краткости)

Если я удалю ограничение, я получу сообщение об ошибке, что имеет смысл: подзапрос будет предоставлять несколько строк, для jsonb_populate_record которых ожидается только одна.

Я могу заставить его выполнять несколько строк, но результат не разбивается на столбцы:

select jsonb_populate_record(null::my_type, body) from reviews_ingestion limit 3;

производит:

jsonb_populate_record
(example_id_1,example_type_1,example_section_id_1, …)
(example_id_2,example_type_2,example_section_id_2, …)
(example_id_3,example_type_3,example_section_id_3, …)

Это немного странно, я бы ожидал увидеть имена столбцов; в конце концов, это точка предоставления типа.

Я знаю, что могу сделать это, используя функциональность запросов Postgres JSON, например

 select 
  body -> 'id' as id,
  body -> 'type' as type,
  body -> 'sectionId' as section_id,
  ...
from reviews_ingestion;
 

Это работает, но кажется довольно неэлегантным. Плюс я теряю типы данных.

Я также рассматривал возможность объединения всех строк в body столбце в массив JSON, чтобы иметь возможность предоставить это jsonb_populate_recordset , но это кажется немного глупым подходом и вряд ли будет эффективным.

Есть ли способ добиться того, чего я хочу, используя функции Postgres?

Ответ №1:

Может быть, вам это нужно — разбить my_type запись на столбцы:

 select (jsonb_populate_record(null::my_type, body)).* 
from reviews_ingestion
limit 3;
-- or whatever other query clauses here
 

т.е. выбрать все из этих my_type записей. Все имена и типы столбцов на месте.
Вот иллюстрация. Мой пользовательский тип — это delmet и CTO t удаленно имитирует data_ingestion .

 create type delmet as (x integer, y text, z boolean);

with t(i, j, k) as 
(
 values 
 (1, '{"x":10, "y":"Nope", "z":true}'::jsonb, 'cats'), 
 (2, '{"x":11, "y":"Yep", "z":false}', 'dogs'), 
 (3, '{"x":12, "y":null, "z":true}', 'parrots')
)
select i, (jsonb_populate_record(null::delmet, j)).*, k
from t;
 

Результат:

i x y z k
1 10 Нет верно кошки
2 11 Да false собаки
3 12 верно попугаи