Postgresql 搜索嵌套的 jsonb 中是否存在
Postgresql search if exists in nested jsonb
我是 jsonb 请求的新手,遇到了问题。在 'Items' table 中,我有 'id' 和 'data' jsonb。这是看起来像数据的内容:
[
{
"paramId": 3,
"value": "dog"
},
{
"paramId": 4,
"value": "cat"
},
{
"paramId": 5,
"value": "fish"
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 3,
"value": "cat"
},
{
"paramId": 4,
"value": "dog"
}
]
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 5,
"value": "cat"
},
{
"paramId": 3,
"value": "dog"
}
]
}
]
data 中的值始终是一个包含对象的数组,但有时对象可以具有包含对象的 'fields' 值。最多一层深。
如何 select 项目的 ID,例如包含 "paramId": 3 和 "value": "cat" 的对象,还有一个对象"paramId":5 和 "value" 喜欢“%ish%”。
我已经找到了当对象处于 0 级时执行此操作的方法
SELECT i.*
FROM items i
JOIN LATERAL jsonb_array_elements(i.data) obj3(val) ON obj.val->>'paramId' = '3'
JOIN LATERAL jsonb_array_elements(i.data) obj5(val) ON obj2.val->>'paramId' = '5'
WHERE obj3.val->>'valeur' = 'cat'
AND obj5.val->>'valeur' LIKE '%ish%';
但我不知道如果字段存在,如何在字段数组中搜索。
预先感谢您的帮助。
编辑:
看来我的问题不清楚。我会努力让它变得更好。
我想要做的是找到所有 'item' 在 'data' 列中符合我的搜索条件的对象。这没有查看对象是在第一层还是在对象的 'fields' 键内。
再举个例子。如果我搜索此记录应该 selected:
'paramId': 3 AND 'value': 'cat
'paramId': 4 AND 'value': LIKE '%og%'
匹配项在 'paramId' 对象的 'fields' 键中:6,我不知道该怎么做。
现在我找到了一个解决方案,但它不是很干净,我不知道它在 10M 记录的生产环境中表现如何。
SELECT i.id, i.data
FROM ( -- A;
select it.id, it.data, i as value
from items it,
jsonb_array_elements(it.data) i
union
select it.id, it.data, f as value
from items it,
jsonb_array_elements(it.data) i,
jsonb_array_elements(i -> 'fields') f
) as i
WHERE (i.value ->> 'paramId' = '5' -- B1;
AND i.value ->> 'value' LIKE '%ish%')
OR (i.value ->> 'paramId' = '3' -- B2;
AND i.value ->> 'value' = 'cat')
group by i.id, i.data
having COUNT(*) >= 2; -- C;
A:我"flatten"一级和二级(二级在'fields'键)
B1、B2:这些是我的搜索条件
C:我确保字段符合所有条件。如果有 3 个条件 --> COUNT(*) >=3
我觉得真的不干净。它用于开发目的,但我认为有更好的方法。
如果有人有想法非常感谢him/her!
这可以用 JSON/Path expression 表示,而不需要取消嵌套所有内容
要搜索paramId = 3 and value = 'cat'
select *
from items
where data @? '$[*] ? ( (@.paramId == 3 && @.value == "cat") || exists( @.fields[*] ? (@.paramId == 3 && @.value == "cat")) )'
$[*]
部分遍历第一级数组的所有元素。要检查 fields
数组中的元素,使用 exists()
运算符嵌套表达式。 @.fields[*]
遍历 fields
数组中的所有元素并再次应用相同的表达式。不过,我看不出有什么方法可以避免重复这些值。
对于 "like" 条件,您可以使用 like_regex:
select *
from items
where data @? '$[*] ? ( (@.paramId == 4 && @.value like_regex ".*og.*") || exists( @.fields[*] ? (@.paramId == 4 && @.value like_regex ".*og.*")) )'
我是 jsonb 请求的新手,遇到了问题。在 'Items' table 中,我有 'id' 和 'data' jsonb。这是看起来像数据的内容:
[
{
"paramId": 3,
"value": "dog"
},
{
"paramId": 4,
"value": "cat"
},
{
"paramId": 5,
"value": "fish"
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 3,
"value": "cat"
},
{
"paramId": 4,
"value": "dog"
}
]
},
{
"paramId": 6,
"value": "",
"fields": [
{
"paramId": 5,
"value": "cat"
},
{
"paramId": 3,
"value": "dog"
}
]
}
]
data 中的值始终是一个包含对象的数组,但有时对象可以具有包含对象的 'fields' 值。最多一层深。
如何 select 项目的 ID,例如包含 "paramId": 3 和 "value": "cat" 的对象,还有一个对象"paramId":5 和 "value" 喜欢“%ish%”。
我已经找到了当对象处于 0 级时执行此操作的方法
SELECT i.*
FROM items i
JOIN LATERAL jsonb_array_elements(i.data) obj3(val) ON obj.val->>'paramId' = '3'
JOIN LATERAL jsonb_array_elements(i.data) obj5(val) ON obj2.val->>'paramId' = '5'
WHERE obj3.val->>'valeur' = 'cat'
AND obj5.val->>'valeur' LIKE '%ish%';
但我不知道如果字段存在,如何在字段数组中搜索。
预先感谢您的帮助。
编辑:
看来我的问题不清楚。我会努力让它变得更好。
我想要做的是找到所有 'item' 在 'data' 列中符合我的搜索条件的对象。这没有查看对象是在第一层还是在对象的 'fields' 键内。 再举个例子。如果我搜索此记录应该 selected:
'paramId': 3 AND 'value': 'cat
'paramId': 4 AND 'value': LIKE '%og%'
匹配项在 'paramId' 对象的 'fields' 键中:6,我不知道该怎么做。
现在我找到了一个解决方案,但它不是很干净,我不知道它在 10M 记录的生产环境中表现如何。
SELECT i.id, i.data
FROM ( -- A;
select it.id, it.data, i as value
from items it,
jsonb_array_elements(it.data) i
union
select it.id, it.data, f as value
from items it,
jsonb_array_elements(it.data) i,
jsonb_array_elements(i -> 'fields') f
) as i
WHERE (i.value ->> 'paramId' = '5' -- B1;
AND i.value ->> 'value' LIKE '%ish%')
OR (i.value ->> 'paramId' = '3' -- B2;
AND i.value ->> 'value' = 'cat')
group by i.id, i.data
having COUNT(*) >= 2; -- C;
A:我"flatten"一级和二级(二级在'fields'键)
B1、B2:这些是我的搜索条件
C:我确保字段符合所有条件。如果有 3 个条件 --> COUNT(*) >=3
我觉得真的不干净。它用于开发目的,但我认为有更好的方法。
如果有人有想法非常感谢him/her!
这可以用 JSON/Path expression 表示,而不需要取消嵌套所有内容
要搜索paramId = 3 and value = 'cat'
select *
from items
where data @? '$[*] ? ( (@.paramId == 3 && @.value == "cat") || exists( @.fields[*] ? (@.paramId == 3 && @.value == "cat")) )'
$[*]
部分遍历第一级数组的所有元素。要检查 fields
数组中的元素,使用 exists()
运算符嵌套表达式。 @.fields[*]
遍历 fields
数组中的所有元素并再次应用相同的表达式。不过,我看不出有什么方法可以避免重复这些值。
对于 "like" 条件,您可以使用 like_regex:
select *
from items
where data @? '$[*] ? ( (@.paramId == 4 && @.value like_regex ".*og.*") || exists( @.fields[*] ? (@.paramId == 4 && @.value like_regex ".*og.*")) )'