假设我们有一个PostgreSQL表contacts
,每个记录都有一堆标记的电子邮件地址(标签和电子邮件对) - 其中一个是“主要”。
这存储如下:
id
主键email
text email_label
text metadata
jsonb
emails
数组email
text label
text 例如,记录可能类似于:
id: 1
email: 'a@a.com'
email_label: 'a'
metadata: {
"emails": [
{
"email": "b@b.com",
"label": "b"
},
{
"email": "c@c.com",
"label": "c"
}
]
}
鉴于此存储模式,我们希望能够通过任何电子邮件地址查找记录。
天真的查询看起来像:
SELECT id
FROM contacts
WHERE
email = 'my@email.com' OR
metadata -> 'emails' @> '[{"email": "my@email.com"}]'
有没有办法创建一个可以显着加快此操作的索引?它需要自动更新以响应记录的更改,理想情况下跨文本列和嵌套的索引JSONB专栏。
此处的特定用例可以高效,快速地通过电子邮件地址进行查找,而无需对此结构进行检修或创建新的关系表。
我认为解决方案涉及使用GIN索引和this question mentions jsonb_path_ops,但我不确定如何将所有碎片拼凑在一起。
答案 0 :(得分:2)
创建以下两个索引:
CREATE INDEX contacts_email_idx
ON contacts (email);
CREATE INDEX contacts_metadata_emails_idx
ON contacts USING gin ((metadata -> 'emails') jsonb_path_ops);
然后查询将很快,因为索引完全匹配这两个条件,并且可以使用位图索引扫描进行组合。
EXPLAIN (COSTS off)
SELECT id
FROM contacts
WHERE email = 'my@email.com'
OR metadata -> 'emails' @> '[{"email": "my@email.com"}]';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on contacts
Recheck Cond: ((email = 'my@email.com'::text) OR ((metadata -> 'emails'::text) @> '[{"email": "my@email.com"}]'::jsonb))
-> BitmapOr
-> Bitmap Index Scan on contacts_email_idx
Index Cond: (email = 'my@email.com'::text)
-> Bitmap Index Scan on contacts_metadata_emails_idx
Index Cond: ((metadata -> 'emails'::text) @> '[{"email": "my@email.com"}]'::jsonb)
(7 rows)