[Из песочницы] Поиск по Postgres с использованием ZomboDb и elasticsearch

В какой-то момент разработки проекта встал вопрос поиска по большому количеству текстов. Причем, тексты имеют различную длину: от твиттов до больших статей. Сначала, основным поисковым движком был выбран встроенный в Postgres _tsvector. Для поиска по простым правилам его было вполне достаточно. Массив текстов рос с большой скоростью, а правила поиска усложнялись, поэтому встроенный движок уже не покрывал требований.

Да, существует sphinx, у него есть отличная интеграция с Postgres, но была цель найти решение для использования elasticsearch с Postgres. Почему? elasticsearch показывал хорошие результаты в некоторых case-ах проекта. Да и уже был сервер с ним для хранения логов logstash-а. Также было желание найти такой инструмент, который полностью возьмет на себя синхронизацию данных.

В результате всего на просторах сети был найден проект ZomboDb, который как раз подходил под требования.

Страница проекта на github.


Установка расширения

Этот раздел является пересказом официальной инструкции.

Поддерживаемые на данный момент версии пакетов:


Пакет Версии
Elasticsearch 1.7.1+ (not 2.0)
Postgres 9.3, 9.4, 9.5

Моя конфигурация: Postgres 9.4, elasticsearch 1.7.5


  1. Со страницы необходимо скачать и установить пакет с плагином для Postgres (deb или rpm)
  2. В postgresql.conf добавить строку:

    local_preload_libraries = 'zombodb.so'`
    

  3. Перезапустить базу и создать extension:

    psql db_name -c "CREATE EXTENSION zombodb;"
    

  4. Далее, с той-же страницы необходимо скачать плагин для elasticsearch и установить его:

    bin/plugin -i zombodb -u file:///path/to/zombodb-plugin-X.X.X.zip
    

  5. Добавить в elasticsearch.yml:

    threadpool.bulk.queue_size: 1024
    threadpool.bulk.size: 12
    http.max_content_length: 1024mb
    index.query.bool.max_clause_count: 1000000
    

  6. Перезапустить elasticsearch.

На этом установка закончена


Создание тестового индекса

Предположим, есть таблицы, в которой лежат теги:

CREATE TABLE public.tags (
  id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('tags_id_seq'::regclass),
  word CHARACTER VARYING(100) NOT NULL
);

Создание индекса:

CREATE INDEX tags_zdb_search_idx
ON tags
USING zombodb (zdb('tags', tags.ctid), zdb(tags))
WITH (url='http://localhost:9200/');

В результате этого запроса создается индекс, и данные сразу поступают в elasticsearch.
Запрос, который найдет слова мама и папа:

SELECT *
FROM tags
WHERE zdb('tags', ctid) ==> 'word:(мама,папа)';

Где word — название поля, по которому будет производиться поиск. Поиск реализуется с помощью оператора ==>.

Также, ZomboDb предоставляет домены phrase и fulltext, базирующиеся на типе text. Используя собственные домены можно определять маппинг для elasticsearch.


Язык запросов

С помощью запросов можно искать по отдельным полям индексируемой таблицы, также и по всем полям.
Запросы поддерживают логические операции (and, or, not), скобки.
Есть возможность использовать различные поисковые операторы. Например, запрос

SELECT *
FROM texts
WHERE zdb('texts', ctid) ==> 'text:папа';

где оператором является двоеточие, вернет тексты, содержащие слово папа.
Также поддерживаются операции more like this и fuzzy like this через операторы :@ и :@~ соответственно.
Пример:

SELECT *
FROM texts
WHERE zdb('texts', ctid) ==> '(text:@папа and title:@мама) or text:тетя';

Также, есть поддержка операторов сравнения:

SELECT *
FROM texts
WHERE zdb('texts', ctid) ==> 'comments > 10';

Подробное описание языка запросов в документации.


Выводы

Проект является хорошим продуктом, который работает «из коробки». Хорошо документирован, обновляется (поддерживается последняя версия Postgres, последний коммит на момент написания статьи 27-и дневной давности). Если хорошо и стабильно покажет себя в production, напишу обертку для sqlalchemy.

Спасибо за внимание!

© Habrahabr.ru