【问题标题】:PostgreSQL search/index (GIN index?) across two columns/arrays跨两列/数组的 PostgreSQL 搜索/索引(GIN 索引?)
【发布时间】:2018-10-26 20:44:29
【问题描述】:

假设我们有一个 PostgreSQL 表 contacts,每条记录都有一堆带标签的电子邮件地址(标签和电子邮件对)——其中一个是“主要”。

这是这样存储的:

  • id主键
  • email文字
  • email_label文字
  • metadatajsonb
    • emails数组
    • email文字
    • label文字

例如,一条记录可能类似于:

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,但我不确定如何将所有部分组合在一起。

【问题讨论】:

    标签: sql postgresql database-indexes


    【解决方案1】:

    创建以下两个索引:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-30
      • 2013-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      相关资源
      最近更新 更多