
在Elasticsearch中,常规的单字段或多字段排序通常能够满足大部分需求。然而,当面临需要根据字段的特定状态(例如字段是否存在、字段值是否为空)来决定排序优先级,并且在此基础上再结合其他字段进行排序时,标准排序机制可能显得力不从心。这时,Elasticsearch提供的脚本排序(Script-Based Sorting)功能便成为解决此类复杂问题的强大工具。
考虑以下场景:我们需要对文档进行排序,首先根据 tags 字段是否包含任何标签来区分文档。有标签的文档应优先显示,然后按 createdAt 字段升序排列;而没有标签的文档则排在后面,同样按 createdAt 字段升序排列。这种“有条件”的排序逻辑,正是脚本排序的典型应用场景。
为了演示这一过程,我们首先需要创建一个Elasticsearch索引,并定义相应的字段映射。createdAt 字段应为 date 类型以便进行时间排序,而 tags 字段则应为 keyword 类型,以支持精确匹配和脚本访问其内容。
PUT idx_sort
{
"mappings": {
"properties": {
"createdAt": {
"type": "date"
},
"tags": {
"type": "keyword"
}
}
}
}接下来,我们插入一些示例文档,这些文档包含不同 tags 状态和 createdAt 时间,以验证我们的排序逻辑。
POST idx_sort/_doc
{
"createdAt": "2022-11-25T09:45:00.000Z",
"tags": [
"Response Needed"
]
}
POST idx_sort/_doc
{
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": [
"Response 02"
]
}
POST idx_sort/_doc
{
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": [
"Customer care","Response Needed"
]
}
POST idx_sort/_doc
{
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": [
]
}解决上述复杂排序需求的关键在于使用 _script 排序。我们将在排序数组中定义一个基于Painless语言的脚本,该脚本负责为每个文档生成一个临时的排序值。
脚本的核心逻辑是判断 tags 字段是否为空。在Painless脚本中,可以通过 doc['field_name.keyword'] 来访问字段的 fielddata,并使用 .size() 方法获取多值字段的元素数量。
def list = doc['tags.keyword']; // 获取tags字段的fielddata
if(list.size() > 0){ // 判断tags列表是否包含元素
return 1; // 如果有标签,返回1作为排序值
} else {
return 0; // 如果没有标签,返回0作为排序值
}这个脚本会为每个文档生成一个数字:如果 tags 字段有值,则返回 1;如果 tags 字段为空,则返回 0。我们将这个脚本作为主要的排序键,并将其 order 设置为 desc(降序),这样 1(有标签)的文档就会排在 0(无标签)的文档之前。
在脚本排序之后,我们需要添加 createdAt 字段的排序。当多个文档通过脚本排序得到相同的值时(例如,所有有标签的文档都得到 1),Elasticsearch会根据 sort 数组中的下一个排序条件进行排序。在本例中,我们希望 createdAt 按升序排列。
{
"createdAt": {
"order": "asc"
}
}将上述脚本排序和辅助排序结合起来,构成完整的搜索查询:
GET idx_sort/_search
{
"sort": [
{
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": """
def list = doc['tags.keyword'];
if(list.size() > 0){
return 1;
} else {
return 0;
}
"""
},
"order": "desc"
}
},
{
"createdAt": {
"order": "asc"
}
}
]
}执行上述查询后,Elasticsearch将返回排序后的文档。观察 hits 数组中的 sort 字段,它会显示每个文档对应的排序键值。例如,[1, 1669283100000] 表示该文档的脚本排序值为 1,createdAt 字段的时间戳为 1669283100000。
"hits": [
{
"_index": "idx_sort",
"_id": "kI89toQBEoAIompjxkWN",
"_score": null,
"_source": {
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": [
"Customer care",
"Response Needed"
]
},
"sort": [
1,
1669283100000
]
},
{
"_index": "idx_sort",
"_id": "j489toQBEoAIompjkkXO",
"_score": null,
"_source": {
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": [
"Response 02"
]
},
"sort": [
1,
1669283100000
]
},
{
"_index": "idx_sort",
"_id": "jo83toQBEoAIompjcEXD",
"_score": null,
"_source": {
"createdAt": "2022-11-25T09:45:00.000Z",
"tags": [
"Response Needed"
]
},
"sort": [
1,
1669369500000
]
},
{
"_index": "idx_sort",
"_id": "kY8-toQBEoAIompj6kXg",
"_score": null,
"_source": {
"createdAt": "2022-11-24T09:45:00.000Z",
"tags": []
},
"sort": [
0,
1669283100000
]
}
]从结果中可以看出,所有带有标签的文档(脚本排序值为 1)都排在了前面,并且在它们内部,根据 createdAt 字段进行了升序排列。接着是无标签的文档(脚本排序值为 0),同样根据 createdAt 字段升序排列。
通过本教程,我们学习了如何利用Elasticsearch的脚本排序功能,结合Painless脚本来处理多字段和条件性的复杂排序需求。这种方法提供了极大的灵活性,能够根据业务逻辑动态生成排序键,从而实现精确的数据组织和呈现。在实际应用中,务必权衡其带来的灵活性与潜在的性能开销,并进行充分的测试和优化。
以上就是Elasticsearch多字段条件排序:利用Painless脚本实现复杂逻辑的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号