我建议使用基于脚本的排序来完成此操作,如下所述:
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 就很容易了 - 您可以使用“自动缩进”功能来降低 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}>]>
但是,正如您所指出的,您想要 最接近 值,我建议将 size 参数更改为 1。