Postgresql поиск, если существует во вложенном jsonb - PullRequest
0 голосов
/ 27 марта 2020

Я новичок в запросе jsonb, и у меня возникла проблема. Внутри таблицы «Items» у меня есть «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"
      }
    ]
  }
]

Значение в данных всегда является массивом с объектом внутри, но иногда объект может иметь значение 'fields' с объектами внутри. Максимальная глубина - один уровень.

Как выбрать идентификатор элементов, например, объекта, содержащего «paramId»: 3 и «value»: «cat», а также объект с «paramId» ": 5 and" value "LIKE '% 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%';

, но я не знать, как искать в массиве полей, если поля существуют.

Заранее благодарю за помощь.

РЕДАКТИРОВАТЬ:
Похоже, мой вопрос не ясен. Я постараюсь сделать его лучше.

Что я хочу сделать, так это найти все объекты, имеющие в столбце «данные» объекты, которые соответствуют моим критериям поиска. Это не смотря на то, находятся ли объекты на первом уровне или внутри ключа 'fields' объекта. Опять например. Эта запись должна быть выбрана, если я ищу:

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

соответствующие значения находятся в клавише 'fields' объект с 'paramId': 6, и я не знаю, как это сделать.

Ответы [ 2 ]

1 голос
/ 28 марта 2020

Это можно выразить, используя JSON / Path выражение без необходимости отмены всего 1007 * Часть $[*] перебирает все элементы массива первого уровня. Чтобы проверить элементы в массиве 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.*")) )'
0 голосов
/ 28 марта 2020

Пока я нашел решение, но оно не совсем чистое, и я не знаю, как оно будет работать в производстве с записями 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: Я "сглаживаю" первое и второй уровень (второй уровень в ключе 'fields')
B1, B2: это мои критерии поиска
C: я проверяю, чтобы у полей были все критерии, соответствующие. Если 3 критерия -> COUNT (*)> = 3

Это действительно не выглядит чистым для меня. Это работает с целью разработки, но я думаю, что есть лучший способ сделать это.

Если у кого-то есть идея, Большое ему спасибо!

...