Я бы порекомендовал использовать сортировку на основе сценариев для достижения этой цели, как описано здесь: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#_script_based_sorting
Работая в предположении, что вы сопоставляете только имя, - если вы хотите точно соответствовать имени, Я бы порекомендовал использовать совпадение на основе фильтра.В индексе я использовал трех разных «пользователей», определенных следующим образом:
POST index1/_doc
{
"name": "John White",
"age": 46
}
POST index1/_doc
{
"name": "John White",
"age": 40
}
POST index1/_doc
{
"name": "John Black",
"age": 47
}
Мне проще написать что-то более сложное, например, с помощью инструментов разработчика Kibana для тестирования, а затем преобразовать его вPython Elasticsearch DSL-совместимый формат - поэтому в Kibana я в конечном итоге придумал следующее:
GET index1/_search
{
"query": {
"match_phrase": {
"name": {
"query": "john"
}
}
},
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": "Math.abs(doc['age'].value - params.target_age)",
"params": {
"target_age": 46
}
},
"order": "asc"
}
}
}
Примечание: использование абсолютного значения разницы даст вам наиболее близкое значение в любом направлении (то есть младше или старше),Некоторые настройки могут быть необходимы, если ваши требования отличаются.Просто измените параметр при изменении запросов, чтобы приспособиться к разным целевым возрастам.
После тестирования и проверки преобразование в Python Elasticsearch DSL довольно просто - вы можете использовать функцию 'Auto Indent', чтобы уменьшить сложность sort
и поместите его прямо в существующий оператор.
s = Search(using=es, index="index1").extra(size=500) \
.query("match_phrase", name={"query": "john".casefold()}) \
.sort({"_script":{"type":"number","script":{"lang":"painless","source": \
"Math.abs(doc['age'].value - params.target_age)", \
"params":{"target_age":46}},"order":"asc"}})
Выполнение этого возвращает ожидаемый ответ:
<Response: [<Hit(index1/_doc/VR3e7WkBsHIsqLp6vfx_): {'name': 'John White', 'age': 46}>, <Hit(index1/_doc/Vx3f7WkBsHIsqLp6DPxM): {'name': 'John Black', 'age': 47}>, <Hit(index1/_doc/Vh3e7WkBsHIsqLp6yfxd): {'name': 'John White', 'age': 40}>]>
Однако, как вы указали, вы хотите ближайший значение, я бы рекомендовал изменить параметр размера на 1
.