按嵌套 JSONB 文档中的条件查询数据集

Posted

技术标签:

【中文标题】按嵌套 JSONB 文档中的条件查询数据集【英文标题】:Querying dataset by conditions in nested JSONB document 【发布时间】:2017-05-26 19:57:55 【问题描述】:

我在 PostgreSQL 数据库 (v 9.6) 中有一个相当简单的表:

CREATE TABLE foobar (id serial, data jsonb);

以下是存储在数据列中的 JSONB 文档示例:

[ key: 'foo', value: 100 ,  key: 'bar', value: 5 ,  key: 'baz', value: 10 ]

我正在尝试编写一个选择,它将返回满足 JSONB 文档中条件的每一行并仅选择指定的嵌套文档;即对于foo 大于X 的每一行,返回所有以baz 为键的嵌套文档。

到目前为止,我已经提出了这样的查询,但它不起作用 - 它返回 0 条记录。

SELECT id
  FROM foobar
 WHERE (data->>'key' = 'foo' AND (data->>'value')::numeric > 5)
    OR (data->>'key' = 'bar' AND (data->>'value')::numeric < 10)

如果有人知道如何优化最终查询,那就太好了。谢谢!

【问题讨论】:

使用&gt;=&lt;=? 【参考方案1】:

对于 foo 大于 X 的每一行,返回所有以 baz 为键的嵌套文档。

使用jsonb_array_elements(data) 两次。第一个用于与键 foo 比较值,第二个用于查找与键 baz 的对象:

with foobar(id, data) as (
values
(1, 
'[
     "key": "foo", "value": 100 , 
     "key": "bar", "value": 5 , 
     "key": "baz", "value": 10 
]'::jsonb)
)

select id, value_baz
from foobar,
jsonb_array_elements(data) el_foo(value_foo),
jsonb_array_elements(data) el_baz(value_baz)
where value_foo->>'key' = 'foo' and (value_foo->>'value')::numeric > 5
and value_baz->>'key' = 'baz';

 id |          value_baz          
----+-----------------------------
  1 | "key": "baz", "value": 10
(1 row)

您的 json 列的格式很奇怪。我认为没有理由在这里使用 json 数组。您可以像这样以简单的形式存储相同的信息:

' "foo": 100, "bar": 5, "baz": 10 '

在这种情况下,您的查询可能很简单:

with foobar(id, data) as (
values
(1, ' "foo": 100, "bar": 5, "baz": 10 '::jsonb)
)

select id, data->'baz' as baz
from foobar
where (data->>'foo')::numeric > 5;

 id | baz 
----+-----
  1 | 10
(1 row) 

【讨论】:

是的,第二种解决方案要好得多;我最初已经尝试过,并在数据上使用 gin 索引实现了约 200 毫秒的选择(我确实有一个相当大的数据集)。第一个选项在我的数据集上不起作用 - 它是否太大并且选择需要超过 30 秒【参考方案2】:

您可以通过以下方式实现:

将 JSON 数组取消嵌套到元素中(使用 the jsonb_array_elements function) HAVING bool_or(...) 确保至少有一个元素以 foo 为键且 value 大于 5 aggregate filtered:那些以bar为键的子文档
SELECT     id, jsonb_agg(e) FILTER (WHERE e ->> 'key' = 'bar')
FROM       foobar
CROSS JOIN jsonb_array_elements(data) e
WHERE      data @> '["key":"foo","key":"bar"]'
GROUP BY   id
HAVING     bool_or(e ->> 'key' = 'foo' AND e -> 'value' > '5')

注意事项

e -&gt; 'value' &gt; '5' 会将 value JSON 属性与 JSON 值 5 进行比较,如果您在 JSON 文档中使用数字,这将非常有效。如果不是这种情况(即您也有 JSON 字符串,您想将其作为数字处理),请使用 CAST,例如 (e -&gt;&gt; 'value')::numeric &gt; 5 WHERE data @&gt; '["key":"foo","key":"bar"]' 是完全可选的。我在这里包括了,因为这是您可以从GIN index(在jsonb 列上;如果有的话)中挤出的最多内容。但是,如果这些键几乎在您的表中的每一行上,那将几乎没有用。 不幸的是,每个解决方案都需要使用jsonb_array_elements(),这违背了 JSON 索引的用例。在您当前的设计中,顺序表扫描是不可避免的(除非上面的谓词实际上可以过滤掉很多行)。

http://rextester.com/XCPB80482

【讨论】:

以上是关于按嵌套 JSONB 文档中的条件查询数据集的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB 条件查询和排序

如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?

在嵌套选择查询中按条件分组后加入

查询包含(或不包含)嵌套文档数组(被填充)但匹配特定条件的文档

将查询构建器条件转换为 MongoDB 操作,包括嵌套的子文档数组

Django / PostgresQL jsonb (JSONField) - 将选择和更新转换为一个查询