多个 key/value 对的 PostgresQL 嵌套 jsonb 查询

PostgresSQL nested jsonb query for multiple key/value pairs

开始使用 JSONB 数据类型,希望有人能帮助我。

我有一个 table (properties) 有两列(id 作为主键和 数据 作为 jsonb)。 数据结构为:

    {
        "ProductType": "ABC",
        "ProductName": "XYZ",
        "attributes": [
            {
            "name": "Color",
            "type": "STRING",
            "value": "Silver"
            },
            {
            "name": "Case",
            "type": "STRING",
            "value": "Shells"
            },
            ...
        ]
    }

我想获取属性具有特定值的所有行,即 return Case = 'Shells' and/or 的所有行颜色 = 'Red'.

我尝试了以下方法,但无法应用两个条件,例如 Case = 'Shells' 和 Color = 'Silver'。 当单个属性的名称和值匹配条件时,我可以获得行,但我不知道如何让它对多个属性起作用。


编辑 1: 我能够使用以下查询获得结果:

WITH properties AS (
    select *
    from (
        values 
        (1, '{"ProductType": "ABC","ProductName": "XYZ","attributes": [{"name": "Color","type": "STRING","value": "Silver"},{"name": "Case","type": "STRING","value": "Shells"}]}'::jsonb), 
        (2, '{"ProductType": "ABC","ProductName": "XYZ","attributes": [{"name": "Color","type": "STRING","value": "Red"},{"name": "Case","type": "STRING","value": "Shells"}]}'::jsonb)
    ) s(id, data)
)
select 
    *
from (
    SELECT 
        id,
        jsonb_object_agg(attr ->> 'name', attr -> 'value') as aggr
    FROM properties m,
       jsonb_array_elements(data -> 'attributes') as attr
    GROUP BY id
    ) a
where aggr ->> 'Color' = 'Red' and aggr ->> 'Case' LIKE 'Sh%'

我可能有数百万条这样的记录,所以我想我现在唯一关心的是这是否有效,如果无效,是否有更好的方法?

step-by-step demo:db<>fiddle

SELECT 
    id
FROM properties m,
   jsonb_array_elements(data -> 'attributes') as attr
GROUP BY id
HAVING jsonb_object_agg(attr ->> 'name', attr -> 'value') @> '{"Color":"Silver", "Case":"Shells"}'::jsonb

问题是,jsonb_array_elements() 将两个相关值移动到不同的记录中。但是,此调用是获取值所必需的。因此,您需要在能够读取它们之后重新聚合这些值。这将使以相关方式检查它们成为可能。

这可以通过使用 jsonb_object_agg() 聚合函数来实现。这里的技巧是我们创建一个具有 "name":"value" 等属性的对象。因此,有了它,我们可以使用 @> 运算符轻松检查是否所有必需的属性都在 JSON 对象中。


关于“编辑 1”

demo:db<>fiddle

你可以这样做:

SELECT
    *
FROM ( 
    SELECT 
        id,
        jsonb_object_agg(attr ->> 'name', attr -> 'value') as obj
    FROM properties m,
       jsonb_array_elements(data -> 'attributes') as attr
    GROUP BY id
) s
WHERE obj ->> 'Color' = 'Silver'
    AND obj ->> 'Case' LIKE 'Sh%'
  1. 如上所述为所有 JSONs
  2. 创建新的 JSON 结构
  3. 之后过滤这个结果。

或者,您可以根据需要在 HAVING 子句中使用 jsonb_object_agg()。我想您需要检查哪种方式在您的情况下性能更高:

SELECT 
    id
FROM properties m,
   jsonb_array_elements(data -> 'attributes') as attr
GROUP BY id
HAVING 
   jsonb_object_agg(attr ->> 'name', attr -> 'value') ->> 'Color' = 'Silver'
   AND
   jsonb_object_agg(attr ->> 'name', attr -> 'value') ->> 'Case' LIKE 'Sh%'