Elasticsearch - 获取(过滤的)子集的(未过滤的)聚合

我有一个弹性搜索索引,其中包含从我的 nginx 访问日志填充的“命中”文档(包含 ip/timestamp/uri 等字段)。

我正在寻找一种获取点击次数/IP的方法 - 但对于IP的子集,即今天发出请求的IP。

我知道我可以通过以下方式进行过滤聚合:

/search?size=0
{
    'query': { 'bool': { 'must': [
        {'range': { 'timestamp': { 'gte': $today}}},{'query_string': {'query': 'status:200 OR status:404'}},]}},'aggregations': {'c': {'terms': {'field': 'ip','size': 99999}}}
}

但这只会总结今天完成的点击次数,我想要索引中的点击次数,但仅来自今天点击次数的 IP。这可能吗?

-编辑-

我已经尝试了 global 选项,但同时

'aggregations': {'c': {'global': {},'aggs': {'c2': {'terms': {'field': 'remote_user','size': 99999}}}}}

返回所有 IP 的计数;它忽略我的时间戳过滤器(例如,它包括几天前点击的 IP)

Hnixy 回答:Elasticsearch - 获取(过滤的)子集的(未过滤的)聚合

有一种方法可以在单个查询中实现您想要的结果,但由于它涉及脚本,并且性能可能会受到影响,具体取决于您将运行此查询的数据量。

我们的想法是利用 scripted_metric aggregation 在整个文档集上构建您自己的聚合逻辑。

我们下面要做的很简单:

  • 我们不提供任何查询,因此我们考虑完整的文档集
  • 映射阶段:我们构建所有 IP 的映射,并为每个
    • 我们计算总点击次数
    • 如果它今天命中并且具有给定的状态(与您在查询中所做的相同),我们会对其进行标记
  • 减少阶段:我们返回标记为今天有点击的每个 IP 的总点击数

查询如下所示:

POST my-index/_search
{
  "size": 0,"aggs": {
    "all_time_hits": {
      "scripted_metric": {
        "init_script": "state.ips = [:]","map_script": """
          // initialize total hits count for each IP and increment
          def ip = doc['ip.keyword'].value;
          if (state.ips[ip] == null) {
            state.ips[ip] = [
              'total_hits': 0,'hits_today': false
            ]
          }
          state.ips[ip].total_hits++;

          // flag IP if:
          // 1. it has hits today 
          // 2. the hit had one of the given statuses
          def today = Instant.ofEpochMilli(new Date().getTime()).truncatedTo(ChronoUnit.DAYS);
          def hitDate = doc['timestamp'].value.toInstant().truncatedTo(ChronoUnit.DAYS);
          def hitToday = today.equals(hitDate);
          def statusOk = params.statuses.indexOf((int) doc['status'].value) >= 0;
          state.ips[ip].hits_today = state.ips[ip].hits_today || (hitToday && statusOk);
        ""","combine_script": "return state.ips;","reduce_script": """
          def ips = [:];
          for (state in states) {
            for (ip in state.keySet()) {
              // only consider IPs that had hits today
              if (state[ip].hits_today) {
                if (ips[ip] == null) {
                  ips[ip] = 0;
                }
                ips[ip] += state[ip].total_hits;
              }
            }
          }
          return ips;
        ""","params": {
          "statuses": [200,404]
        }
      }
    }
  }
}

答案如下:

  "aggregations" : {
    "all_time_hits" : {
      "value" : {
        "123.123.123.125" : 1,"123.123.123.123" : 4
      }
    }
  }

我认为这几乎符合您的预期。

另一个选项(性能更高,因为没有脚本)需要您进行两次查询。首先,具有日期范围和状态检查的查询使用 terms 聚合来检索今天命中的所有 IP(就像您现在所做的那样),然后是第二个查询,您在这些 IP 上进行过滤(使用 {{ 1}} 查询)对整个索引(无日期范围或状态检查)并使用 terms 聚合获取每个索引的命中计数。

,

在您共享的示例中,您有一个查询,并且您的文档会根据该查询进行过滤。但是您希望您的聚合处理所有文档,而不管查询如何。

这就是 global 选项存在的原因。

此上下文由您搜索的索引和文档类型定义,但不受搜索查询本身的影响。

示例查询示例:

{
  "query": {
    "match": { "type": "t-shirt" }
  },"aggs": {
    "all_products": {
      "global": {},"aggs": {     
      "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

本文链接:https://www.f2er.com/24457.html

大家都在问