#json #postgresql
#json #postgresql
Вопрос:
Учитывая следующую таблицу, я хочу найти категорию по идентификатору, а затем получить объект JSON, содержащий его родительскую строку в формате JSON. если я посмотрю идентификатор категории 999, мне нужна следующая структура json.
Как я могу этого добиться?
{
id: 999
name: "Sprinting",
slug: "sprinting",
description: "sprinting is fast running",
parent: {
id: 2
name: "Running",
slug: "running ",
description: "All plans related to running.",
parent: {
id: 1
name: "Sport",
slug: "sport ",
description: null,
}
}
}
CREATE TABLE public.categories (
id integer NOT NULL,
name text NOT NULL,
description text,
slug text NOT NULL,
parent_id integer
);
INSERT INTO public.categories (id, name, description, slug, parent_id) VALUES (1, 'Sport', NULL, 'sport', NULL);
INSERT INTO public.categories (id, name, description, slug, parent_id) VALUES (2, 'Running', 'All plans related to running.', 'running', 1);
INSERT INTO public.categories (id, name, description, slug, parent_id) VALUES (999, 'Sprinting', 'sprinting is fast running', 'sprinting', 2);```
Ответ №1:
(Объяснение ниже)
WITH RECURSIVE hierarchy AS (
SELECT id, parent_id
FROM categories
WHERE id = 999
UNION
SELECT
c.id, c.parent_id
FROM categories c
JOIN hierarchy h ON h.parent_id = c.id
),
jsonbuilder AS (
SELECT
c.id,
h.parent_id,
jsonb_build_object('id', c.id, 'name', c.name, 'description', c.description, 'slug', c.slug) as jsondata
FROM hierarchy h
JOIN categories c ON c.id = h.id
WHERE h.parent_id IS NULL
UNION
SELECT
c.id,
h.parent_id,
jsonb_build_object('id', c.id, 'name', c.name, 'description', c.description, 'slug', c.slug, 'parent', j.jsondata)
FROM hierarchy h
JOIN categories c ON c.id = h.id
JOIN jsonbuilder j ON j.id = h.parent_id
)
SELECT
jsondata
FROM jsonbuilder
WHERE id = 999
Как правило, вам нужен рекурсивный запрос для создания вложенных объектов JSON. Наивный подход:
- Получить запись с
id = 999
помощью, создать объект JSON - Получить запись с
id = parent_id
помощью функции записи с999
помощью (id = 2
), создать объект JSON, добавить этот родительский атрибут als к предыдущему объекту. - Повторяйте шаг 2, пока родительский файл не станет нулевым
К сожалению, я не видел простого способа добавить вложенный родительский элемент. Каждый шаг вкладывает JSON в глубину. Да, я уверен, что есть способ сделать это, сохранив путь к родителям и используя jsonb_set()
его каждый раз. Это может сработать.
С другой стороны, гораздо проще поместить созданный в данный момент объект JSON в новый. Так сказать, подход заключается в создании JSON с самого глубокого уровня. Для этого вам также понадобится родительский путь. Но вместо того, чтобы создавать и сохранять его при создании объекта JSON, вы могли бы сначала создать его с помощью отдельного рекурсивного запроса:
WITH RECURSIVE hierarchy AS (
SELECT id, parent_id
FROM categories
WHERE id = 999
UNION
SELECT
c.id, c.parent_id
FROM categories c
JOIN hierarchy h ON h.parent_id = c.id
)
SELECT * FROM hierarchy
Извлечение записи с id = 999
помощью и ее родительского элемента. После этого извлеките запись родительского элемента, его id
и его parent_id
. Делайте это до parent_id
тех пор, пока не будет NULL
.
Это дает:
id | parent_id
--: | --------:
999 | 2
2 | 1
1 | null
Теперь у нас есть простой список сопоставлений, который показывает дерево обхода. В чем разница с нашими исходными данными? Если бы ваши данные содержали два или более дочерних элемента для записи id = 1
, мы бы не знали, какой дочерний элемент мы должны использовать, чтобы, наконец, достичь дочернего 999
элемента . Однако в этом результате перечислены только отношения привязки и не будут возвращены какие-либо родственные элементы.
Хорошо имея это, мы можем перемещаться по дереву от самого верхнего элемента, который может быть встроен на самом глубоком уровне:
- Извлеките запись, у которой нет родительского элемента. Создайте объект JSON из его данных.
- Извлеките дочерний элемент предыдущей записи. Создайте объект JSON из его данных и вставьте предыдущие данные JSON в качестве родительских.
- Продолжайте, пока не останется дочернего элемента.
Как это работает?
В этом запросе используются рекурсивные CTE. Первая часть — это начальный запрос, так сказать, первая запись. Вторая часть, часть после UNION
, является рекурсивной частью, которая обычно ссылается на само WITH
предложение. Это всегда ссылка на предыдущий ход.
Часть JSON — это просто создание объекта JSON с использованием jsonb_build_object(), который принимает произвольное количество значений. Таким образом, мы можем использовать данные текущей записи и дополнительно для parent
атрибута уже созданные данные JSON из предыдущего хода.
Комментарии:
1. Это работает потрясающе, большое спасибо за вашу помощь!