从postgres中的复杂嵌套结构中检索具有特定键名的json元素

问题描述 投票:1回答:2

我在postgres json字段中有一个复杂的嵌套json结构。我想用键'$ type'列出所有元素值,无论它们出现在嵌套结构中的哪个位置。该结构包含嵌套在数组中的数组到多个深层。我应该使用什么SQL查询?

表结构是:

create table if not exists documents
(
  id text not null
    constraint documents_pkey primary key,
  value json not null
)
json postgresql
2个回答
0
投票

此递归函数从复杂的jsonb对象中提取所有属性:

create or replace function jsonb_extract_all(jsonb_data jsonb, curr_path text[] default '{}')
returns table(path text[], value text)
language plpgsql as $$
begin
    if jsonb_typeof(jsonb_data) = 'object' then
        return query 
            select (jsonb_extract_all(val, curr_path || key)).*
            from jsonb_each(jsonb_data) e(key, val);
    elseif jsonb_typeof(jsonb_data) = 'array' then
        return query 
            select (jsonb_extract_all(val, curr_path || ord::text)).*
            from jsonb_array_elements(jsonb_data) with ordinality e(val, ord);
    else
        return query
            select curr_path, jsonb_data::text;
    end if;
end $$;

用法示例:

with my_table(data) as (
select
    '{
        "$type": "a",
        "other": "x",
        "nested_object": {"$type": "b"},
        "array_1": [{"other": "y"}, {"$type": "c"}],
        "array_2": [{"$type": "d"}, {"other": "z"}]
    }'::jsonb
)

select f.*
from my_table
cross join jsonb_extract_all(data) f
where path[cardinality(path)] = '$type';

         path          | value 
-----------------------+-------
 {$type}               | "a"
 {array_1,2,$type}     | "c"
 {array_2,1,$type}     | "d"
 {nested_object,$type} | "b"
(4 rows)    

0
投票

您可以使用递归查询。我在这里完成了大部分工作:

with recursive dived(jkey, jval, jtype) as (
 select t.key, t.value, 
  json_typeof(t.value) jtype
  from  json_each('{"id":"243769","name":"domains","type":"TABLE","adata":{"sfield":"name"},"fields":{"id":{"ind":1,"enum":null,"refs":[null,null],"reqd":true,"type":"int4","constr":["p",null],"default":null},"name":{"ind":2,"enum":null,"refs":[null,null],"reqd":true,"type":"text","constr":["u",null],"default":null},"appid":{"ind":5,"enum":null,"refs":["apps","id"],"reqd":true,"type":"int4","constr":[null,null],"default":null},"userid":{"ind":8,"enum":null,"refs":["users","id"],"reqd":true,"type":"int8","constr":[null,null],"default":null},"createdat":{"ind":6,"enum":null,"refs":[null,null],"reqd":true,"type":"timestamptz","constr":[null,null],"default":null},"updatedat":{"ind":7,"enum":null,"refs":[null,null],"reqd":true,"type":"timestamptz","constr":[null,null],"default":null},"subdomainforward":{"ind":4,"enum":null,"refs":[null,null],"reqd":false,"type":"text","constr":[null,null],"default":null},"wilcardsubdomain":{"ind":3,"enum":null,"refs":[null,null],"reqd":false,"type":"bool","constr":[null,null],"default":null}},"schema":"web","relchecks":0,"relhasrules":false,"relhastriggers":true,"relrowsecurity":false,"relforcerowsecurity":false}'::json) t

   union all

  select t.key, t.value,
    json_typeof(t.value) jtype
    from dived, json_each(dived.jval) as t
      where dived.jtype in ('object' /*, 'array'*/)
 )

select * From dived where jkey = 'yourkey' limit 100

当涉及到数组和json_array_elements时,你只需要添加一个或多个逻辑的情况。

使用json迭代嵌套数组对于递归查询来说并不太难,但我发现它很乏味。

将CASE WHEN放在json_each前面,如下所示:

  CASE WHEN dived.jtype = 'array' then
  json_array_elements(dived.jval) t

有可能在场景中处理情况,否则您可能需要专门针对数组的单独递归查询,然后使用对象键/值进行联合。

您也可以在这里找到更多信息:Collect Recursive JSON Keys In Postgres

我希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.