Поиск / индекс PostgreSQL (индекс GIN?) По двум столбцам / массивам - PullRequest
0 голосов
/ 16 мая 2018

Допустим, у нас есть таблица PostgreSQL contacts, каждая запись имеет набор помеченных адресов электронной почты (пары меток и электронных писем) & mdash; один из которых является «основным».

Это хранится как:

  • id первичный ключ
  • email текст
  • email_label текст
  • metadata JSONB
    • 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, а в этом вопросе упоминается jsonb_path_ops , но я не уверен, как свести все кусочки вместе.

1 Ответ

0 голосов
/ 16 мая 2018

Создайте следующие два индекса:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...