Запрос с функцией оценки
Запрос с функцией оценки позволяет изменять оценки документов, которые извлекаются по запросу. Это может быть полезно, если, например, функция оценки вычислительно затратна, а достаточно вычислить оценку на отфильтрованном наборе документов.
Для использования запроса с функцией оценки необходимо определить запрос и одну или несколько функций, которые вычисляют новую оценку для каждого документа, возвращённого запросом.
Запрос с функцией оценки может использоваться с одной функцией, например, так:
resp = client.search(
query={
"function_score": {
"query": {
"match_all": {}
},
"boost": "5",
"random_score": {},
"boost_mode": "multiply"
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
query: {
match_all: {}
},
boost: '5',
random_score: {},
boost_mode: 'multiply'
}
}
}
)
puts response res, err := es.Search(
es.Search.WithBody(strings.NewReader(`{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"boost": "5",
"random_score": {},
"boost_mode": "multiply"
}
}
}`)),
es.Search.WithPretty(),
)
fmt.Println(res, err) const response = await client.search({
query: {
function_score: {
query: {
match_all: {},
},
boost: "5",
random_score: {},
boost_mode: "multiply",
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"query": { "match_all": {} },
"boost": "5",
"random_score": {},
"boost_mode": "multiply"
}
}
} | См. Функции оценки для списка поддерживаемых функций. |
Кроме того, можно комбинировать несколько функций. В этом случае можно выбрать, применять ли функцию только в том случае, если документ соответствует заданному фильтрующему запросу.
resp = client.search(
query={
"function_score": {
"query": {
"match_all": {}
},
"boost": "5",
"functions": [
{
"filter": {
"match": {
"test": "bar"
}
},
"random_score": {},
"weight": 23
},
{
"filter": {
"match": {
"test": "cat"
}
},
"weight": 42
}
],
"max_boost": 42,
"score_mode": "max",
"boost_mode": "multiply",
"min_score": 42
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
query: {
match_all: {}
},
boost: '5',
functions: [
{
filter: {
match: {
test: 'bar'
}
},
random_score: {},
weight: 23
},
{
filter: {
match: {
test: 'cat'
}
},
weight: 42
}
],
max_boost: 42,
score_mode: 'max',
boost_mode: 'multiply',
min_score: 42
}
}
}
)
puts response res, err := es.Search(
es.Search.WithBody(strings.NewReader(`{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"boost": "5",
"functions": [
{
"filter": {
"match": {
"test": "bar"
}
},
"random_score": {},
"weight": 23
},
{
"filter": {
"match": {
"test": "cat"
}
},
"weight": 42
}
],
"max_boost": 42,
"score_mode": "max",
"boost_mode": "multiply",
"min_score": 42
}
}
}`)),
es.Search.WithPretty(),
)
fmt.Println(res, err) const response = await client.search({
query: {
function_score: {
query: {
match_all: {},
},
boost: "5",
functions: [
{
filter: {
match: {
test: "bar",
},
},
random_score: {},
weight: 23,
},
{
filter: {
match: {
test: "cat",
},
},
weight: 42,
},
],
max_boost: 42,
score_mode: "max",
boost_mode: "multiply",
min_score: 42,
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"query": { "match_all": {} },
"boost": "5",
"functions": [
{
"filter": { "match": { "test": "bar" } },
"random_score": {},
"weight": 23
},
{
"filter": { "match": { "test": "cat" } },
"weight": 42
}
],
"max_boost": 42,
"score_mode": "max",
"boost_mode": "multiply",
"min_score": 42
}
}
} | Усиление для всего запроса. | |
| См. Функции оценки для списка поддерживаемых функций. |
Оценки, полученные по фильтрующему запросу каждой функции, значения не имеют.
Если для функции не указан фильтр, это эквивалентно заданию "match_all": {}.
Сначала каждый документ оценивается с помощью определённых функций. Параметр score_mode определяет способ объединения вычисленных оценок:
| | оценки умножаются (по умолчанию) |
| | оценки суммируются |
| | оценки усредняются |
| | применяется первая функция, имеющая соответствующий фильтр |
| | используется максимальная оценка |
| | используется минимальная оценка |
Поскольку оценки могут быть на разных шкалах (например, от 0 до 1 для функций затухания, но произвольные для field_value_factor), а также иногда требуется разный вклад функций в оценку, оценку каждой функции можно настроить с помощью пользовательского weight. weight может быть определён для каждой функции в массиве functions (пример выше) и умножается на оценку, вычисленную соответствующей функцией. Если вес задан без объявления других функций, weight действует как функция, которая просто возвращает weight.
В случае, если score_mode установлено в avg, отдельные оценки будут объединены взвешенным средним. Например, если две функции возвращают оценки 1 и 2, а их соответствующие веса равны 3 и 4, то их оценки будут объединены как (1*3+2*4)/(3+4), а не как (1*3+2*4)/2.
Новую оценку можно ограничить определённым пределом, установив параметр max_boost. По умолчанию для max_boost установлено значение FLT_MAX.
Нова вычисленная оценка объединяется с оценкой запроса. Параметр boost_mode определяет способ объединения:
| | оценка запроса и оценка функции умножаются (по умолчанию) |
| | используется только оценка функции, оценка запроса игнорируется |
| | оценка запроса и оценка функции складываются |
| | среднее |
| | максимум из оценки запроса и оценки функции |
| | минимум из оценки запроса и оценки функции |
По умолчанию изменение оценки не изменяет набор документов, которые соответствуют запросу. Для исключения документов, которые не соответствуют определённому порогу оценки, можно установить параметр min_score на желаемый порог оценки.
Для того, чтобы min_score сработал, все документы, возвращённые запросом, должны быть оценены, а затем отфильтрованы по одному.
Запрос с функцией оценки предоставляет несколько типов функций оценки.
-
script_score -
weight -
random_score -
field_value_factor - функции затухания:
gauss,linear,exp
Скрипт-оценка
Функция скрипт-оценки позволяет обернуть другой запрос и настроить оценку с помощью вычислений, полученных из других числовых значений полей документа с помощью выражения скрипта. Вот простой пример:
resp = client.search(
query={
"function_score": {
"query": {
"match": {
"message": "elasticsearch"
}
},
"script_score": {
"script": {
"source": "Math.log(2 + doc['my-int'].value)"
}
}
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
query: {
match: {
message: 'elasticsearch'
}
},
script_score: {
script: {
source: "Math.log(2 + doc['my-int'].value)"
}
}
}
}
}
)
puts response const response = await client.search({
query: {
function_score: {
query: {
match: {
message: "elasticsearch",
},
},
script_score: {
script: {
source: "Math.log(2 + doc['my-int'].value)",
},
},
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"query": {
"match": { "message": "elasticsearch" }
},
"script_score": {
"script": {
"source": "Math.log(2 + doc['my-int'].value)"
}
}
}
}
} Во всех документах Elasticsearch все оценки — положительные 32-битные числа с плавающей запятой.
Если функция скрипт-оценки генерирует оценку с большей точностью, она преобразуется в ближайшее 32-битное число с плавающей запятой.
Аналогично, оценки должны быть неотрицательными. В противном случае Elasticsearch вернёт ошибку.
Помимо различных значений и выражений полей скрипта, параметр скрипта _score может быть использован для получения оценки на основе обернутого запроса.
Компиляция скриптов кэшируется для более быстрого выполнения. Если скрипт должен принимать параметры, предпочтительнее повторно использовать один и тот же скрипт и предоставить ему параметры:
resp = client.search(
query={
"function_score": {
"query": {
"match": {
"message": "elasticsearch"
}
},
"script_score": {
"script": {
"params": {
"a": 5,
"b": 1.2
},
"source": "params.a / Math.pow(params.b, doc['my-int'].value)"
}
}
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
query: {
match: {
message: 'elasticsearch'
}
},
script_score: {
script: {
params: {
a: 5,
b: 1.2
},
source: "params.a / Math.pow(params.b, doc['my-int'].value)"
}
}
}
}
}
)
puts response const response = await client.search({
query: {
function_score: {
query: {
match: {
message: "elasticsearch",
},
},
script_score: {
script: {
params: {
a: 5,
b: 1.2,
},
source: "params.a / Math.pow(params.b, doc['my-int'].value)",
},
},
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"query": {
"match": { "message": "elasticsearch" }
},
"script_score": {
"script": {
"params": {
"a": 5,
"b": 1.2
},
"source": "params.a / Math.pow(params.b, doc['my-int'].value)"
}
}
}
}
} Обратите внимание, что в отличие от запроса custom_score, оценка запроса умножается на результат оценки скрипта. Если вы хотите этого избежать, установите "boost_mode": "replace".
Вес
Оценка по весу позволяет умножить оценку на предоставленный weight. Иногда это желательно, так как значения усиления, заданные для определённых запросов, нормализуются, в то время как для этой функции оценки это не так. Числовое значение имеет тип float.
"weight" : number
Случайное число
Функция random_score генерирует случайные числа, равномерно распределенные от 0 до 1 (не включая 1). По умолчанию, она использует внутренние идентификаторы документов Lucene в качестве источника случайности, что очень эффективно, но, к сожалению, не воспроизводимо, так как документы могут быть переименованы при слиянии.
Если вам нужно, чтобы результаты были воспроизводимыми, можно предоставить seed и field. Конечное значение затем будет рассчитано на основе этого значения, минимального значения field для рассматриваемого документа и соли, рассчитанной на основе имени индекса и идентификатора фрагмента, чтобы документы с одинаковым значением, но хранящиеся в разных индексах, получали разные оценки. Обратите внимание, что документы в одном фрагменте и с одинаковым значением для field будут, тем не менее, получать одинаковые оценки, поэтому обычно желательно использовать поле с уникальными значениями для всех документов. Хорошим вариантом по умолчанию может быть использование поля _seq_no, единственным недостатком которого является то, что оценки изменятся, если документ будет обновлён, так как операции обновления также обновляют значение поля _seq_no.
Было возможно установить seed без установки поля, но это устарело, так как это требует загрузки fielddata для поля _id, что потребляет много памяти.
resp = client.search(
query={
"function_score": {
"random_score": {
"seed": 10,
"field": "_seq_no"
}
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
random_score: {
seed: 10,
field: '_seq_no'
}
}
}
}
)
puts response res, err := es.Search(
es.Search.WithBody(strings.NewReader(`{
"query": {
"function_score": {
"random_score": {
"seed": 10,
"field": "_seq_no"
}
}
}
}`)),
es.Search.WithPretty(),
)
fmt.Println(res, err) const response = await client.search({
query: {
function_score: {
random_score: {
seed: 10,
field: "_seq_no",
},
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"random_score": {
"seed": 10,
"field": "_seq_no"
}
}
}
} Фактор значения поля
Функция field_value_factor позволяет использовать поле из документа для влияния на оценку. Она похожа на использование функции script_score, но избегает накладных расходов на скрипты. Если используется для многозначного поля, в расчётах используется только первое значение поля.
Например, представьте, что у вас есть документ, индексированный с числовым полем my-int, и вы хотите повлиять на оценку документа с помощью этого поля. Пример реализации:
resp = client.search(
query={
"function_score": {
"field_value_factor": {
"field": "my-int",
"factor": 1.2,
"modifier": "sqrt",
"missing": 1
}
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
field_value_factor: {
field: 'my-int',
factor: 1.2,
modifier: 'sqrt',
missing: 1
}
}
}
}
)
puts response const response = await client.search({
query: {
function_score: {
field_value_factor: {
field: "my-int",
factor: 1.2,
modifier: "sqrt",
missing: 1,
},
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"field_value_factor": {
"field": "my-int",
"factor": 1.2,
"modifier": "sqrt",
"missing": 1
}
}
}
} Что переведётся в следующую формулу для оценки:
sqrt(1.2 * doc['my-int'].value)
Существует ряд опций для функции field_value_factor:
| | Поле, которое необходимо извлечь из документа. |
| | Необязательный множитель, умножающий значение поля, по умолчанию |
| | Модификатор, применяемый к значению поля, может быть одним из: |
| Модификатор | Значение |
|---|---|
| Не применять никакого множителя к значению поля |
| Вычисление десятичного логарифма значения поля. Поскольку эта функция вернёт отрицательное значение и вызовет ошибку при использовании со значениями от 0 до 1, рекомендуется использовать |
| Добавить 1 к значению поля и вычислить десятичный логарифм. |
| Добавить 2 к значению поля и вычислить десятичный логарифм. |
| Вычисление натурального логарифма значения поля. Поскольку эта функция вернёт отрицательное значение и вызовет ошибку при использовании со значениями от 0 до 1, рекомендуется использовать |
| Добавить 1 к значению поля и вычислить натуральный логарифм. |
| Добавить 2 к значению поля и вычислить натуральный логарифм. |
| Возвести значение поля в квадрат (умножить его на себя). |
| Вычислить квадратный корень из значения поля. |
| Найти обратную величину значения поля. Это то же, что и |
-
missing - Значение, используемое, если в документе нет этого поля. Модификатор и множитель всё равно применяются к нему так, как будто оно было прочитано из документа.
Значения, полученные с помощью функции field_value_score, должны быть неотрицательными, в противном случае будет выброшено исключение. Модификаторы log и ln дадут отрицательные значения, если использовать их для значений от 0 до 1. Убедитесь, что вы ограничили значения поля с помощью фильтра диапазона, или используйте log1p и ln1p.
Помните, что вычисление логарифма (log()) от 0 или квадратного корня из отрицательного числа является незаконной операцией, и будет выброшено исключение. Убедитесь, что вы ограничили значения поля с помощью фильтра диапазона, или используйте log1p и ln1p.
Функции затухания
Функции затухания оценивают документ с помощью функции, которая затухает в зависимости от расстояния числового значения поля документа от заданного пользователем начала отсчета. Это похоже на запрос по диапазону, но с плавными краями вместо прямоугольных.
Чтобы использовать оценку по расстоянию для запроса, содержащего числовые поля, пользователь должен определить origin и scale для каждого поля. origin необходимо для определения «центральной точки», от которой вычисляется расстояние, а scale — для определения скорости затухания. Функция затухания задается как
"DECAY_FUNCTION": {
"FIELD_NAME": {
"origin": "11, 12",
"scale": "2km",
"offset": "0km",
"decay": 0.33
}
} |
| |
| Указанное поле должно быть числовым, датированным или геоточковым полем. |
В приведенном выше примере поле является geo_point, а начало отсчета может быть задано в формате геокоординат. scale и offset в этом случае должны быть указаны с единицей измерения. Если ваше поле — поле даты, вы можете установить scale и offset как дни, недели и так далее. Пример:
resp = client.search(
query={
"function_score": {
"gauss": {
"@timestamp": {
"origin": "2013-09-17",
"scale": "10d",
"offset": "5d",
"decay": 0.5
}
}
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
gauss: {
"@timestamp": {
origin: '2013-09-17',
scale: '10d',
offset: '5d',
decay: 0.5
}
}
}
}
}
)
puts response const response = await client.search({
query: {
function_score: {
gauss: {
"@timestamp": {
origin: "2013-09-17",
scale: "10d",
offset: "5d",
decay: 0.5,
},
},
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"gauss": {
"@timestamp": {
"origin": "2013-09-17",
"scale": "10d",
"offset": "5d",
"decay": 0.5
}
}
}
}
} | Формат даты начала отсчета зависит от | |
| Параметры |
| | Точка начала отсчета, используемая для вычисления расстояния. Должна быть задана как число для числовых полей, дата для полей даты и геокоординаты для геокоординатных полей. Требуется для гео- и числовых полей. Для полей дат по умолчанию используется |
| | Требуется для всех типов. Определяет расстояние от начала отсчета + смещение, при котором вычисленное значение будет равно параметру |
| | Если определен |
| | Параметр |
В первом примере ваши документы могут представлять отели и содержать поле с геолокацией. Вы хотите вычислить функцию затухания в зависимости от того, как далеко находится отель от заданного местоположения. Вы можете не сразу увидеть, какую шкалу выбрать для функции Гаусса, но вы можете сказать что-то вроде: «На расстоянии 2 км от желаемого местоположения оценка должна быть уменьшена до одной трети». Параметр «масштаб» затем будет автоматически скорректирован, чтобы гарантировать, что функция оценки вычисляет оценку 0,33 для отелей, находящихся в 2 км от желаемого местоположения.
Во втором примере документы со значением поля между 2013-09-12 и 2013-09-22 получат вес 1,0, а документы, которые находятся в 15 днях от этой даты, получат вес 0,5.
Поддерживаемые функции затухания
DECAY_FUNCTION определяет форму затухания:
-
gauss -
Нормальное затухание, вычисляется как:
где
вычисляется для обеспечения того, чтобы оценка принимала значение
decayна расстоянииscaleотorigin+-offsetСм. Нормальное затухание, ключевое слово
gaussдля графиков, демонстрирующих кривую, генерируемую функциейgauss. -
exp -
Экспоненциальное затухание, вычисляется как:
где снова параметр
вычисляется для обеспечения того, чтобы оценка принимала значение
decayна расстоянииscaleотorigin+-offsetСм. Экспоненциальное затухание, ключевое слово
expдля графиков, демонстрирующих кривую, генерируемую функциейexp. -
linear -
Линейное затухание, вычисляется как:
.
где снова параметр
sвычисляется для обеспечения того, чтобы оценка принимала значениеdecayна расстоянииscaleотorigin+-offsetВ отличие от нормального и экспоненциального затухания, эта функция фактически устанавливает оценку в 0, если значение поля превышает двойное значение заданного пользователем масштаба.
Для отдельных функций три функции затухания вместе с их параметрами можно визуализировать следующим образом (поле в этом примере называется «возраст»):
Поля с несколькими значениями
Если поле, используемое для вычисления затухания, содержит несколько значений, по умолчанию выбирается значение, ближайшее к началу отсчета, для определения расстояния. Это можно изменить, установив multi_value_mode.
| | Расстояние — минимальное расстояние |
| | Расстояние — максимальное расстояние |
| | Расстояние — среднее расстояние |
| | Расстояние — сумма всех расстояний |
Пример:
"DECAY_FUNCTION": {
"FIELD_NAME": {
"origin": ...,
"scale": ...
},
"multi_value_mode": "avg"
} Подробный пример
Предположим, вы ищете отель в определенном городе. Ваш бюджет ограничен. Также вы хотите, чтобы отель был недалеко от центра города, поэтому чем дальше отель от желаемого места, тем меньше вероятность, что вы забронируете его.
Вы хотите, чтобы результаты запроса, соответствующие вашим критериям (например, "отель, Нанси, некурящий"), были оценены с учетом расстояния до центра города и цены.
Интуитивно вы хотите определить центр города как начало отсчета и, возможно, вы готовы пройти 2 км до центра города от отеля.
В этом случае вашим началом отсчета для поля местоположения является центр города, а масштаб составляет ~2 км.
Если у вас ограниченный бюджет, то вы, вероятно, предпочтете что-то дешевое, а не дорогое. Для поля цены начало отсчета будет 0 евро, а масштаб зависит от того, сколько вы готовы заплатить, например, 20 евро.
В этом примере поля могут называться "цена" для цены отеля и "местоположение" для координат этого отеля.
Функция для price в этом случае будет
"gauss": {
"price": {
"origin": "0",
"scale": "20"
}
} | Эта функция затухания также может быть |
и для location:
"gauss": {
"location": {
"origin": "11, 12",
"scale": "2km"
}
} | Эта функция затухания также может быть |
Предположим, вы хотите умножить эти две функции на исходный рейтинг. Запрос будет выглядеть следующим образом:
resp = client.search(
query={
"function_score": {
"functions": [
{
"gauss": {
"price": {
"origin": "0",
"scale": "20"
}
}
},
{
"gauss": {
"location": {
"origin": "11, 12",
"scale": "2km"
}
}
}
],
"query": {
"match": {
"properties": "balcony"
}
},
"score_mode": "multiply"
}
},
)
print(resp) response = client.search(
body: {
query: {
function_score: {
functions: [
{
gauss: {
price: {
origin: '0',
scale: '20'
}
}
},
{
gauss: {
location: {
origin: '11, 12',
scale: '2km'
}
}
}
],
query: {
match: {
properties: 'balcony'
}
},
score_mode: 'multiply'
}
}
}
)
puts response res, err := es.Search(
es.Search.WithBody(strings.NewReader(`{
"query": {
"function_score": {
"functions": [
{
"gauss": {
"price": {
"origin": "0",
"scale": "20"
}
}
},
{
"gauss": {
"location": {
"origin": "11, 12",
"scale": "2km"
}
}
}
],
"query": {
"match": {
"properties": "balcony"
}
},
"score_mode": "multiply"
}
}
}`)),
es.Search.WithPretty(),
)
fmt.Println(res, err) const response = await client.search({
query: {
function_score: {
functions: [
{
gauss: {
price: {
origin: "0",
scale: "20",
},
},
},
{
gauss: {
location: {
origin: "11, 12",
scale: "2km",
},
},
},
],
query: {
match: {
properties: "balcony",
},
},
score_mode: "multiply",
},
},
});
console.log(response); GET /_search
{
"query": {
"function_score": {
"functions": [
{
"gauss": {
"price": {
"origin": "0",
"scale": "20"
}
}
},
{
"gauss": {
"location": {
"origin": "11, 12",
"scale": "2km"
}
}
}
],
"query": {
"match": {
"properties": "balcony"
}
},
"score_mode": "multiply"
}
}
} Далее мы покажем, как выглядит вычисленная оценка для каждой из трех возможных функций затухания.
Нормальное затухание, ключевое слово gauss
При выборе gauss в качестве функции затухания в приведенном выше примере контур и поверхность графика множителя выглядят следующим образом:
Предположим, что результаты вашего исходного поиска соответствуют трем отелям:
- "Backback Nap"
- "Drink n Drive"
- "BnB Bellevue".
"Drink n Drive" находится довольно далеко от вашего определенного местоположения (почти 2 км) и не слишком дешевый (около 13 евро), поэтому он получает низкий множитель - 0,56. "BnB Bellevue" и "Backback Nap" находятся довольно близко к определенному местоположению, но "BnB Bellevue" дешевле, поэтому он получает множитель 0,86, а "Backpack Nap" - 0,66.
Экспоненциальное затухание, ключевое слово exp
При выборе exp в качестве функции затухания в приведенном выше примере контур и поверхность графика множителя выглядят следующим образом:
Линейное затухание, ключевое слово linear
При выборе linear в качестве функции затухания в приведенном выше примере контур и поверхность графика множителя выглядят следующим образом:
Поддерживаемые поля для функций затухания
Поддерживаются только числовые, датированные и геокоординатные поля.
Что делать, если поле отсутствует?
Если числовое поле отсутствует в документе, функция вернет 1.
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/query-dsl-function-score-query.html