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
再次阅读所有内容后,我意识到这是一个问题,这是由于我使用的是最新版本的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中可能有某种方法可以实现相同的目的,但是我不知道该怎么做。
答案 0 :(得分:1)
在jsonb_each()
子句中使用FROM
和jsonb_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)
使用更多的子元素和更深的结构(更多的嵌套元素)扩展了测试数据:
{
"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元素可以包含variants
或children
元素。
C:如果存在variants元素,则扩展记录将转换回json。结果结构为{"key" : "name_of_variant", "value" : "json_of_variant"}
。 value
将是下一次递归的JSON(variants
的JSON可以拥有自己的children
元素。这就是为什么它起作用的原因)。否则,展开的children
元素将是下一个数据
D:如果存在variants
元素,则打印key