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

Выдача и релевантность

Управляйте сортировкой, страницами и полями ответа. Boost меняет релевантность; оценка зависит от корпуса и не является вероятностью.

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

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

  • Строить детерминированную сортировку и пагинацию.
  • Возвращать только необходимые клиенту поля.
  • Понимать boost и сохранять поисковую оценку текста.

Совпадение и представление — разные этапы. Query определяет полный набор кандидатов; sort, from, size и _source определяют ответ клиенту. Даже правильный фильтр не даёт правильного решения, если нарушены страница, порядок или набор полей.

Оценки релевантности зависят от структуры запроса и статистики корпуса, в том числе частоты терминов и длины документов. Это не вероятности и не постоянные числа. При анализе релевантности сравнивайте относительный порядок документов. Явная сортировка по полю может заменить порядок по релевантности: добавляйте её, когда этого требует условие.

Страница — последний этап
  • Кандидаты query
  • Фильтры допуска
  • Score или сортировка полей
  • from + size
  • Проекция _source

Фильтрация предшествует пагинации, а проекция source меняет ответ, а не допуск документа.

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

01 / 05

Сортировка по цене

sort — упорядоченный список ключей. Elasticsearch сравнивает первый, а второй использует только при равенстве первого. Пример запрашивает самые дорогие товары с id по возрастанию для равных цен. Направления задаются как asc и desc.

Сортируйте по полям с подходящими индексированными значениями: price, rating, id, brand.keyword. Анализируемое name здесь не подходит как keyword-ключ. При явной сортировке не по оценке _score может быть null: вычисление релевантности не понадобилось. null не означает неудачный поиск.

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

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

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

Цены убывают. При равенстве максимальной цены первым будет меньший id. Второй ключ не пересортировывает весь результат, а разрешает равенство первого.

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

Вторая страница

from — смещение с нуля, а не номер страницы. Для страницы p длиной n используется from = (p − 1) × n и size = n. Пример запрашивает четвёртую страницу по три элемента: from равен 9. Фильтрация и сортировка происходят до применения смещения.

Используйте одну детерминированную сортировку на всех страницах. Иначе равные значения могут приводить к пересечениям или пропускам. Тренажёр ограничивает выдачу 100 документами и from + size значением 500: это небольшие учебные страницы, а не глубокая промышленная пагинация.

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

POST /products/_search
{
  "query": {
    "match_all": {}
  },
  "size": 3,
  "_source": [
    "name",
    "price"
  ],
  "sort": [
    {
      "id": "asc"
    }
  ],
  "from": 9
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 300,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "10",
        "_score": null,
        "_source": {
          "name": "Logitech Wireless Charger 10",
          "price": 2499
        }
      },
      {
        "_id": "11",
        "_score": null,
        "_source": {
          "name": "Dell Wireless Headphones 11",
          "price": 49
        }
      },
      {
        "_id": "12",
        "_score": null,
        "_source": {
          "name": "apple Premium Wireless Headphones 12",
          "price": 99
        }
      }
    ]
  }
}

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

ID равны 10, 11 и 12, тогда как total описывает все 300 совпадений. Длина страницы и размер отбора отвечают на разные вопросы.

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

Поля ответа

Фильтрация _source выбирает исходные JSON-свойства для каждого результата. Она не меняет запрос, оценку и доступные поля сортировки. Пример возвращает только name и category, но сортирует по id: ключ сортировки не обязан присутствовать в ответе.

_id и _score — метаданные результата вне _source. Сокращение исходных полей поэтому не удаляет идентификатор. Указывайте исходные пути полей: подполе mapping вроде brand.keyword не означает отдельное свойство _source.

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

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

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

В каждом source-объекте ровно name и category. У результата сохраняются _id и _score, а порядок по-прежнему определяется id.

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

Приоритет названия

В multi_match запись name^3 увеличивает вес name относительно description без boost. Пример ищет travel по обоим полям. Boost меняет вклад поля, но не даёт универсальной гарантии, что любое совпадение названия всегда выше любого совпадения описания при любых текстах и статистике корпуса.

Не умножайте отображённый _score вручную и не сравнивайте с жёстко заданной целью. Движок вычисляет оценку по запросу и индексу. Задание на релевантность проверяет отношения порядка и нужную конструкцию. В условии может требоваться конкретный boost, даже если другое значение кажется разумным.

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

POST /products/_search
{
  "query": {
    "multi_match": {
      "query": "travel",
      "fields": [
        "name^3",
        "description"
      ]
    }
  },
  "size": 2,
  "_source": [
    "name",
    "description"
  ]
}
Ответ Elasticsearch
Сокращённый ответ · JSON
{
  "hits": {
    "total": {
      "value": 178,
      "relation": "eq"
    },
    "hits": [
      {
        "_id": "3",
        "_score": 4.8997154,
        "_source": {
          "name": "Sony Travel Speaker 3",
          "description": "Precision equipment for professionals"
        }
      },
      {
        "_id": "13",
        "_score": 4.8997154,
        "_source": {
          "name": "Apple Travel Speaker 13",
          "description": "Premium wireless sound for travel"
        }
      }
    ]
  }
}

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

Числовые оценки относятся к одному запуску, а не являются постоянным эталоном. Посмотрите, в каком поле есть travel, затем сравните с запросом без boost, чтобы увидеть влияние на порядок.

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

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

Прикладной поиск часто сочетает 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

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

  • Ключи сортировки применяются по порядку; id разрешает равенство.
  • from = (страница − 1) × size; total не равен длине страницы.
  • _source меняет ответ, а не отбор или сортировку.
  • Boost меняет вклад поля; оценки зависят от корпуса.
  • Оставляйте текст с оценкой в must, а допуск — в filter.

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

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

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