Полнотекстовый поиск

Опубликовано admin в Ср, 03/02/2016 - 16:21

Задача поиска по тексту предполагает работу со сложными индексами и большими объемами данных. Поэтому для ее решения образовалась целая группа отдельных инструментов.

Многие базы данных имеют встроенную возможность поиска по тексту, однако всегда это очень ограниченная реализация. В большинстве случаев следует использовать более подходящие технологии.

Устройство полнотекстового индекса

Все технологии полнотекстового поиска работают по одному принципу. На основе текстовых данных строится индекс, который способен очень быстро искать соответствия по ключевым словам.

Обычно сервис поиска состоит из двух компонент. Поисковик и индексатор.

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

Поисковик — интерфейс поиска по индексу — принимает от клиента запрос, обрабатывает фразу и ищет ее в индексе.

Существует несколько популярных технологий для реализации полнотекстового поиска в приложениях.

1. Sphinx

Супер простое решение, которое подойдет для большинства случаев. По умолчанию поддерживает английский и русский язык. Имеет интерфейс для индексирования таблиц MySQL. Чтобы начать использовать Sphinx достаточно установить его из пакетов, настроить источник данных и запустить индексатор в cron задачу.

Конфигурация делится на source и index для определения источника данных и параметров индекса:

source product_product
{
type = mysql

sql_host = 127.0.0.1
sql_user = root
sql_pass = root
sql_db = shop

sql_query = SELECT id, title, description FROM products
}

index product
{
source = product
path = /var/data/product
charset_type = utf-8
}

# настройка индексации таблицы products прямо из базы MySQL

После этого достаточно запустить индексатор в cron, например для переиндексации каждые 5 минут:
*/5 * * * * root [ -x /usr/bin/indexer ] && /usr/bin/indexer --quiet --rotate --all

В таком режиме максимальная задержка до появления данных в поиске будет составлять 5 минут.

Sphinx поддерживает обычный MySQL протокол для поиска, поэтому чтобы найти в индексе какой-то текст достаточно подключиться к порту 9306 и отправить обычный MySQL запрос:

SELECT id FROM products WHERE MATCH("htc one x")
# В результате будут возвращены id найденных документов

Например, в PHP:
<?
$sp = new PDO('mysql:host=127.0.0.1;port=9306', 'root', '');
$list = $sp->query('SELECT id FROM products WHERE MATCH("htc one x")');
while ( $row = $list->fetch(PDO::FETCH_ASSOC) )
{
$product = get_product_by_id($row['id']); # достаем данные из MySQL
echo $product['title'] . '';
}

# Sphinx вернет ID, по которому можно получить данные продукта из MySQL

При больших объемах можно использовать схему Delta индексов для ускорения индексации. Кроме этого, Sphinx поддерживает Real Time индексы, фильтрацию и сортировку результатов поиска и поиск по wildcard условиям.

2. Solr

Solr — не просто поисковый индекс, а еще и хранилище документов. Т.е. в отличие от Sphinx'a, документы сохраняются целиком и их не нужно дублировать в базу данных.

Решение Java-based, поэтому понадобится JVM и сам Solr. Из пакетов можно поставить все вместе:
apt-get install solr-jetty

Либо просто скачать Solr и запустить его:
wget http://apache.cp.if.ua/lucene/solr/5.3.1/solr-5.3.1.tgz
tar -xvf solr-5.3.1.tgz
cd solr-5.3.1
bin/solr start

После этого сервис станет доступен на порту 8983:
http://127.0.0.1:8983/

Solr работает по текстовому HTTP протоколу. Сразу после установки можно отправлять данные в индекс. Индекс — это что-то вроде таблицы в MySQL, для ее создания нужно выполнить команду:

bin/solr create -c shop
# создаем индекс shop

Чтобы добавить документ в индекс, достаточно отправить такой запрос.

curl http://localhost:8983/solr/shop/update -d '
[
{"id" : "1",
"title_t" : "The Solr And Shit",
"author_t" : "Den Golotyuk"
}
]'
# Приставка _t нужна, чтобы значение стало доступно для полнотекстового поиска

Теперь можно сделать выборку документа по ID:

curl http://localhost:8983/solr/shop/get?id=1

Чтобы стала доступной возможность поиска по индексу, необходимо запустить перестроение индекса:
curl http://localhost:8983/solr/shop/update?commit=true

После этого можно искать по тексту:
curl http://localhost:8983/solr/demo/query -d 'q=author_t:Den'

Получим что-то типа этого:
{
"responseHeader":{
"status":0,
"QTime":13,
"params":{
"q":"author_t:Den"
}
},
"response":{
"numFound":1,
"start":0,
"docs":[
{
"id":"4",
"title_t":[
"Sphinx And Solr"
],
"author_t":[
"Den Golotyuk"
],
"_version_":1513752384077037568
}
]
}
}

Solr поддерживает масштабирование в кластер, поэтому это решение подойдет для очень больших объемов данных и нагрузок. Кроме обычного текстового поиска этот поисковик может находить неточные соответствия (например, при поиске слов с ошибками).

3. Elastic

Elasticsearch — целая инфраструктура для работы с данными, в том числе полнотекстовым поиском. Построен на основе Apache Lucene.

Установка из кастомного репозитория Debian:

wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb http://packages.elastic.co/elasticsearch/1.4/debian stable main" | sudo tee -a /etc/apt/sources.list
apt-get update && apt-get install elasticsearch
update-rc.d elasticsearch defaults 95 10
/etc/init.d/elasticsearch restart

После запуска (может занять несколько секунд) нужно проверить доступность:

curl localhost:9200
{
"status":200,
"name":"Hermod",
"cluster_name":"elasticsearch",
"version":{
"number":"1.6.2",
"build_hash":"622039121e53e5f520b5ff8720fdbd3d0cb5326b",
"build_timestamp":"2015-07-29T09:24:47Z",
"build_snapshot":false,
"lucene_version":"4.10.4"
},
"tagline":"You Know, for Search"
}

Индексы (таблицы) создаются автоматически при индексации, а сам индексатор работает в режиме реального времени. Поэтому для добавления документа в индекс нужно сделать только один вызов:

curl -XPUT "http://localhost:9200/shop/products/1" -d'
{
"title": "Elastic",
"description": "Better than Solr"
}'
# сохраняем продукт в индекс shop/products с id = 1

{"_index":"shop","_type":"products","_id":"1","_version":1,"created":true}

Чтобы получить документ по id достаточно сделать такой вызов:
curl -XGET "http://localhost:9200/shop/products/1"

Для поиска документов по тексту:
curl -XPOST "http://localhost:9200/shop/products/_search" -d'
{
"query": {
"query_string": {
"query": "Better"
}
}
}'

{
"took":1395,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"failed":0
},
"hits":{
"total":1,
"max_score":0.15342641,
"hits":[
{
"_index":"shop",
"_type":"products",
"_id":"1",
"_score":0.15342641,
"_source":{
"title":"Elastic",
"description":"Better than Solr"
}
}
]
}
}

Elastic имеет мега продвинутую систему хранения данных и протокол запросов. Поэтому во многих случаях его применяют, как движок для Ad-hoc запросов.

Самое важное
Для поиска по тексту следует использовать указанные инструменты, т.к. обычные базы данных весьма ограничены и неэффективны в этом вопросе. Короткая сводка поможет выбрать подходящее решение:

  • Sphinx. Простой, быстрый, легкий, используется в связке с базовый данных, поиск по русскому/английскому тексту, wildcard поиск.
  • Solr. Большой, мощный, выступает как хранилище, миллион функций, сделать можно практически все, есть неточный поиск и возможность масштабироваться из коробке.
  • Elastic. Не только поиск и хранилище, а и другие инструменты (визуализация, сборщик логов, система шифрования и т.п.). Умеет масштабироваться и позволяет выполнять выборки очень сложной формы, что делает это хорошим вариантов для аналитической платформы.
( categories: )