Elasticsearch 订单项按分数聚合

Elasticsearch order terms aggregate by score

我是 ElasticSearch 的新手。以前我只在 Django-Haystack 中以非常有限的方式使用它,并且从未直接与 ES 交谈过。

目前,我有一个包含一些文档的 ElasticSearch(5.x 索引)。我正在使用 Python + elasticsearch-dsl + django-elasticsearch-dsl 所以我正在索引数据库模型,但这并不重要。我会尽量让这个问题与库无关。

从概念上讲,我将用户和他们的帖子都存储在同一个索引中。用户文档和帖子文档有一个共同点 - 字段 user_id.

用户看起来像这样:

{
    "_id": 1,
    "_type": "user_document",
    "username": "jdoe",
    "user_id": 1,
    "title": "Test user"
}

帖子是这样的:

{
    "_id": 1,
    "_doc": "post_document",
    "user_id": 1,
    "title": "Hello world!",
    "text": "Lorem ipsum test test test..."
}

我希望我的应用程序实现的是一个单输入搜索字段,它对用户及其帖子进行全文搜索(在现实世界中有更多文档 "types" - 我正在简化事情在这里,仅用于示例目的)。我想按 user_id 聚合以仅显示匹配的不同用户的列表。

目前,我正在做这样的查询:

{
    "query": {
        "multi_match": {
            "query": "test",
            "fields": ["username^3", "title^2", "text"]
        }
    },
    "aggs": {
        "user_ids": {"terms": {"field": "user_id"}}
    }
}

然后使用响应的 aggregations.user_ids.buckets.key 获取匹配用户列表。

然而,该列表似乎只是按文档计数排序(因此,如果用户有一对包含单词 "test" 的帖子,他们似乎会赢得名为 "test" 的用户),并且我想尝试订购。我目前的想法是使用平均(或中值)文档匹配 _score.

注意:在实际情况中,不止有两种文档类型,因此走捷径并仅在特定 _type 上查询是行不通的。

我该怎么做?我正在阅读 "Sorting by a Metric" 一章,但我有点迷失了那里的想法。我做了几次尝试,但基本上都是无稽之谈。任何人都可以展示一个具体的查询示例(最好是解释它是如何构建的),以便我从中学习?

这是 Gist with an example dataset,上面显示的搜索查询,以及我得到的确切结果。我想要的(在 test_query_01_results.json 中)是让 user_id 1 优先于 2,其逻辑是 2.0794415 > (0.78306973 + 0.45315093) / 2.

我觉得我做错的另一件事是我根本不使用 hits - 我只是不需要它们 - 只有聚合的 user_id 值。如果这没问题 - 有没有办法 "disable" 它们并且只有 return 聚合?

我想我找到了对聚合进行排序的解决方案。我必须创建一个子聚合,然后一切正常。我错了,在没有任何 _score 的情况下试图使用 "order": {"_score: "desc"} 和类似的废话(那是文档的集合,而不是文档,所以那里没有分数)。

{
    "query": {
        "multi_match": {
            "query": "test",
            "fields": ["username^3", "title^2", "text"]
        }
    },
    "aggs": {
        "user_ids": {
            "terms": {
                "field": "user_id",
                "order": {"avg_score": "desc"}
            },
            "aggs": {
                "avg_score": {
                    "avg": {"script": "_score"}
                }
            }
        }
    }
}

有了这个,我的 aggregations 看起来就像我想要的那样:

...
"aggregations": {
    "user_ids": {
        "buckets": [
            {
                "avg_score": {"value": 2.079441547393799},
                "doc_count": 1,
                "key": 1
            }, 
            {
                "avg_score": {"value": 0.618110328912735},
                "doc_count": 2,
                "key": 2
            }
        ],
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0
    }
},
...

但是,关于 hits(我不使用)的问题仍然存在。

使用以下查询

{
"size": 0 ,                    ==> to return no hits
"query": {                     ==> query similar to yours
    "multi_match": {
        "query": "test",
        "fields": ["username^3", "title^2", "text"]
    }
},
"aggs": {
    "user_ids": {
        "terms": {
            "field": "user_id",
            "order": {"avg_score": "desc"}
        },
        "aggs": {
            "avg_score": {
                "avg": {"script": "_score"}
              }
          }
      }
    }
  }