Перейти к основному содержимому

Краткое введение в полнотекстовый поиск с помощью ElasticSearch

· 6 мин. чтения

1. Обзор

Запросы полнотекстового поиска и лингвистический поиск по документам. Он включает одно или несколько слов или фраз и возвращает документы, соответствующие условиям поиска.

ElasticSearch — это поисковая система, основанная на Apache Lucene , бесплатной библиотеке программного обеспечения для поиска информации с открытым исходным кодом. Он предоставляет распределенную полнотекстовую поисковую систему с веб-интерфейсом HTTP и документами JSON без схемы.

В этой статье рассматривается REST API ElasticSearch и демонстрируются основные операции только с использованием HTTP-запросов.

2. Настройка

Чтобы установить ElasticSearch на свой компьютер, обратитесь к официальному руководству по установке .

RESTfull API работает на порту 9200. Давайте проверим, правильно ли он работает, используя следующую команду curl:

curl -XGET 'http://localhost:9200/'

Если вы наблюдаете следующий ответ, экземпляр работает правильно:

{
"name": "NaIlQWU",
"cluster_name": "elasticsearch",
"cluster_uuid": "enkBkWqqQrS0vp_NXmjQMQ",
"version": {
"number": "5.1.2",
"build_hash": "c8c4c16",
"build_date": "2017-01-11T20:18:39.146Z",
"build_snapshot": false,
"lucene_version": "6.3.0"
},
"tagline": "You Know, for Search"
}

3. Индексирование документов

ElasticSearch ориентирован на документы. Он хранит и индексирует документы. Индексация создает или обновляет документы. После индексирования вы можете искать, сортировать и фильтровать полные документы, а не строки данных в столбцах. Это принципиально иное представление о данных, и это одна из причин, по которой ElasticSearch может выполнять сложный полнотекстовый поиск.

Документы представлены в виде объектов JSON. Сериализация JSON поддерживается большинством языков программирования и стала стандартным форматом, используемым движением NoSQL. Он простой, лаконичный и легко читаемый.

Мы собираемся использовать следующие случайные записи для выполнения нашего полнотекстового поиска:

{
"title": "He went",
"random_text": "He went such dare good fact. The small own seven saved man age."
}

{
"title": "He oppose",
"random_text":
"He oppose at thrown desire of no. \
Announcing impression unaffected day his are unreserved indulgence."
}

{
"title": "Repulsive questions",
"random_text": "Repulsive questions contented him few extensive supported."
}

{
"title": "Old education",
"random_text": "Old education him departure any arranging one prevailed."
}

Прежде чем мы сможем проиндексировать документ, нам нужно решить, где его хранить. Можно иметь несколько индексов, которые, в свою очередь, содержат несколько типов. Эти типы содержат несколько документов, и каждый документ имеет несколько полей.

Мы собираемся хранить наши документы по следующей схеме:

текст : Имя индекса.

article : Имя типа.

id : идентификатор этого конкретного примера текстовой записи.

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

curl -XPUT 'localhost:9200/text/article/1?pretty'
-H 'Content-Type: application/json' -d '
{
"title": "He went",
"random_text":
"He went such dare good fact. The small own seven saved man age."
}'

Здесь мы используем id=1 , мы можем добавить другие записи, используя ту же команду и увеличенный идентификатор.

4. Получение документов

После того, как мы добавим все наши документы, мы можем проверить, сколько документов у нас есть в кластере, используя следующую команду:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}'

Кроме того, мы можем получить документ, используя его идентификатор, с помощью следующей команды:

curl -XGET 'localhost:9200/text/article/1?pretty'

И мы должны получить следующий ответ от эластичного поиска:

{
"_index": "text",
"_type": "article",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"title": "He went",
"random_text":
"He went such dare good fact. The small own seven saved man age."
}
}

Как мы видим, этот ответ соответствует записи, добавленной с использованием идентификатора 1.

5. Запрос документов

Хорошо, давайте выполним полнотекстовый поиск с помощью следующей команды:

curl -XGET 'localhost:9200/text/article/_search?pretty' 
-H 'Content-Type: application/json' -d '
{
"query": {
"match": {
"random_text": "him departure"
}
}
}'

И получаем следующий результат:

{
"took": 32,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1.4513469,
"hits": [
{
"_index": "text",
"_type": "article",
"_id": "4",
"_score": 1.4513469,
"_source": {
"title": "Old education",
"random_text": "Old education him departure any arranging one prevailed."
}
},
{
"_index": "text",
"_type": "article",
"_id": "3",
"_score": 0.28582606,
"_source": {
"title": "Repulsive questions",
"random_text": "Repulsive questions contented him few extensive supported."
}
}
]
}
}

Как мы видим, мы ищем «его уход» и получаем два результата с разными оценками. Первый результат очевиден, потому что внутри текста есть выполненный поиск, и, как мы видим, у нас есть оценка 1,4513469 .

Второй результат извлекается, потому что целевой документ содержит слово «он».

По умолчанию ElasticSearch сортирует совпадающие результаты по их показателю релевантности, то есть по тому, насколько хорошо каждый документ соответствует запросу. Обратите внимание, что оценка второго результата меньше по сравнению с первым попаданием, что указывает на более низкую релевантность.

6. Нечеткий поиск

Нечеткое сопоставление рассматривает два слова, которые «нечетко» похожи, как если бы они были одним и тем же словом. Во-первых, нам нужно определить, что мы подразумеваем под нечеткостью.

Elasticsearch поддерживает максимальное расстояние редактирования, указанное с помощью параметра fuzziness, равное 2. Для параметра fuzziness можно установить значение AUTO, что приводит к следующим максимальным расстояниям редактирования:

  • 0 для строк из одного или двух символов
  • 1 для строк из трех, четырех или пяти символов
  • 2 для строк более пяти символов

вы можете обнаружить, что расстояние редактирования, равное 2 , возвращает результаты, которые не кажутся связанными.

Вы можете получить лучшие результаты и более высокую производительность при максимальной нечеткости 1. Расстояние относится к расстоянию Левенштейна, которое представляет собой строковую метрику для измерения разницы между двумя последовательностями. Неформально расстояние Левенштейна между двумя словами — это минимальное количество односимвольных правок.

Хорошо, давайте выполним наш поиск с нечеткостью:

curl -XGET 'localhost:9200/text/article/_search?pretty' -H 'Content-Type: application/json' -d' 
{
"query":
{
"match":
{
"random_text":
{
"query": "him departure",
"fuzziness": "2"
}
}
}
}'

И вот результат:

{
"took": 88,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 1.5834423,
"hits": [
{
"_index": "text",
"_type": "article",
"_id": "4",
"_score": 1.4513469,
"_source": {
"title": "Old education",
"random_text": "Old education him departure any arranging one prevailed."
}
},
{
"_index": "text",
"_type": "article",
"_id": "2",
"_score": 0.41093433,
"_source": {
"title": "He oppose",
"random_text":
"He oppose at thrown desire of no.
\ Announcing impression unaffected day his are unreserved indulgence."
}
},
{
"_index": "text",
"_type": "article",
"_id": "3",
"_score": 0.2876821,
"_source": {
"title": "Repulsive questions",
"random_text": "Repulsive questions contented him few extensive supported."
}
},
{
"_index": "text",
"_type": "article",
"_id": "1",
"_score": 0.0,
"_source": {
"title": "He went",
"random_text": "He went such dare good fact. The small own seven saved man age."
}
}
]
}
}'

Как мы видим, нечеткость дает нам больше результатов.

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

7. Заключение

В этом кратком руководстве мы сосредоточились на индексировании документов и запросе полнотекстового поиска в Elasticsearch напрямую через его REST API .

У нас, конечно, есть API-интерфейсы, доступные для нескольких языков программирования, когда нам это нужно, но API по-прежнему довольно удобен и не зависит от языка.