elasticsearch - 通过elasticsearch中嵌套字段值的总和查询文档

标签 elasticsearch

在此处对 ElasticSearch 初学者进行排名。

我有一个客户列表,他们的订单作为嵌套字段。假设文档结构如下:

[
  { customerId: 123,
    birthday: 1980-01-01,
    orders: [
      {
        orderValue: 1500,
        orderDate: 2018-12-18T12:18:12Z
      },
      [...]
    ]
  },
  [...]
}

我想查询的是:两个日期之间下单了一定金额的用户列表。我希望能够将其与范围查询相结合,例如生日。

我已经到了可以使用聚合获得每个订阅者两个日期之间排序的总和的地步:

{
  "size": 0,
  "aggs": {
    "foo": {
      "nested": {
        "path": "orders"
      },
      "aggs": {
        "grouped_by_customerId": {
          "terms": {
            "field": "orders.customerId.keyword"
          },
          "aggs": {
            "filtered_by_date": {
              "filter": {
                "range": {
                  "orders.orderDate": {
                    "from": "2018-01-28",
                    "to": null,
                    "include_lower": false,
                    "include_upper": true,
                    "format": "yyyy-MM-dd",
                    "boost": 1
                  }
                }
              },
              "aggs": {
                "sum": {
                  "sum": {
                    "field": "orders.orderValue"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

但是,我想限制在查询部分返回的结果,以便更好地与我们所有其他过滤器混合。

我的第一个想法是使用脚本过滤器并将边界日期和最小值作为参数传递,但随后我不得不迭代文档的嵌套文档,这似乎行不通。

最后一个想法是否可行,如果可行,如何实现?

谢谢!

最佳答案

终于自己解决了这个问题,使用 Function Score query如下:

{
  "query": {
    "bool": {
      "must": [
        {
          "function_score": {
            "min_score": 1,
            "query": {
              "nested": {
                "path": "orders",
                "ignore_unmapped": false,
                "score_mode": "min",
                "boost": 1,
                "query": {
                  "range": {
                    "orders.orderDate": {
                      "from": "2018-12-10",
                      "to": null,
                      "include_lower": true,
                      "include_upper": true,
                      "format": "yyyy-MM-dd",
                      "boost": 1
                    }
                  }
                }
              }
            },
            "functions": [
              {
                "filter": {
                  "match_all": {}
                },
                "script_score": {
                  "script": {
                    "source": "ArrayList x = params['_source']['orders'];if (x == null) { return 0 }long result = x.stream().filter(order -> {  if(params.startDate != null && !ZonedDateTime.parse(order.orderDate).isAfter(ZonedDateTime.parse(params.startDate))) return false; return true}).mapToLong(order->Long.parseLong(order.orderValue)).sum();if(params.operator == 'GT') return result > params.totalOrderValue ? 2 : 0;else if (params.operator == 'GE') return result >= params.totalOrderValue ? 3 : 0;else if (params.operator == 'LE') return result <= params.totalOrderValue ? 4 : 0;else if(params.operator == 'LT') return result < params.totalOrderValue ? 5 : 0;return result == params.totalOrderValue ? 6 : 0",
                    "lang": "painless",
                    "params": {
                      "totalOrderValue": 120,
                      "operator": "GE",
                      "startDate": "2012-12-10T23:00:00.000Z"
                    }
                  }
                }
              }
            ],
            "score_mode": "multiply",
            "max_boost": 3.4028235e+38,
            "boost": 1
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1
    }
  }
}

这产生的实际分数是用于测试运算符的调试输出,但是 min_score 为 1 意味着它们中的任何一个都匹配。使用 _source 非常慢。

如果不在 function_score 中查询,它可以工作,但需要 20 秒左右的时间来处理 300 万条记录。通过查询,您仅查看订单与日期范围实际匹配的客户。

由于无痛脚本处理了整个订单列表,因此它必须重新计算日期。在那里要做一些优化,但至少我有一个概念证明。

我以前见过这个问题,但没有满意的答案,所以希望有人觉得这有用。

关于elasticsearch - 通过elasticsearch中嵌套字段值的总和查询文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53899312/

相关文章:

c# - NEST是否会在Elasticsearch或客户端中进行投影?

Elasticsearch:最大虚拟内存区域 vm.max_map_count [65530] 太低,至少增加到 [262144]

mysql - 哪种数据库引擎适合大数据集

elasticsearch - 如何减少Elasticsearch查询子句的数量?

elasticsearch - 为字段的所有子字段设置 'index'属性

javascript - elastic.js 中的 Elasticsearch 聚合查询

elasticsearch - 如何设置Elasticsearch和Filebeat

java - Elasticsearch错误: failed to parse,文档为空

java - 使用 Elasticsearch 的 RestClient 时如何绕过 "connection reset by peer"

angularjs - 使用存储在Angular变量中的URI查询来查询Elasticsearch