ElasticSearch多级父子聚合


79

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

公司->员工->可用性

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

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

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

  • 给定ASC到位置的距离
  • 评分DESC
  • 尽快供货ASC

例如:

公司A距离5英里,评级为4,并且其一名雇员最快在20小时内可用。公司B也距离5英里,也评级为4,但其一名雇员最快可以在5小时内可用。

因此排序结果必须为B,A。

我想对每个数据赋予特殊的权重,因此我开始编写聚合,以后可以在custom_score脚本中使用。

创建索引,导入数据和搜索的全部依据

现在,我设法编写了一个查询,该查询实际上返回了结果,但是可用性聚合存储桶为空。但是,我也将结果重新整理得井井有条,我想将它们弄平。

目前我回来了:

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

我想要像这样的聚合:

公司IDS->首次供货

这样,我就能执行custom_score脚本来计算分数并对其进行正确排序。

更简化的问题:
如何按多级(大)孩子进行排序/汇总并可能使结果平坦。


您可以将映射和一些示例文档(带有后代)添加到要点吗?很难看到如何发明可对系统进行适当测试的伪造文档。
斯隆·阿伦斯

嘿斯隆-我添加了映射和示例结果。为了方便理解,我将其剥离了一点。全栈中有更多数据:)谢谢!
Pete Minus 2014年

我在这里有同样的问题。尽管性能可能较低,但我只请求具有默认类型DocCount的所有结果。然后,我自己进行了递归展平,排序和限制,这并不理想。
Matt Traynham 2015年

1
我已经执行了要点,但是搜索时出现错误500 Query Failed [Failed to execute main query]]; nested: NullPointerException;。您能否在本地环境中执行要点并确保一切正常?谢谢!
2015年

为什么不为您的结果创建方程式。您的数据不模糊!您汇总每个查询?。聚合是输入操作,而不是查询或输出。一个问题“如何检查此结果是否为True(正确)?”
dsgdfg,2015年

Answers:


3

您不需要进行聚合:

这些是排序标准:

  1. 距离ASC(公司位置)
  2. 评分DESC(company.rating_value)
  3. 不久的将来的可用性ASC(company.employee.availability.start)

如果忽略#3,则可以运行相对简单的公司查询,如下所示:

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棘手,因为您需要向下查找每个与请求时间最接近的公司的可用性(公司>员工>可用性),并将该持续时间用作第三排序标准。

我们将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每个孙子代(Availability)将是1 / number-of-hours-until-available(以便我们可以使用最长的互惠时间,直到每个Employee可用为止)和最大互惠(ly?)每个公司提供的员工)。

放在一起,我们继续查询公司,但使用公司>员工>可用性生成_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" }
 }
}

使用线性衰减函数而不是_scoretime-until-available生成脚本可能会获得更好的性能。
彼得·迪克森·摩西

Elasticsearch默认情况下禁用动态脚本。更好的方法是使用索引脚本。看到这里:elastic.co/blog/…–
schellingerht

皮特·米纳斯(Pete Minus):您能使它正常工作吗?我知道这是一个比较老的问题,但是有很多人对您的解决方案感兴趣。
彼得·迪克森·摩西

彼得·迪克森-摩西(Peter Dixon-Moses):最终,我放弃了并写下了两个查询-首先按公司/员工进行搜索,然后通过“可用性”搜索排名前100位的公司,然后合并。为什么?仅在ES中构建它要花费太多时间/精力。搜索所花费的时间是可以接受的。
Pete Minus 2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.