我有一个包含嵌套文档的文档集合。我根据一些复杂的逻辑(查询很大)过滤它们,然后对它们进行排序。我想仅根据嵌套对象的内部命中字段对它们进行排序。但是,显然,OpenSearch 并没有这样做(我已经尝试过指定“inner_hits”选项和不指定“inner_hits”选项)。我猜想在查询的过滤部分和排序部分上复制查询就可以解决问题,但考虑到查询的规模,这似乎有点矫枉过正。有更好的办法吗?
这是一些演示代码:
PUT /demo1
{
"mappings": {
"properties": {
"tags": {
"type": "nested",
"properties": {
"tag": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"weight": {
"type": "double"
}
}
}
}
}
}
PUT demo1/_doc/1
{
"tags": [
{
"tag": "tag1",
"weight": 5
},
{
"tag": "tag2",
"weight": 10
},
{
"tag": "tag3",
"weight": 7
}
]
}
PUT demo1/_doc/2
{
"tags": [
{
"tag": "tag1",
"weight": 1
},
{
"tag": "tag2",
"weight": 3
},
{
"tag": "tag3",
"weight": 16
}
]
}
POST demo1\_search
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "tags",
"query": {
"term": {
"tags.tag": "tag2"
}
},
"inner_hits":{} # this affects nothing, unfortunately
}
}
]
}
},
"sort": {
"tags.weight": {
"missing": "_last",
"mode": "max",
"nested": {
"path": "tags"
},
"order": "desc"
}
}
}
查询输出是这样的(不需要):
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "demo1",
"_id": "2",
"_score": null,
"_source": {
"tags": [
{
"tag": "tag1",
"weight": 1
},
{
"tag": "tag2",
"weight": 3
},
{
"tag": "tag3",
"weight": 16
}
]
},
"sort": [
16
]
},
{
"_index": "demo1",
"_id": "1",
"_score": null,
"_source": {
"tags": [
{
"tag": "tag1",
"weight": 5
},
{
"tag": "tag2",
"weight": 10
},
{
"tag": "tag3",
"weight": 7
}
]
},
"sort": [
10
]
}
]
}
}
...同时,我希望文档按另一个方向排序(仅使用 tag2 进行排序)。
我也可以在排序部分重复过滤,就像这样:
POST demo1\_search
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "tags",
"query": { # this part will be duplicated
"term": {
"tags.tag": "tag2"
}
}
}
}
]
}
},
"sort": {
"tags.weight": {
"missing": "_last",
"mode": "max",
"nested": {
"path": "tags",
"filter": { # duplication
"term": {
"tags.tag": "tag2"
}
}
},
"order": "desc"
}
}
}
但这对我来说并不是很好,因为查询有太多条件。
在使用“inner_hits”选项时,有什么方法可以对内部点击进行排序吗?
据我所知,OpenSearch 原生不支持直接根据
inner_hits
的结果进行排序。 inner_hits
功能主要用于返回查询响应中匹配的嵌套对象,但它并不直接影响主查询结果的排序。排序应用于根文档,而不是 inner_hits
返回的嵌套对象。
要实现基于嵌套对象内字段的排序,通常需要在主查询中指定排序条件,而不是在
inner_hits
部分。正如您已经发现的那样,这通常需要在排序子句中复制查询逻辑的某些部分。
您概述的方法(在排序子句的嵌套部分内复制过滤器)通常是处理此类需求的方式。我理解您对查询的大小和复杂性的担忧。
你可以处理这个问题的方法是
如果查询非常大且复杂,寻找优化方法可能会有所帮助。这可能涉及简化逻辑、减少条件数量或重构查询以提高效率。这有时可以减轻重复的负担。您的情况可以这样做吗?
如果排序逻辑对于标准排序机制来说过于复杂,您可以考虑使用脚本进行排序。这将允许更复杂的逻辑,但显然是以性能为代价的。
在某些情况下,预处理数据以使排序更容易可能会更有效。在索引过程中向您的文档添加其他字段,这些字段是专门为使您的排序要求更容易实现而设计的。
有时,Elasticsearch/OpenSearch 中的数据建模方式可能会使某些类型的查询或排序变得更加困难。检查并可能调整您的数据模型可能会为您的排序需求提供更直接的解决方案。
作为最后的手段,您可以在从 OpenSearch 检索结果后对应用程序代码中的内部命中进行排序。这种方法效率较低且更复杂,但在某些情况下可能是必要的。
此外,我假设您已检查最新文档或社区论坛以了解任何新功能或解决方法。
不幸的是,没有直接的方法可以直接基于
inner_hits
实现排序,而无需在查询或应用程序逻辑中进行某种形式的重复或额外处理。
也就是说,我确实查看了您的查询。要根据 OpenSearch 中嵌套对象内的字段进行排序,您需要在查询的排序部分中指定嵌套路径和排序条件。正如您所提到的,由于 OpenSearch 当前的限制,有必要在排序部分复制部分过滤条件。
以下示例展示了如何构造查询以根据
weight
嵌套对象的 tag2
字段对文档进行排序:
POST demo1/_search
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "tags",
"query": {
"term": {
"tags.tag": "tag2"
}
}
}
}
]
}
},
"sort": {
"tags.weight": {
"order": "desc",
"nested": {
"path": "tags",
"filter": {
"term": {
"tags.tag": "tag2"
}
}
}
}
}
}