Когда документ сохранён, он индексируется и становится доступным для поиска практически в реальном времени - в течение 1 секунды. Lucene использует структуру данных, называемую инвертированным индексом (inverted index), которая поддерживает очень быстрый полнотекстовый поиск. Инвертированный индекс перечисляет каждое уникальное слово, которое встречается в любом документе, и идентифицирует все документы, в которых встречается каждое слово. Немного про инвертированный индекс на wikipedia.

Немного сложно и непонятно? Рассмотрим на примере, где у нас есть следующие документы:

  1. Сегодня очень холодно
  2. Вчера было тепло
  3. Прошлый месяц было тепло

Elasticsearch разобьёт все предложения на слова и сопоставит их с id документа, а если точнее, то Lucene. В итоге в инвертированном индексе это будет выглядеть так:

Слово Id документа
сегодня 1
очень 1
вчера 2
было 2,3
тепло 2,3
прошлый 3
месяц 3

.

Благодаря такому индексу становиться очень просто найти все документы со словом тепло. Слово тепло содержится в документах 2 и 3. И elasticsearch уже об этом знает и соответственно вернет документы быстрее, чем это могло быть.

Инвертированный индекс отлично подходит для поиска, но имеет некоторые ограничения. Например, если вы попытаетесь отсортировать результаты запроса по определённому полю, вы можете получить ошибку, где вам будет предложено использовать keyword.

Использование keyword

Инвертированный индекс действительно эффективен для поиска, но не для сортировки и агрегирования. Для выполнения этих действий рекомендуется использовать keyword, поскольку поле keyword имеет значения документа.

Значения документа (Doc values) - это еще один тип структуры данных, который используется в Elasticsearch для хранения данных. Значения документа имеют столбчатую структуру, которая лучше подходит для агрегирования, сортировки.

Для примера добавим несколько значений в индекс и попробуем отсортировать.

PUT my_index/_doc/2
{
  "field1" : "Вешалка"
}
PUT my_index/_doc/3
{
  "field1" : "Кухонный комбайн"
}
PUT my_index/_doc/4
{
  "field1" : "Стул"
}

GET my_index/_search 
{
  "query": {
    "match": {
      "field1": "united"
    }
  },
  "sort": [
    {
      "field1": {
        "order": "desc"
      }
    }
  ]
}

В итоге получим ошибку "Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [field1] in order to load field data by uninverting the inverted index. Note that this can use significant memory.".

Используем keyword:

GET my_index/_search 
{
  "query": {
    "match": {
      "field1": "united"
    }
  },
  "sort": [
    {
      "field1.keyword": {
        "order": "desc"
      }
    }
  ]
}

Оптимизация

Исходя из этой статьи мы уже знаем, что по умолчанию Elasticsearch отображает каждую строку как текст и ключевое слово. Соответственно у нас хранятся инвертированный индекс, полный токенов (слов) для text и значения документа (Doc values).

Получается большой объём данных с учетом того, что возможно вам это и не нужно. Для решения этой проблемы можно использовать:

  • Не индексировать поле, т.е не создавать инвертированный индекс. Но в таком случае вы не сможете делать тестовые запросы с этим полем. Например, если поле нужно только для сортировки или агрегаций. Для этого выставляем index в false
    PUT my_index/_mapping
    {
      "properties": {
     "filed1": {
       "type": "keyword",
       "index": "false"
     }
      }
    }
    
  • Не создавать значения документа (Doc values). Поле будет доступно для поиска, но недоступно для агрегации и сортировок. Для этого выставляем doc_values в false
    PUT my_index/_mapping
    {
      "properties": {
    "filed1": {
      "type": "keyword",
      "doc_values": "false"
    }
      }
    }
    
  • Не создавать ни инвертированный индекс, ни значения документа. Т.е поле не будет доступно ни для поиска, ни для сортировки, ни для агрегации. Но оно будет выводится в _score при поиске документов.

В итоге

  • Lucene создает несколько структур данных из ваших документов: инвертированные индексы и значения документов.
  • Для ускорения поиска используется инвертированный индекс
  • Значения документов позволяют агрегировать и сортировать значения
  • Инвертированный индекс доступен только для полнотекстовых операций (поиск)
  • Присутствует возможность отключить инвертированный индекс или значения документа для полей