Select 行嵌套 json 数组字段包含 PostgreSQL 中提供的数组中的任何值?

Select rows where nested json array field includes any of values from a provided array in PostgreSQL?

我正在尝试编写一个 sql 查询来查找 table 中与提供的 json 数组的任何值匹配的行。

更具体地说,我有以下数据库 table:

CREATE TABLE mytable (
    name text,
    id SERIAL PRIMARY KEY,
    config json,
    matching boolean
);

INSERT INTO "mytable"(
  "name", "id", "config", "matching"
) 
VALUES 
  (
    E 'Name 1', 50,
    E '{"employees":[1,7],"industries":["1","3","4","13","14","16"],"levels":["1110","1111","1112","1113","1114"],"revenue":[0,5],"states":["AK","Al","AR","AZ","CA","CO","CT","DC","DE","FL","GA","HI","IA","ID","IL"]}', 
    TRUE
  ), 
  (
    E 'Name 2', 63,  
    E '{"employees":[3,5],"industries":["1"],"levels":["1110"],"revenue":[2,5],"states":["AK","AZ","CA","CO","HI","ID","MT","NM","NV","OR","UT","WA","WY"]}', 
    TRUE,
  ), 
  (
    E 'Name 3', 56,
    E '{"employees":[0,0],"industries":["14"],"levels":["1111"],"revenue":[7,7],"states":["AK","AZ","CA","CO","HI","ID","MT","NM","NV","OR","UT","WA","WY"]}', 
    TRUE,
  ),  
  (
    E 'Name 4', 61, 
    E '{"employees":[3,8],"industries":["1"],"levels":["1110"],"revenue":[0,5],"states":["AK","AZ","CA","CO","HI","ID","WA","WY"]}', 
    FALSE
  );

我需要使用给定的过滤参数对此 table 执行搜索查询。过滤参数基本上对应于 config 字段中的 json 键。它们来自客户端,看起来像这样:

{"employees": [1, 8], "industries": ["12", "5"]}
{"states": ["LA", "WA", "CA"], "levels": ["1100", "1100"], "employees": [3]}

并且给定这样的过滤器,我需要在我的 table 中找到包含来自提供的每个过滤器键的相应过滤器键的任何数组元素的行。

因此,给定过滤器 {"employees": [1, 8], "industries": ["12", "5"]},查询必须 return 所有行,其中 config 字段中的 [employees 键包含 18 AND 其中 config 字段中的 industries 键包含 125);

我需要从 javascript 代码动态生成这样的查询,以便我可以 include/exclude 通过特定参数过滤 adding/removing AND 运算符。

到目前为止,我有一个超长的运行查询,它在config字段中生成数组元素的所有可能组合,感觉很不对劲:

select * from mytable
    cross join lateral json_array_elements(config->'employees') as e1
    cross join lateral json_array_elements(config->'states') as e2
    cross join lateral json_array_elements(config->'levels') as e3
    cross join lateral json_array_elements(config->'revenue') as e4;

我也试过这样做:

select * from mytable
    where
        matching = TRUE
        and (config->'employees')::jsonb @> ANY(ARRAY ['[1, 7, 8]']::jsonb[])
        and (config->'states')::jsonb @> ANY(ARRAY ['["AK", "AZ"]']::jsonb[])
        and ........;

然而这并没有奏效,尽管看起来很有希望。

此外,我尝试使用 ?| 运算符但无济于事。


基本上,我需要的是:在 json 字段中给定一个数组键,检查该字段是否包含另一个数组(这是我的过滤参数)中提供的任何值;我必须动态地为多个过滤参数执行此操作。

所以逻辑如下:

select all rows from the table
   *where*
   matching = TRUE
   *and* config->key1 includes any of the keys from [5,6,8,7]
   *and* config->key2 includes any of the keys from [8,6,2]
   *and* so forth;

你能帮我实现这样一个 sql 查询吗?

或者这样的 sql 查询可能总是非常慢,最好在数据库级别之外进行此类过滤?

我会尝试类似的东西。我想有一定的副作用(例如,如果比较数据为空怎么办?)而且我没有在更大的数据集上测试它......这只是我想到的第一个......:[=​​14= ]

demo:db<>fiddle

SELECT 
    *
FROM
    mytable t
JOIN (SELECT '{"states": ["LA", "WA", "CA"], "levels": ["1100", "1100"], "employees": [3]}'::json as data) c 
ON 
  CASE WHEN c.data -> 'employees' IS NOT NULL THEN
     ARRAY(SELECT json_array_elements_text(t.config -> 'employees')) && ARRAY(SELECT json_array_elements_text(c.data -> 'employees'))
  ELSE TRUE END
  
  AND
  
  CASE WHEN c.data -> 'industries' IS NOT NULL THEN
     ARRAY(SELECT json_array_elements_text(t.config -> 'industries')) && ARRAY(SELECT json_array_elements_text(c.data -> 'industries'))
  ELSE TRUE END
  
  AND
  
  CASE WHEN c.data -> 'states' IS NOT NULL THEN
     ARRAY(SELECT json_array_elements_text(t.config -> 'states')) && ARRAY(SELECT json_array_elements_text(c.data -> 'states'))
  ELSE TRUE END
  
  AND
  
  CASE WHEN c.data -> 'revenue' IS NOT NULL THEN
     ARRAY(SELECT json_array_elements_text(t.config -> 'revenue')) && ARRAY(SELECT json_array_elements_text(c.data -> 'revenue'))
  ELSE TRUE END
  
  AND
  
  CASE WHEN c.data -> 'levels' IS NOT NULL THEN
     ARRAY(SELECT json_array_elements_text(t.config -> 'levels')) && ARRAY(SELECT json_array_elements_text(c.data -> 'levels'))
  ELSE TRUE END

加入条件说明:

      CASE WHEN c.data -> 'levels' IS NOT NULL THEN
         ARRAY(SELECT json_array_elements_text(t.config -> 'levels')) && ARRAY(SELECT json_array_elements_text(c.data -> 'levels'))
      ELSE TRUE END

如果您的比较数据不包含特定属性,则条件为 true,因此将被忽略。如果它包含一个属性,通过将两个 JSON 数组转换为简单的 Postgres 数组

来比较此属性的 table 和比较数组