2015-01-02 3 views
2

У меня есть вложенная и иерархическая структура выражается в JSON например:Postgres JSONB запрос о вложенных/рекурсивных элементах

{ 
    "id":1, 
    "children": [ 
     { "id":2 }, 
     { "id": 3, "children": [ 
     { "id": 4 } 
     ] 
     } 
    ] 
} 

Может Postgres ответить на запрос, содержит ли запись "id": 4 в любой части документа ?

Если да, такие запросы при поддержке JSONB индексации добавлен в версии 9.4?

+2

К сожалению, я попытался решить эту проблему с помощью 9.3, однако я столкнулся с проблемами с получением s ame для запуска на объектах и ​​массивах. На самом деле я думаю, что в 9.4 это может быть намного проще. Я начал писать рекурсивный запрос, чтобы сделать это, но, увы, я потерпел неудачу - на сегодняшний день. –

+1

Ну, конечно, я понял, что взломать, было ли поле json массивом или объектом на основе первого символа его текстового представления. –

+1

Не можете напрямую ответить на ваш вопрос, но я использую JSONSelect и PLV8 для обработки моих более сложных задач обхода JSON и запросов. Потратил некоторое время, чтобы настроить, но он работает хорошо для нас. – trvrm

ответ

1

UPDATE: Благодаря therealgaxbo на Reddit, мы начали с моим оригинальным кодом и разработали что-то более краткое:

with recursive deconstruct (jsonlevel) as(
    values ('{"id":1,"children":[{"id":2},{"id":3,"children":[{"id":4}]}]}'::json) 

    union all 

    select 
     case left(jsonlevel::text, 1) 
      when '{' then (json_each(jsonlevel)).value 
      when '[' then json_array_elements(jsonlevel) 
     end as jsonlevel 
    from 
     deconstruct 
    where 
     left(jsonlevel::text, 1) in ('{', '[') 
) 
select * from deconstruct where case when left(jsonlevel::text, 1) = '{' then jsonlevel->>'id' = '4' else false end; 

Моего первоначальный ответ ниже:

Я экспериментировал, как сумасшедшие и, наконец, придумал с чем-то вроде этого:

with recursive ret(jsondata) as 
(select row_to_json(col)::text jsondata from 
json_each('{ 
    "id":1, 
    "children": [ 
     { "id":2 }, 
     { "id": 3, "children": [ 
     { "id": 4 } 
     ] 
     } 
    ] 
}'::json) col 
union 
select case when left(jsondata::text,1)='[' then row_to_json(json_each(json_array_elements(jsondata)))::text 
when left((jsondata->>'value'),2)='{}' then null::text 
when left((jsondata->>'value')::text,1)='[' then row_to_json(json_each(json_array_elements(jsondata->'value')))::text 
else ('{"key":'||(jsondata->'key')||', "value":'||(jsondata->'value')||'}')::json::text end jsondata 
from (
select row_to_json(json_each(ret.jsondata::json)) jsondata 
from ret) xyz 

) 
select max(1) from ret 
where jsondata::json->>'key'='id' 
and jsondata::json->>'value'='1' 
+0

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

+0

Спасибо, Джо за ваш код. Честно говоря, ваш SQL просто подтвердил мои опасения - для меня это слишком сложно, чтобы оправдать его. Я не хочу, чтобы все в команде стали гуру Postgres, чтобы понять, что происходит. Я предполагаю, что у меня есть два варианта: свести мою иерархию JSON к простому списку с ParentID или просто забыть об этом в Postgres и запросить вещи на уровне приложений. –

+1

С 9.4 и jsonb это будет намного проще .. теперь вы можете легко написать функцию, чтобы скрыть некоторые сложности, если это необходимо. –

Смежные вопросы