我有一个带有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索引?
如果您不需要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);
但这场比赛是区分大小写的。
如果您需要搜索以匹配无视案例,事情会变得更加复杂。
您可以使用此查询,类似于您的原始:
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
值,但如果您传递text
或varchar
(如在预准备语句中),它将停止工作。 Postgres不知道如何投射,因为输入是模棱两可的。在这种情况下,您需要显式转换:
... @> '"[email protected]"'::text::jsonb;
或传递一个简单的字符串而不包含双引号,并在Postgres中转换为jsonb
:
... @> to_jsonb('[email protected]'::text);
相关,有更多解释: