Оглавление курса
Query DSL · 9 / 28

Структура запроса и контексты

Отделим отбор документов от оценки и научимся читать Query DSL как дерево.

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

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

  • Дерево условий
  • Два вопроса к документу
  • Постоянная оценка и кеш

Отделим отбор документов от оценки и научимся читать Query DSL как дерево.

Примеры course-products используют учебную схему и товар из главы 5 «Mapping и типы полей». Разборы упражнений ниже используют отдельный публичный набор тренажёра.

Логика главы
  • Дерево условий
  • Два вопроса к документу
  • Постоянная оценка и кеш

Отделим отбор документов от оценки и научимся читать Query DSL как дерево.

Курс ориентирован на Elasticsearch 9.4.3. Разборы упражнений используют публичный набор products; остальные запросы показывают отдельные учебные индексы и могут требовать подготовки или прав, недоступных в тренажёре. Ответы сокращены, score может меняться. Для расширенных функций проверяйте лицензию и вариант развёртывания.

01 / 07

Дерево условий

Leaf query проверяет конкретное условие: term, range или match. Compound query объединяет другие запросы или меняет их оценку: bool, dis_max, constant_score. JSON описывает дерево, а не последовательность инструкций. Порядок элементов filter не задаёт гарантированный порядок выполнения: движок оптимизирует план самостоятельно.

Самопроверка
02 / 07

Два вопроса к документу

Query context спрашивает, насколько хорошо документ подходит, и может вычислять _score. Filter context решает, проходит ли документ обязательное ограничение. Наличие, tenant_id и диапазон цены обычно относятся к ограничениям. Перенос match в filter не меняет его анализатор, но убирает его вклад в ранжирование. Это решение о смысле запроса, а не механическая оптимизация.

Самопроверка
03 / 07

Постоянная оценка и кеш

match_all задаёт все документы, match_none — ни одного. constant_score оборачивает фильтр и выдаёт одинаковый вклад подходящим документам. Filter context делает некоторые результаты пригодными для кеширования, но кеш зависит от частоты использования, сегментов и внутренних политик. Нельзя обещать, что любой filter будет сохранён или что второй запрос обязательно быстрее.

Все документы с точным брендом получают одинаковый вклад в оценку.

POST /course-products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "brand": "Sony"
        }
      }
    }
  }
}

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

Все документы с точным брендом получают одинаковый вклад в оценку.

Самопроверка
04 / 07

Разбор на примере каталога

Запрос «wireless» с ограничением price<100 содержит два разных решения. Совпадение name даёт основания сравнивать релевантность. Цена определяет допустимость покупки в бюджете, но товар за 20 не обязан быть более текстово релевантным, чем товар за 90. Поэтому match ставят в must, range — в filter. Если пользователь выбрал сортировку по цене, это уже отдельный контракт порядка. Перемещение всех условий в must может внести ненужные оценки и затруднить объяснение результатов.

Самопроверка
05 / 07

Все товары

Корень тела поиска — 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
06 / 07

Фильтр без scoring

bool.filter требует выполнения каждого ограничения без добавления вклада в релевантность. Точный бренд, наличие и цена — подходящие примеры: товар не должен становиться релевантнее только из-за наличия. Если поисковых условий нет, bool только с filter обычно возвращает нулевую оценку.

Контекст распространяется на всю вложенную конструкцию. Вложенный bool с двумя фильтрами означает сгруппированное И. Сохраняйте читаемость и не создавайте уровни без необходимости. Elasticsearch может учитывать фильтры для кеширования, но корректность не должна зависеть от попадания в кеш или конкретного порядка исполнения.

УсловиеОбязательно?Добавляет score в query context?
mustДаДа
filterДаНет
must_notНе должно совпастьНет
shouldЗависит от minimum_should_matchДа

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

POST /products/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "brand.keyword": "Sony"
          }
        },
        {
          "term": {
            "in_stock": false
          }
        },
        {
          "range": {
            "price": {
              "lt": 1200
            }
          }
        }
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "brand",
    "price",
    "in_stock"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 40,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "3",
        "_score": null,
        "_source": {
          "name": "Sony Travel Speaker 3",
          "brand": "Sony",
          "price": 100,
          "in_stock": false
        }
      },
      {
        "_id": "15",
        "_score": null,
        "_source": {
          "name": "Sony USB Cable 15",
          "brand": "Sony",
          "price": 499,
          "in_stock": false
        }
      }
    ]
  }
}

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

Товары выполняют все три ограничения. Сортировка по id стабилизирует просмотр; нулевой или null-score в таком ответе не означает ошибку.

Закрепить в задании 7
07 / 07

Релевантность и фильтры

Прикладной поиск часто сочетает match с оценкой в must и ограничения наличия и цены в filter. Все условия обязательны, но только текст влияет на релевантность. Пример ищет gaming среди товаров в наличии дешевле 800.

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

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

POST /products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "gaming"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "in_stock": true
          }
        },
        {
          "range": {
            "price": {
              "lt": 800
            }
          }
        }
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "price",
    "in_stock"
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 20,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "4",
        "_score": 2.3304372,
        "_source": {
          "name": "Logitech Gaming Keyboard 4",
          "price": 199,
          "in_stock": true
        }
      },
      {
        "_id": "14",
        "_score": 2.3304372,
        "_source": {
          "name": "Samsung Gaming Keyboard 14",
          "price": 199,
          "in_stock": true
        }
      }
    ]
  }
}

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

Найденные товары выполняют оба фильтра, а их взаимный порядок определяется текстовым поиском. total учитывает все допущенные совпадения, а не только небольшую страницу.

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

Самопроверка

Сначала ответьте самостоятельно, затем раскройте объяснение.

1. Что описывает JSON Query DSL?

Дерево условий и параметров поиска.

2. Исчезает ли анализ текста в filter?

Нет, match всё ещё анализирует текст, но не влияет на score.

3. Где поставить tenant_id?

В обязательное ограничение, обычно bool.filter, установленное сервером.

4. Всегда ли filter кешируется?

Нет, действует политика кеша и ограничения сегментов.

5. Когда полезен constant_score?

Когда достаточно одинакового вклада для всех прошедших условие документов.

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

  • Дерево условий и параметров поиска.
  • Нет, match всё ещё анализирует текст, но не влияет на score.
  • В обязательное ограничение, обычно bool.filter, установленное сервером.
  • Нет, действует политика кеша и ограничения сегментов.
  • Когда достаточно одинакового вклада для всех прошедших условие документов.

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

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

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