如果嵌套jsonb

时间:2020-03-27 14:13:22

标签: sql postgresql nested jsonb postgresql-12

我是jsonb请求的新手,但遇到了问题。在“项目”表中,我有“ id”和“数据” 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"
      }
    ]
  }
]

数据中的值始终是内部包含对象的数组,但是有时对象可以具有内部包含对象的“字段”值。最多深一层。

如何选择项目的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%';

但是我不知道如何在字段数组中搜索字段(如果存在)。

在此先感谢您的帮助。

编辑:
看来我的问题不清楚。我会尝试做得更好。

我要做的是在“数据”列中找到与我的搜索条件相匹配的所有“项目”。这无需查看对象是处于第一级还是在对象的“字段”键内。 再举一个例子。如果我搜索,则应选择此记录:

  • 'paramId': 3 AND 'value': 'cat
  • 'paramId': 4 AND 'value': LIKE '%og%'

匹配的对象位于带有'paramId':6的对象的'fields'键中,我不知道该怎么做。

2 个答案:

答案 0 :(得分:1)

这可以使用JSON/Path expression表示,而无需取消嵌套

要搜索paramId = 3和value ='cat'

select *
from items
where data @? '$[*] ? ( (@.paramId == 3 && @.value == "cat") || exists( @.fields[*] ? (@.paramId == 3 && @.value == "cat")) )'

$[*]部分遍历第一级数组的所有元素。要检查fields数组中的元素,可以使用exists()运算符来嵌套表达式。 @.fields[*]遍历fields数组中的所有元素,并再次应用相同的表达式。我看不出有办法避免重复值的方法。

对于“喜欢”情况,可以使用like_regex:

select *
from items
where data @? '$[*] ? ( (@.paramId == 4 && @.value like_regex ".*og.*") || exists( @.fields[*] ? (@.paramId == 4 && @.value like_regex ".*og.*")) )'

答案 1 :(得分:0)

到目前为止,我已经找到了一种解决方案,但是它并不是很干净,我不知道它在生产1000万条记录时的性能如何。

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:我“拉平”第一层和第二层(第二层位于“字段”键中)
B1,B2:这是我的搜索条件
C:我确保所有字段都符合条件。如果3个条件-> COUNT(*)> = 3

对我来说,这看起来确实不干净。它是为开发目的而工作的,但我认为有更好的方法来实现它。

如果有人有一个主意,多谢他/她!