具有期限和日期范围的聚合

Aggregation with a term and a date range

我是 ElasticSearch 的新手,我正在尝试进行聚合,但似乎做不好。

我在 ElasticSearch 索引中有一些数据,如下所示:

{
    "customerId": "example_customer",
    "request": {
        "referer": "https://example.org",
    }
    "@timestamp": "2020-09-29T14:14:00.000Z"
}

我的映射:

{
    "mappings": {
        "properties": {
            "customerId": { "type": "keyword" },
            "request": {
                "properties": {
                    "referer": { "type": "keyword" }
                }
            }
        }
    }
}

我正在尝试获取在某个日期范围内对特定客户出现最频繁的引荐来源网址。我可以像这样为客户制作过滤器:

var result = await _client.SearchAsync<InsightRecord>(s =>
    s.Aggregations(
        a => a
            .Filter("customer", customer =>
                customer.Filter(q => q.Term(ir => ir.CustomerId, customerId)))
            .Terms("top_referer", ts => ts.Field("request.referer"))
    )
);

return result.Aggregations.Terms("top_referer").Buckets
    .Select(bucket => new TopReferer { Url = bucket.Key, Count = bucket.DocCount ?? 0})

现在我想将其缩小到特定时间范围。这是我目前所拥有的:

var searchDescriptor = s.Aggregations(a =>
    a.Filter("customer", customer =>
        customer.Filter(q =>
            q.Bool(b =>
                b.Must(
                    f2 => f2.DateRange(date => date.GreaterThanOrEquals(from).LessThanOrEquals(to)),
                    f1 => f1.Term(ir => ir.CustomerId, customerId)
                )
            )
        )
    )
    .Terms("top_referers", ts => ts.Field("request.referer"))
);

问题是日期过滤器没有包含在查询中,它转换为 JSON:

{
  "aggs": {
    "customer": {
      "filter": {
        "bool": {
          "filter": [{
              "term": {
                "customerId": {
                  "value": "example_customer"
                }
              }
            }
          ]
        }
      }
    },
    "top_referers": {
      "terms": {
        "field": "request.referer"
      }
    }
  }
}

我试过以不同的方式订购它们,但没有用。 JSON 中始终显示客户过滤器,并且跳过了日期范围。我还看到一些人将查询与聚合结合使用,但我觉得我应该能够单独使用聚合来做到这一点。这可能吗?我在 JSON?

中没有显示范围的查询中做错了什么

问题是日期 range 查询没有指定字段

f2 => f2.DateRange(date => date.GreaterThanOrEquals(from).LessThanOrEquals(to)),

向该查询添加一个字段

f2 => f2.DateRange(date => date
    .Field("@timestamp")
    .GreaterThanOrEquals(from)
    .LessThanOrEquals(to)
),

此外,要将过滤器聚合应用于术语聚合,术语聚合需要是过滤器聚合的 sub 聚合。所以它会像

var customerId = "foo";
var from = "now-365d";
var to = "now";

var result = await _client.SearchAsync<InsightRecord>(s => s
    .Aggregations(a => a
        .Filter("customer", customer => customer
            .Filter(q => q
                .Bool(b => b
                    .Must(
                        f2 => f2.DateRange(date => date
                            .Field("@timestamp")
                            .GreaterThanOrEquals(from)
                            .LessThanOrEquals(to)
                        ),
                        f1 => f1.Term(ir => ir.CustomerId, customerId)
                    )
                )
            )
            .Aggregations(aa => aa
                .Terms("top_referers", ts => ts.Field("request.referer"))
            )
        )
    )
);

虽然我没有使用过滤器聚合指定日期范围和术语查询,但我倾向于将它们指定为搜索请求的查询。计算聚合时会考虑查询。当您想在数据集上查询但 运行 仅在数据集的子集上进行聚合时,过滤器聚合很有用,例如如果您要搜索 所有 客户,但随后想 运行 仅对一部分客户进行聚合。在实践中,对于这个特定的例子,无论查询被指定为搜索请求的查询部分,还是作为过滤器聚合,术语聚合作为 sub,结果应该是相同的聚合,但前者可能更容易从中获得结果。

指定查询类似于

var result = await _client.Search<InsightRecord>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(
                f2 => f2.DateRange(date => date
                    .Field("@timestamp")
                    .GreaterThanOrEquals(from)
                    .LessThanOrEquals(to)
                ),
                f1 => f1.Term(ir => ir.CustomerId, customerId)
            )
        )
    )
    .Aggregations(aa => aa
        .Terms("top_referers", ts => ts.Field("request.referer"))
    )
);

此外,我们还可以做一些其他事情

  1. 由于我们只对术语聚合的结果感兴趣,而不对搜索结果感兴趣,因此我们可以指定 .Size(0) 不在响应中返回搜索结果。
  2. bool 查询可以用 combining queries with operator overloading 更简洁地表达,并且由于两个查询都是谓词(文档匹配查询或不匹配),我们可以在filter 子句省略评分。最后的查询类似于
var result = await _client.SearchAsync<InsightRecord>(s => s
    .Size(0)
    .Query(q => +q
        .DateRange(date => date
            .Field("@timestamp")
            .GreaterThanOrEquals(from)
            .LessThanOrEquals(to)
        ) && +q
        .Term(ir => ir.CustomerId, customerId)
    )
    .Aggregations(aa => aa
        .Terms("top_referers", ts => ts.Field("request.referer"))
    )
);

生成查询

{
  "aggs": {
    "top_referers": {
      "terms": {
        "field": "request.referer"
      }
    }
  },
  "query": {
    "bool": {
      "filter": [{
        "range": {
          "@timestamp": {
            "gte": "now-365d",
            "lte": "now"
          }
        }
      }, {
        "term": {
          "customerId": {
            "value": "foo"
          }
        }
      }]
    }
  },
  "size": 0
}

可以按照您的问题中的表述访问术语聚合桶。