Postgres将jsonb值返回为jsonb数组吗?

时间:2018-08-21 01:38:29

标签: postgresql recursion

tl; dr-有什么方法可以从Postgres中的jsonb对象获取值作为jsonb_array吗?


尝试在postgres中使用递归cte来展平像这样的任意深的树结构:

string

通常紧随this post之后,但是我仍然坚持处理C节点,实际上我真正想要的是{ "type": "foo", "properties": {...}, "children": [ "type": "bar", "properties": {...}, "children": [ "type": "multivariate", "variants": { "arbitrary-name": { properties: {...}, children: [...], }, "some-other-name": { properties: {...}, children: [...], }, "another": { properties: {...}, children: [...], } } ] ] }

更新:

抱歉,我显然应该包含我尝试过的查询:

type: "multivariate"

该架构只是一个jsonb_agg(jsonb_object_values(json_object -> 'variants'))和一个jsonb WITH RECURSIVE tree_nodes (id, json_element) AS ( -- non recursive term SELECT id, node FROM trees UNION -- recursive term SELECT id, CASE WHEN jsonb_typeof(json_element) = 'array' THEN jsonb_array_elements(json_element) WHEN jsonb_exists(json_element, 'children') THEN jsonb_array_elements(json_element -> 'children') WHEN jsonb_exists(json_element, 'variants') THEN (select jsonb_agg(element.value) from jsonb_each(json_element -> 'variants') as element) END AS json_element FROM tree_nodes WHERE jsonb_typeof(json_element) = 'array' OR jsonb_typeof(json_element) = 'object' ) select * from tree_nodes;

此查询给出错误:

id

我只想要node

更新2:

再次阅读所有内容后,我意识到这是一个问题,这是由于我使用的是最新版本的PostgreSQL(10.3),显然它不再允许从ERROR: set-returning functions are not allowed in CASE LINE 16: THEN jsonb_array_elements(json_element -> 'children') ^ HINT: You might be able to move the set-returning function into a LATERAL FROM item. 语句返回集合,这有点像获得这种使树木变平的方法来工作的关键。在最新版本的PostgreSQL中可能有某种方法可以实现相同的目的,但是我不知道该怎么做。

2 个答案:

答案 0 :(得分:1)

jsonb_each()子句中使用FROMjsonb_agg(<jsonb_each_alias>.value)中的SELECT,例如:

select
    id,
    jsonb_agg(child.value)
from
    (values
      (101, '{"child":{"a":1,"b":2}}'::jsonb),
      (102, '{"child":{"c":3,"d":4,"e":5}}'::jsonb
    )) as t(id, json_object), -- example table, replace values block with actual tablespec
    jsonb_each(t.json_object->'child') as child
group by t.id

如果您需要在{{1}之前迭代更高级别的数组,则可以始终链接其他jsonb函数,这些函数在setof jsonb中返回jsonb_array_elements(例如FROM) }};例如:

jsonb_each

更新答案

为回答您的评论和问题,编辑有关需要遍历每个树节点的select id, jsonb_agg(sets.value) from (values (101, '{"children":[{"a_key":{"a":1}},{"a_key":{"b":2}}]}'::jsonb), (102, '{"children":[{"a_key":{"c":3,"d":4,"e":5}},{"a_key":{"f":6}}]}'::jsonb )) as t(id, json_object), -- example table, replace values block with actual tablespec jsonb_array_elements(t.json_object->'children') elem, jsonb_each(elem->'a_key') as sets group by t.id; 并提取'children''的问题;我可以通过将CTE分为多个阶段来实现此目标:

'variants

这种将查询分解为“流水线”操作的功能是我最喜欢的CTE之一-它使查询更易于理解,维护和调试。

答案 1 :(得分:1)

db<>fiddle

使用更多的子元素和更深的结构(更多的嵌套元素)扩展了测试数据:

{
    "type": "foo", 
    "children": [
        {
            "type" : "bar1", 
            "children" : [{
                "type" : "blubb",
                "children" : [{
                    "type" : "multivariate",
                    "variants" : {
                        "blubb_variant1": {
                            "properties" : {
                                "blubb_v1_a" : 100
                            },
                            "children" : ["z", "y"]
                        },
                        "blubb_variant2": {
                            "properties" : {
                                "blubb_v2_a" : 300,
                                "blubb_v2_b" : 4200
                            },
                            "children" : []
                        }
                    }
                }]
            }]
        },
        {
            "type" : "bar2", 
            "children" : [{
                "type" : "multivariate",
                "variants" : {
                    "multivariate_variant1": {
                        "properties" : {
                            "multivariate_v1_a" : 1,
                            "multivariate_v1_b" : 2
                        },
                        "children" : [1,2,3]
                    },
                    "multivariate_variant2": {
                        "properties" : {
                            "multivariate_v2_a" : 3,
                            "multivariate_v2_b" : 42,
                            "multivariate_v2_d" : "fgh"
                        },
                        "children" : [4,5,6]
                    },
                    "multivariate_variant3": {
                        "properties" : {
                            "multivariate_v3_a" : "abc",
                            "multivariate_v3_b" : "def"
                        },
                        "children" : [7,8,9]
                    }
                }
            },
            {
                "type" : "blah",
                "variants" : {
                    "blah_variant1": {
                        "properties" : {
                            "blah_v1_a" : 1,
                            "blah_v1_b" : 2
                        },
                        "children" : [{
                            "type" : "blah_sub1",
                            "variants" : {
                                "blah_sub1_variant1" : {
                                    "properties" : {
                                        "blah_s1_v1_a" : 12345,
                                        "children" : ["a",1, "bn"]
                                    }
                                }
                            }
                        }]
                    },
                    "blah_variant2": {
                        "properties" : {
                            "blah_v2_a" : 3,
                            "blah_v2_b" : 42,
                            "blah_v2_c" : "fgh"
                        },
                        "children" : [4,5,6]
                    }
                }
            }]
        }
    ]
}

结果:

variants                 json                                                                                                                                                                                            
-----------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  
"multivariate_variant1"  {"children": [1, 2, 3], "properties": {"multivariate_v1_a": 1, "multivariate_v1_b": 2}}                                                                                                         
"multivariate_variant2"  {"children": [4, 5, 6], "properties": {"multivariate_v2_a": 3, "multivariate_v2_b": 42, "multivariate_v2_d": "fgh"}}                                                                            
"multivariate_variant3"  {"children": [7, 8, 9], "properties": {"multivariate_v3_a": "abc", "multivariate_v3_b": "def"}}                                                                                                 
"blah_variant1"          {"children": [{"type": "blah_sub1", "variants": {"blah_sub1_variant1": {"properties": {"children": ["a", 1, "bn"], "blah_s1_v1_a": 12345}}}}], "properties": {"blah_v1_a": 1, "blah_v1_b": 2}}  
"blah_variant2"          {"children": [4, 5, 6], "properties": {"blah_v2_a": 3, "blah_v2_b": 42, "blah_v2_c": "fgh"}}                                                                                                    
"blubb_variant1"         {"children": ["z", "y"], "properties": {"blubb_v1_a": 100}}                                                                                                                                     
"blubb_variant2"         {"children": [], "properties": {"blubb_v2_a": 300, "blubb_v2_b": 4200}}                                                                                                                         
"blah_sub1_variant1"     {"properties": {"children": ["a", 1, "bn"], "blah_s1_v1_a": 12345}}   

查询:

WITH RECURSIVE json_cte(variants, json) AS (
    SELECT NULL::jsonb, json FROM (
        SELECT '{/*FOR TEST DATA SEE ABOVE*/}'::jsonb as json
    )s

    UNION

    SELECT  
        row_to_json(v)::jsonb -> 'key',                                -- D        
        CASE WHEN v IS NOT NULL THEN row_to_json(v)::jsonb -> 'value' ELSE c END  -- C
    FROM json_cte
         LEFT JOIN LATERAL jsonb_array_elements(json -> 'children') as c ON TRUE  -- A
         LEFT JOIN LATERAL jsonb_each(json -> 'variants') as v ON TRUE -- B
)
SELECT * FROM json_cte WHERE variants IS NOT NULL

WITH RECURSIVE结构以递归方式检查元素。 UNION的第一部分是起点。第二部分是递归部分,其中将进行下一步的最后一次计算。

A:如果在当前JSON中有一个children元素,则每个子元素的所有元素都将扩展为一行

B:如果当前JSON具有元素variants,则所有元素都将扩展为一条记录。请注意,在示例中,一个JSON元素可以包含variantschildren元素。

C:如果存在variants元素,则扩展记录将转换回json。结果结构为{"key" : "name_of_variant", "value" : "json_of_variant"}value将是下一次递归的JSON(variants的JSON可以拥有自己的children元素。这就是为什么它起作用的原因)。否则,展开的children元素将是下一个数据

D:如果存在variants元素,则打印key