Оглавление курса
Теоретический курс · 1 / 6

Точные совпадения

Ищите документы по точным значениям, диапазонам и наличию полей. Поля keyword сохраняют регистр, а text проходят анализ.

Разделов: 5Elasticsearch 9.4.3Ещё не прочитан

Чему вы научитесь

  • Читать поисковый запрос, mapping и важные части ответа.
  • Выбирать точные запросы для keyword, чисел и логических значений.
  • Учитывать границы диапазонов и отсутствие индексированных значений.

Elasticsearch — поисковый движок. Индекс объединяет документы с идентификаторами и типизированными полями. Учебный products содержит 300 фиксированных товаров. Mapping определяет способ индексирования: число допускает числовые сравнения, а text анализируется в термины. Исходный JSON хранится в _source. Наличие поля в mapping не означает, что оно заполнено в каждом документе.

Каждый пример ниже — тело POST /products/_search. В тренажёр вставляйте только JSON: физический индекс выбирает сервер, он же включает точный подсчёт совпадений. «Выполнить запрос» помогает изучить данные; «Проверить решение» проверяет результат и изучаемую конструкцию. Значения примеров специально отличаются от заданий. Сначала найдите документы, затем объясните, почему конкретный товар попал в ответ.

От товара к результату
  • Документ: JSON
  • Mapping: типы полей
  • Query: условия
  • hits: найденные документы

Mapping определяет индексированные значения. Query отбирает документы, hits возвращает страницу совпадений.

Примеры выполнены на публичном наборе products в Elasticsearch 9.4.3. Ответы — сокращённые фрагменты JSON: время, сведения о шардах и индексе опущены. Числовые оценки иллюстративны и могут меняться. Примеры объясняют способ решения; значения в заданиях могут отличаться.

01 / 05

Все товары

Корень тела поиска — JSON-объект. query задаёт условия совпадения. match_all с пустым объектом не ограничивает документы и удобен для знакомства с набором данных. size управляет числом возвращаемых документов, а не числом совпадений; sort делает небольшую демонстрацию воспроизводимой. В JSON нужны двойные кавычки, недопустимы комментарии и завершающая запятая.

hits.total.value показывает число совпавших документов; relation: eq означает точный подсчёт. hits.hits содержит возвращённую страницу. _id идентифицирует документ, _source содержит запрошенные исходные поля, _score — оценку релевантности. took измеряется в миллисекундах и меняется между запусками. Фрагменты ниже опускают took, сведения о шардах и индексе: это сокращение для чтения, а не отдельный формат ответа Elasticsearch.

ПолеТипНазначение
idintegerСтабильный ID товара и дополнительный ключ сортировки
name / descriptiontextАнализируемый полнотекстовый поиск
brand / brand.keywordtext / keywordПоиск по тексту / точное значение бренда
category / statuskeywordТочные значения с учётом регистра
price / ratingdouble / floatЧисла; rating может отсутствовать
in_stock / created_atboolean / dateНаличие / дата создания

Разберём другой сценарий

POST /products/_search
{
  "query": {
    "match_all": {}
  },
  "size": 2,
  "_source": [
    "name",
    "price"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 300,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "1",
        "_score": null,
        "_source": {
          "name": "Wireless",
          "price": 100
        }
      },
      {
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "Reference device",
          "price": 100
        }
      }
    ]
  }
}

Как читать результат

Возвращены два документа, но total равен 300. Порядок id задаёт sort, а не релевантность. _source ограничен name и price для читаемости. Управление выдачей подробно рассматривается в четвёртом уроке.

Закрепить в задании 1
02 / 05

Точный бренд

term ищет индексированное значение без анализа входной строки. Для идентификатора бренда выбирайте brand.keyword: он хранит полное значение с учётом регистра. Sony и sony здесь различаются. Само поле brand имеет тип text, поэтому его индексированные токены не равны исходной строке бренда.

Та же конструкция term работает с логическими и числовыми полями: передавайте true, а не строку "true", число, а не отформатированную цену. В запросе используются индексированные пути. В _source лежат исходные поля: brand.keyword — подполе mapping, а не отдельное свойство исходного JSON.

Разберём другой сценарий

POST /products/_search
{
  "query": {
    "term": {
      "brand.keyword": "Sony"
    }
  },
  "size": 2,
  "_source": [
    "name",
    "brand"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 50,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "3",
        "_score": null,
        "_source": {
          "name": "Sony Travel Speaker 3",
          "brand": "Sony"
        }
      },
      {
        "_id": "9",
        "_score": null,
        "_source": {
          "name": "Sony Premium Travel Laptop 9",
          "brand": "Sony"
        }
      }
    ]
  }
}

Как читать результат

У каждого найденного товара brand равен Sony. В _source возвращается brand, хотя условие обращается к brand.keyword. Небольшой size не ограничивает число совпадений в total.

Закрепить в задании 2
03 / 05

Несколько статусов

terms проверяет принадлежность списку: документ проходит, если поле равно хотя бы одному значению. Это логическое ИЛИ для точных значений одного поля, а не требование содержать все значения. status здесь — одно keyword-значение. Альтернативы передаются массивом.

Для компактного отбора точных значений используйте terms; когда альтернативы затрагивают разные поля или составные условия, понадобится bool. Список значений не создаёт самостоятельные блоки запроса. Отсутствующее поле не совпадает с обычным значением списка: товар без status не включается автоматически.

Разберём другой сценарий

POST /products/_search
{
  "query": {
    "terms": {
      "status": [
        "preorder",
        "discontinued"
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "status"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 200,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "1",
        "_score": null,
        "_source": {
          "name": "Wireless",
          "status": "preorder"
        }
      },
      {
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "Reference device",
          "status": "discontinued"
        }
      }
    ]
  }
}

Как читать результат

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

Закрепить в задании 3
04 / 05

Диапазон цен

range сравнивает индексированные значения, а не строки. gte и lte включают границу, gt и lt исключают её. Обе границы интервала одного поля задаются в одном объекте range. Пример выбирает цены от 200 до 800 с включением обеих границ.

Перед записью JSON переведите условие: «дешевле» означает lt, «не меньше» — gte. Числовые типы учитывают дробные значения, поэтому 499.99 и 500 могут оказаться по разные стороны границы. Эта же группа запросов поддерживает даты, но в price передавайте числа без символа валюты.

СравнениеГраница включена?
gt / ltНет
gte / lteДа

Разберём другой сценарий

POST /products/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 200,
        "lte": 800
      }
    }
  },
  "size": 2,
  "_source": [
    "name",
    "price"
  ],
  "sort": [
    {
      "price": "asc"
    },
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 60,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "5",
        "_score": null,
        "_source": {
          "name": "Dell USB Cable 5",
          "price": 499
        }
      },
      {
        "_id": "15",
        "_score": null,
        "_source": {
          "name": "Sony USB Cable 15",
          "price": 499
        }
      }
    ]
  }
}

Как читать результат

Страница начинается с самого дешёвого подходящего товара; total считает весь интервал. Товар с ценой ровно 800 включается, в отличие от условия lt: 800.

Закрепить в задании 4
05 / 05

Товары с рейтингом

exists проверяет наличие индексированного значения. При нашем mapping отсутствующий rating и rating: null не проходят эту проверку. Числовой ноль является значением и не должен считаться отсутствием. Пустая текстовая строка также не равна отсутствующему полю.

Наличие свойства в _source не всегда гарантирует индексированное значение: на это влияют настройки индексирования. В упражнениях mapping фиксирован и не заменяет null через null_value. Чтобы найти документы без рейтинга, инвертируйте exists через bool.must_not; эта комбинация понадобится в продвинутом уроке.

Разберём другой сценарий

POST /products/_search
{
  "query": {
    "exists": {
      "field": "rating"
    }
  },
  "size": 2,
  "_source": [
    "name",
    "rating"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 258,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "1",
        "_score": null,
        "_source": {
          "name": "Wireless",
          "rating": 3.1
        }
      },
      {
        "_id": "2",
        "_score": null,
        "_source": {
          "name": "Reference device",
          "rating": 3.2
        }
      }
    ]
  }
}

Как читать результат

Каждый найденный товар имеет числовой рейтинг. total меньше размера индекса: у части товаров индексированного рейтинга нет.

Закрепить в задании 5

Краткая памятка

  • query отбирает документы; size ограничивает возвращаемую страницу.
  • Для полных точных строк используйте keyword и сохраняйте регистр.
  • term — одно значение; terms — любое значение массива.
  • Выбирайте gt/gte/lt/lte по формулировке каждой границы.
  • exists проверяет индексированное значение; ноль не означает отсутствие.

Официальная документация

Готовы применить знания?

Подтвердите прочтение, чтобы сохранить своё место. Чтение и решение заданий учитываются отдельно в этом браузере с помощью гостевой cookie.