OpenSearch:对内部命中进行排序

OpenSearch: Sorting on inner hits

提问人:user2173353 提问时间:11/14/2023 最后编辑:user2173353 更新时间:11/15/2023 访问量:55

问:

我有一个包含嵌套文档的文档集合。我根据一些复杂的逻辑(查询很大)过滤它们,然后对它们进行排序。我只想根据嵌套对象的内部命中字段对它们进行排序。但是,显然,OpenSearch 没有这样做(我已经尝试过指定“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 进行排序

评论


答:

1赞 Alex Ixeras 11/15/2023 #1

据我所知,OpenSearch 本身并不支持直接根据 的结果进行排序。该功能主要用于在查询响应中返回匹配的嵌套对象,但它不会直接影响主查询结果的排序。排序应用于根文档,而不是 返回的嵌套对象。inner_hitsinner_hitsinner_hits

要实现基于嵌套对象中的字段的排序,通常需要在主查询中指定排序条件,而不是在部件中指定排序条件。这通常需要在排序子句中复制查询逻辑的某些部分,正如你已经发现的那样。inner_hits

您概述的方法(在排序子句的嵌套部分内复制筛选器)通常是处理此类要求的方式。我理解您对查询的大小和复杂性的担忧。

你可以处理这个的方法是

优化您的查询

如果查询非常大且复杂,则寻找优化它的方法可能会有所帮助。这可能涉及简化逻辑、减少条件数量或重构查询以提高其效率。这有时可以使复制的负担减轻。这是在你的情况下可以做的事情吗?

使用脚本化排序

如果排序逻辑对于标准排序机制来说过于复杂,可以考虑使用脚本进行排序。这将允许更复杂的逻辑,但显然是以牺牲性能为代价的。

预处理数据

在某些情况下,对数据进行预处理以简化排序可能会更有效。在索引过程中向文档添加其他字段,这些字段专门设计用于使排序要求更易于实现。

查看数据模型

有时,在 Elasticsearch/OpenSearch 中对数据进行建模的方式会使某些类型的查询或排序变得更加困难。查看并可能调整数据模型可能会为您的排序要求提供更直接的解决方案。

后处理结果

作为最后的手段,您可以在从 OpenSearch 检索结果后对应用程序代码中的内部命中进行排序。这种方法效率较低且更复杂,但在某些情况下可能是必要的。


此外,我假设您已经查看了最新的文档或社区论坛,了解任何新功能或解决方法。

遗憾的是,没有直接的方法可以直接实现排序,而无需在查询或应用程序逻辑中进行某种形式的复制或额外处理。inner_hits

也就是说,我确实看了一下你的查询。要根据 OpenSearch 中嵌套对象中的字段进行排序,您需要在查询的 sort 部分中指定嵌套路径和排序条件。正如您提到的,由于 OpenSearch 的当前限制,在排序部分复制部分筛选条件是必要的。

以下示例说明了如何构建查询以根据嵌套对象的字段对文档进行排序:weighttag2

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"
          }
        }
      }
    }
  }
}

请记住,在排序部分中复制筛选条件是必要的,以确保排序基于正确的嵌套对象。查询的复杂性可能会随着更复杂的筛选逻辑而增加,但要在 OpenSearch 中实现所需的排序行为,需要此结构。