首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Elasticsearch -为(已过滤的)子集获取(未过滤)聚合

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

Stack Overflow用户
提问于 2021-07-28 07:58:13
回答 2查看 108关注 0票数 1

我有一个包含“命中”文档的elasticsearch索引(包括ip/timestamp/uri等字段),这些文档是从我的nginx访问日志中填充的。

我正在寻找一种方法来获取数量的hits / ip -但是对于一个ip的子集,也就是那些今天进行请求的ip。

我知道我可以通过以下操作进行过滤聚合:

代码语言:javascript
复制
/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。这个是可能的吗?

-编辑-

我尝试过全局选项,但是

代码语言:javascript
复制
'aggregations': {'c': {'global': {}, 'aggs': {'c2': {'terms': {'field': 'remote_user', 'size': 99999}}}}}

返回来自所有IP;它忽略了我在时间戳上的过滤器(例如。其中包括几天前命中的IP)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-30 09:38:12

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

其思想是利用聚合,以便在整个文档集上构建自己的聚合逻辑。

我们下面所做的非常简单:

  • 我们不提供任何查询,所以我们考虑完整的文档集
  • 映射阶段:,我们为所有IP和每个构建了一个映射
    • 我们计算点击率。
    • 如果它今天有命中,并且具有给定的状态,我们将标记它(与您在查询中所做的相同)。

  • 缩减阶段:,我们返回今天被标记为有点击的每个IP的总命中数。

下面是查询的样子:

代码语言:javascript
复制
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]
        }
      }
    }
  }
}

答案是这样的:

代码语言:javascript
复制
  "aggregations" : {
    "all_time_hits" : {
      "value" : {
        "123.123.123.125" : 1,
        "123.123.123.123" : 4
      }
    }
  }

我觉得这很符合你的期望。

另一个选项(因为没有脚本)要求您进行两个查询。首先是带有日期范围和状态检查的terms聚合查询,以检索当前有命中的所有IP(就像现在一样),然后是第二个查询,在整个索引(没有日期范围或状态检查)上对这些IP进行筛选(使用terms查询),并使用terms聚合对每个IP进行点击计数。

票数 1
EN

Stack Overflow用户

发布于 2021-07-28 08:23:25

在您已经共享的示例中,您有一个查询,并且您的文档会根据这个查询进行过滤。但是,您希望您的聚合能够接受所有文档,而不管查询是什么。

这就是为什么存在global选项。

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

示例查询示例:

代码语言:javascript
复制
{
  "query": {
    "match": { "type": "t-shirt" }
  },
  "aggs": {
    "all_products": {
      "global": {}, 
      "aggs": {     
      "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68556524

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档