在Array子对象上创建Postgres JSONB索引

问题描述 投票:3回答:1

我有一个带有JSONB列myTable的表myJsonb,其数据结构我想索引如下:

{
  "myArray": [
    {
      "subItem": {
        "email": "[email protected]"
      }
    },
    {
      "subItem": {
        "email": "[email protected]"
      }
    }
  ]
}

我想在email上运行索引查询,如:

SELECT *
FROM mytable
WHERE '[email protected]' IN (
  SELECT lower(
      jsonb_array_elements(myjsonb -> 'myArray')
      -> 'subItem'
      ->> 'email'
  )
);

如何为此创建Postgres JSONB索引?

arrays json postgresql indexing jsonb
1个回答
2
投票

如果您不需要lower(),则查询可以简单有效:

SELECT *
FROM   mytable
WHERE  myjsonb -> 'myArray' @> '[{"subItem": {"email": "[email protected]"}}]'

jsonb_path_ops索引支持:

CREATE INDEX mytable_myjsonb_gin_idx ON mytable
USING  gin ((myjsonb -> 'myArray') jsonb_path_ops);

但这场比赛是区分大小写的。

Case-insensitive!

如果您需要搜索以匹配无视案例,事情会变得更加复杂。

您可以使用此查询,类似于您的原始:

SELECT *
FROM   t
WHERE  EXISTS (
   SELECT 1
   FROM   jsonb_array_elements(myjsonb -> 'myArray') arr
   WHERE  lower(arr #>>'{subItem, email}') = '[email protected]'
   );

但我想不出一个使用索引的好方法。

相反,我会使用基于提取小写电子邮件数组的函数的表达式索引:

功能:

CREATE OR REPLACE FUNCTION f_jsonb_arr_lower(_j jsonb, VARIADIC _path text[])
  RETURNS jsonb LANGUAGE sql IMMUTABLE AS
'SELECT jsonb_agg(lower(elem #>> _path)) FROM jsonb_array_elements(_j) elem';

指数:

CREATE INDEX mytable_email_arr_idx ON mytable
USING  gin (f_jsonb_arr_lower(myjsonb -> 'myArray', 'subItem', 'email') jsonb_path_ops);

查询:

SELECT *
FROM   mytable 
WHERE  f_jsonb_arr_lower(myjsonb -> 'myArray', 'subItem', 'email') @> '"[email protected]"';

虽然这适用于无类型的字符串文字或实际的jsonb值,但如果您传递textvarchar(如在预准备语句中),它将停止工作。 Postgres不知道如何投射,因为输入是模棱两可的。在这种情况下,您需要显式转换:

... @> '"[email protected]"'::text::jsonb;

或传递一个简单的字符串而不包含双引号,并在Postgres中转换为jsonb

... @> to_jsonb('[email protected]'::text);

相关,有更多解释:

© www.soinside.com 2019 - 2024. All rights reserved.