具有期限和日期范围的汇总

时间:2020-09-30 14:14:33

标签: c# json elasticsearch nest

我是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中是什么错误?

1 个答案:

答案 0 :(得分:1)

问题在于日期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. combining queries with operator overloading可以更简洁地表示bool查询,并且由于两个查询都是谓词(文档与查询匹配或不匹配),我们可以在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
}

可以按照您的问题中的说明来访问术语聚合桶。