【问题标题】:ElasticSearch multi level parent-child aggregationElasticSearch 多级父子聚合
【发布时间】:2015-02-15 03:04:38
【问题描述】:

我有 3 个级别的父/子结构。比方说:

公司 -> 员工 -> 可用性

由于可用性(以及员工)在这里经常更新,我选择使用父/子结构来对抗嵌套。并且搜索功能工作正常(所有文档都在正确的分片中)。

现在我想对这些结果进行排序。按公司(第一级)的元数据对它们进行排序很容易。但我还需要按 3 级(可用性)排序。

我想要按以下方式排序的公司列表:

  • 与指定 ASC 位置的距离
  • 评级 DESC
  • 最快可用 ASC

例如:

A 公司在 5 英里外,评级为 4,并且他们的一名员工最快在 20 小时内有空 B 公司也在 5 英里外,评级也为 4,但他们的一名员工最快在 5 小时内有空。

因此排序结果需要是B,A。

我想为每个数据附加特殊的权重,所以我开始编写聚合,以后可以在我的 custom_score 脚本中使用。

Full gist for creating index, importing data and searching

现在,我设法编写了一个实际返回结果的查询,但可用性聚合存储桶为空。 但是,我也得到了过于结构化的结果,我想将它们展平。

目前我回来了:

公司 IDS -> 员工 IDS -> 首次可用性

我想进行如下聚合:

公司 IDS -> 首次可用性

这样我就可以使用我的custom_score 脚本来计算分数并正确排序。

更简化的问题:
如何按多级(大)子级排序/聚合并可能使结果变平。

【问题讨论】:

  • 您能否将您的映射和一些示例文档(带有后代)添加到要点中?很难看到如何发明能够对您的系统进行充分测试的虚假文档。
  • 嘿 Sloan - 我添加了映射和示例结果。为了更容易理解,我已经把它剥离了一点。完整堆栈中包含更多数据:) 谢谢!
  • 我有同样的问题here。尽管性能可能较差,但我只是请求所有具有默认类型 DocCount 的结果。然后我做了自己的递归展平、排序和限制,这并不理想。
  • 我已经执行了你的要点,但是在搜索时我收到错误 500 Query Failed [Failed to execute main query]]; nested: NullPointerException;。你能在你的本地环境中执行你的要点并确保它没问题吗?谢谢!
  • 为什么不为您的结果创建方程式。你的数据不模糊!您汇总每个查询? .聚合是输入操作,而不是查询或输出。一个问题“你如何检查这个结果是真的(对)?”

标签: sorting elasticsearch aggregation


【解决方案1】:

您不需要聚合来执行此操作:

这些是排序标准:

  1. 距离 ASC (company.location)
  2. 评级 DESC (company.rating_value)
  3. 未来最快可用性 ASC (company.employee.availability.start)

如果你忽略#3,那么你可以像这样运行一个相对简单的company查询:

GET /companies/company/_search
{
 "query": { "match_all" : {} },
 "sort": {
    "_script": {
        "params": {
            "lat": 51.5186,
            "lon": -0.1347
        },
        "lang": "groovy",
        "type": "number",
        "order": "asc",
        "script": "doc['location'].distanceInMiles(lat,lon)"
    },
    "rating_value": { "order": "desc" }
  }
}

#3 很棘手,因为您需要找到最接近请求和使用时间的每家公司的可用性(company > employee > availability)该持续时间作为第三个​​排序标准。

我们将在孙级别使用function_score 查询来获取请求时间与命中_score 中每个可用性之间的时间差。 (然后我们将使用_score 作为第三个排序标准)。

要联系孙辈,我们需要在 has_child 查询中使用 has_child 查询。

对于每家公司,我们都希望有最快的员工(当然还有他们最接近的员工)。 Elasticsearch 2.0 将为我们提供"score_mode": "min" 来处理此类情况,但目前,由于我们仅限于"score_mode": "max",我们将让孙子_score 成为当时的倒数 -区别。

          "function_score": {
            "filter": { 
              "range": { 
                "start": {
                  "gt": "2014-12-22T10:34:18+01:00"
                } 
              }
            },
            "functions": [
              {
                "script_score": {
                  "lang": "groovy",
                  "params": {
                      "requested": "2014-12-22T10:34:18+01:00",
                      "millisPerHour": 3600000
                   },
                  "script": "1 / ((doc['availability.start'].value - new DateTime(requested).getMillis()) / millisPerHour)"
                }
              }
            ]
          }

所以现在每个孙子的_score可用性)将是1 / number-of-hours-until-available(这样我们就可以使用最大倒数时间,直到每个员工都可以使用,并且每个公司的最大倒数(ly?) 个可用员工)。

综上所述,我们继续查询company,但使用company > employee >availabilty 生成_score 用作#3 排序标准:

GET /companies/company/_search
{
 "query": { 
    "has_child" : {
        "type" : "employee",
        "score_mode" : "max",
        "query": {
          "has_child" : {
            "type" : "availability",
            "score_mode" : "max",
            "query": {
              "function_score": {
                "filter": { 
                  "range": { 
                    "start": {
                      "gt": "2014-12-22T10:34:18+01:00"
                    } 
                  }
                },
                "functions": [
                  {
                    "script_score": {
                      "lang": "groovy",
                      "params": {
                          "requested": "2014-12-22T10:34:18+01:00",
                          "millisPerHour": 3600000
                       },
                      "script": "1/((doc['availability.start'].value - new DateTime(requested).getMillis()) / millisPerHour)"
                    }
                  }
                ]
              }
            }
          }
        }
    }
 },
 "sort": {
  "_script": {
    "params": {
        "lat": 51.5186,
        "lon": -0.1347
    },
    "lang": "groovy",
    "type": "number",
    "order": "asc",
    "script": "doc['location'].distanceInMiles(lat,lon)"
  },
  "rating_value": { "order": "desc" },
  "_score": { "order": "asc" }
 }
}

【讨论】:

  • 使用linear decay function 而不是从time-until-available 生成_score 的脚本可能会获得更好的性能。
  • Elasticsearch 默认禁用动态脚本。更好的是使用索引脚本。见这里:elastic.co/blog/…
  • Pete Minus:你能做到吗?我知道这是一个较老的问题,但是有很多人对您的解决方案感兴趣。
  • Peter Dixon-Moses:最终我放弃并写了两个查询 - 首先按公司/员工搜索,然后通过可用性搜索前 100 家公司,然后合并。为什么?仅在 ES 中构建它花费了太多时间/精力。花在搜索上的时间是可以接受的。
【解决方案2】:

您应该查看 R-Tree 数据结构 https://en.wikipedia.org/wiki/R-tree

【讨论】:

    猜你喜欢
    • 2023-03-20
    • 2016-02-17
    • 1970-01-01
    • 2018-07-05
    • 2022-01-25
    • 1970-01-01
    • 2017-07-12
    • 2021-05-11
    • 1970-01-01
    相关资源
    最近更新 更多