Postgresql рекурсивный анализ вложенного JSON - PullRequest
0 голосов
/ 12 октября 2018

Я пытаюсь проанализировать вложенный JSON в Postgres, который имеет следующую структуру:

{
    "id": 0,
    "children": [{
            "id": 965,
            "children": [{
                    "id": 967
                },
                {
                    "id": 969
                },
                {
                    "id": 971
                },
                {
                    "id": 973
                }
            ]
        },
        {
            "id": 974,
            "children": [{
                    "id": 976
                },
                {
                    "id": 978
                }
            ]
        }
    ]
}  

Моя желаемая форма вывода такая:

-------------------------------------------
|parent_id          |child_id             |
-------------------------------------------
|0                  |965                  |
|0                  |974                  |
|965                |967                  |
|965                |969                  |
|965                |971                  |
|965                |973                  |
|974                |976                  |
|974                |978                  |
-------------------------------------------  

Я пытаюсьдля этого используйте рекурсивные CTE, однако, что бы я ни пытался, я сталкиваюсь либо с could not identify an equality operator for type json, либо с ERROR: set-returning functions are not allowed in CASE.Я могу получить данные для одного уровня, но это далеко от того, что я намерен делать, поэтому любые предложения относительно того, что я мог бы сделать по-другому, были бы очень полезны.

Здесь - это то, как вы можете быстро опробовать его в случае необходимости вместе со всем, что я пробовал, и с ошибками, которые возникли.Также было бы бонусом, если ваши предложения работают на json и jsonb оба.

Спасибо

Редактировать: число уровней JSON не фиксировано, оно может быть до любого уровня (максимум 3-4).
Редактировать: добавление запросов, которые я пробовал здесьсамо по себе:

with recursive cte(id,json_element) as (
select tree->'id',tree->'children' from json_test
union
select json_element->'id',json_element->'children' from cte
) select * from cte;
--could not identify an equality operator for type json

WITH RECURSIVE cte(id, children) AS (
  SELECT
    tree->'id' as id,
    tree->'children' as children
  FROM json_test
  UNION ALL
  SELECT
    children -> 'id',
    children -> 'children'
  FROM cte,
    --json_each(CASE WHEN json_typeof(cte.children) <> 'object' THEN '{}' :: JSON ELSE cte.children END) AS t
    json_each(CASE WHEN json_typeof(cte.children) = 'array' THEN json_array_elements(cte.children) ELSE cte.children END) AS t
)
SELECT * FROM cte WHERE json_typeof(cte.children) <> 'object';
 --ERROR: set-returning functions are not allowed in CASE
  --Hint: You might be able to move the set-returning function into a LATERAL FROM item.  

Я также пытался несколько раз изменить эти запросы, но в итоге получилось с теми же ошибками.

1 Ответ

0 голосов
/ 13 октября 2018

demo: db <> fiddle

WITH RECURSIVE jsondata AS (
    SELECT 
        data ->> 'id' as parent_id,
        data -> 'children' as children 
    FROM (
        SELECT '{"id": 0, "children": [{"id": 965, "children": [{"id": 967}, {"id": 969}, {"id": 971}, {"id": 973}]}, {"id": 974, "children": [{"id": 976}, {"id": 978}]}]}'::jsonb as data
    ) s

    UNION

    SELECT
        value ->> 'id',
        value -> 'children'
    FROM jsondata, jsonb_array_elements(jsondata.children)

)
SELECT 
    parent_id, 
    jsonb_array_elements(children) ->> 'id' 
FROM jsondata 
WHERE children IS NOT NULL

Это просто: получите идентификатор текущего элемента как parent_id.Дайте массив children в качестве нового объекта json.

В части рекурсии вы должны развернуть массив children в одну строку на каждого потомка.Также вы не можете получить id и children.

Поскольку это делается до последнего дочернего узла (у которого нет собственных дочерних элементов по определению), их столбцы children равны NULL.Таким образом, эти строки могут быть отфильтрованы.Последний шаг - развернуть объекты children json и прочитать их элементы id, чтобы вывести их так, как вы ожидаете.

Теперь столбцы имеют тип text.Конечно, вы могли бы целочисленное приведение в конце концов:

parent_id::int, 
(jsonb_array_elements(children) ->> 'id')::int
...