Быстрое введение в полнотекстовый поиск с ElasticSearch

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

1. обзор

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

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

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

2. Настроить

Чтобы установить ElasticSearch на свой компьютер, обратитесь кofficial setup guide.

RESTful 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."
}

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

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

text: имя индекса. article: имя типа. id: 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."
        }
      }
    ]
  }
}

Как мы видим, мы ищем“him departure” и получаем два результата с разными оценками. Первый результат очевиден, потому что в тексте есть выполненный поиск, и, как мы видим, у нас есть оценка1.4513469.

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

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

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

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

  • 0 для строк из одного или двух символов

  • 1 для строк из трех, четырех или пяти символов

  • 2 для строк из более чем пяти символов

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

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

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

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. Заключение

В этом кратком руководстве мы сосредоточились наindexing documents and querying Elasticsearch for full-text search, directly via it’s REST API.

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