Эффективный поиск на сайте с помощью Elasticsearch

8 min read

Elasticsearch – это мощный поисковый движок, позволяющий организовать эффективный поиск в базе данных. Несмотря на обилие возможностей, начать пользоваться им под силу даже новичку.

Основным назначением Elasticsearch является сложный полнотекстовый поиск в базе с учетом морфологии языка, контролем опечаток и ранжированием результатов по релевантности.

Elasticsearch представляет из себя noSQL базу данных с управлением через HTTP запросы и плотной интеграцией с системой визуализации данных (Kibana), системой сбора событий и логов (Logstash) и различными инструментами анализа данных.

Представляем краткий гайд о том, как установить Elasticsearch на Linux, заполнить базу данных и формировать простые поисковые запросы.

Установка

Скачать Elasticsearch можно по ссылке с официального сайта. Для Ubuntu нам понадобится DEB-пакет.

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.0.deb

Для работы потребуется Java 8. Установим её.

apt-get install default-jre

Теперь можно установить DEB-пакет с помощью команды dpkg.

dpkg -i elasticsearch-6.4.0.deb

Запустить сервис можно с помощью команды

service elasticsearch start

Решаем проблему с нехваткой оперативной памяти

На серверах с небольшим объемом оперативной памяти может возникнуть ошибка Cannot allocate memory. Чтобы её устранить, откройте файл /etc/elasticsearch/jvm.options и отредактируйте в нем две строки, определяющие объем доступной памяти.

- -Xms1g - -Xmx1g + -Xms512m + -Xmx512m

1g означает, что Elasticsearch будет использовать 1 гигабайт оперативной памяти. 512m — 512 мегабайт. Вы можете подставить своё значение в зависимости от характеристик сервера. Узнать объем оперативной памяти в байтах можно с помощью команды free.

total used free shared buff/cache available Mem: 1009048 439472 552860 2344 16716 569576 Swap: 499996 220968 279028

Проверяем работу сервера

Убедитесь в том, что сервер запустился на 9200 порту и отвечает с помощью команды

curl http://localhost:9200/

Elasticsearch ответит вам на HTTP запрос примерно следующим текстом

{ "name" : "N8G0WmE", "cluster_name" : "elasticsearch", "cluster_uuid" : "1Bhuh06rQvucrSYwL_iJHQ", "version" : { "number" : "6.4.0", "build_flavor" : "default", "build_type" : "deb", "build_hash" : "595516e", "build_date" : "2018-08-17T23:18:47.308994Z", "build_snapshot" : false, "lucene_version" : "7.4.0", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" }

Поисковые запросы к Elasticsearch

Документы в Elasticsearch представлены в JSON-формате. Для добавления, изменения, удаления и совершения запросов используется JSON API.Данные в базе представлены в виде записей разных типов(type), разнесённых по разным индексам(index). Это что-то вроде документов в разных коллекциях в MongoDB или таблиц в SQL.

Для запросов к Elasticsearch мы будем использовать программу cURL. Если вам это кажется неудобным, рекомендуем скачать Insomnia REST клиент.

Добавим запись о пользователе с уникальным ID=1 в коллекцию accounts с типом user.

curl -X POST http://localhost:9200/accounts/person/1 -H 'Content-Type: application/json' -d @- << EOF { "username" : "n0str", "firstname" : "Alex", "lastname" : "Menshikov" } EOF

В адресе URL мы указали индекс, тип, а также ID новой записи. А в теле запроса мы передали содержимое записи в формате JSON.

Сервер ответит, что всё в порядке.

{"_index":"accounts","_type":"person","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

Получить информацию о записи можно по её ID.

curl http://localhost:9200/accounts/person/1

Удалить – с помощью метода DELETE.

curl http://localhost:9200/accounts/person/1 -X DELETE

Готовим набор данных

Elasticsearch представляет из себя базу данных, по которой будет производиться поиск. Вы можете перенести данные из своей базы (например, список статей или перечень описаний товаров интернет магазина). Мы в качестве данных используем готовый набор данных о пользователях сайта.

Записи из БД были выгружены в JSON-формат следующей структуры.

{ "account_number": INT, "balance": INT, "firstname": "String", "lastname": "String", "age": INT, "gender": "M or F", "address": "String", "employer": "String", "email": "String", "city": "String", "state": "String" }

Загрузим и распакуем тестовый набор данных.

wget https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip apt install unzip unzip accounts.zip

Elasticsearch поддерживает пакетную загрузку записей — так называемый bulk import.

curl -H 'Content-Type: application/x-ndjson' -X POST 'http://localhost:9200/accounts/person/_bulk?pretty' --data-binary @accounts.json

Проверить сколько индексов записано в базу можно с помощью запроса curl http://localhost:9200/_cat/indices?v 

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open accounts 6TerrElGTmW_E0BBZtMIYQ 5 1 1000 0 483kb 483kb

Если вы хотите загрузить собственный набор данных, вы можете сформировать JSON-файл, содержащий записи из базы данных, по аналогии с этим примером.

Формируем поисковые запросы

Теперь, когда индекс сформирован, можно делать поисковые запросы к базе данных. Начнем с запроса на выгрузку всех записей.

curl -X POST localhost:9200/accounts/_search -H 'Content-Type: application/json' -d @- << EOF { "query": { "match_all": {} } } EOF

В ответ по умолчанию мы получим 10 случайных результатов.

{ "took":411, "timed_out":false, "_shards":{ "total":5, "successful":5, "skipped":0, "failed":0 }, "hits":{ "total":1000, "max_score":1.0, "hits":[ { "_index":"accounts", "_type":"person", "_id":"25", "_score":1.0, "_source":{ "account_number":25, "balance":40540, "firstname":"Virginia", "lastname":"Ayala", "age":39, "gender":"F", "address":"171 Putnam Avenue", "employer":"Filodyne", "email":"virginiaayala@filodyne.com", "city":"Nicholson", "state":"PA" } }, ... ] } }

В поле hits можно увидеть общее число совпадений — 1000.

Попробуем выбрать всех пользователей, возраст которых младше 21 года. Для этого воспользуемся Range Query. Подставим в запрос параметр size, чтобы получить больше стандартных 10 результатов.

curl -X POST localhost:9200/accounts/_search?size=1000 -H 'Content-Type: application/json' -d @- << EOF { "query": { "range" : { "age" : { "lt" : 21 } } } } EOF

В ответ получим JSON, содержащий все 44 подходящих под запрос результата. 

Наконец, выведем обоих пользователей, у которых в адресе есть слово Hampton

curl -X POST localhost:9200/accounts/_search?size=1000 -H 'Content-Type: application/json' -d @- << EOF { "query": { "match" : { "address" : "Hampton" } } } EOF

Итоги

Мы установили Elasticsearch, научились выполнять к нему запросы на выбор, добавление и удаление записей. Мы импортировали тестовый набор данных и сформировали несколько выборок из него. В следующих статьях мы научимся делать более сложные запросы, настроим полнотекстовый поиск (как в Google) с поддержкой корректировки ошибок ввода и подсказками на основе вводимых данных.