【问题标题】:Elasticsearch Date AggregationsElasticsearch 日期聚合
【发布时间】:2019-08-22 22:19:04
【问题描述】:

我正在努力整理一个查询,并且可以使用一些帮助。文档很简单,只记录一个用户的登录时间

{
"timestamp":"2019-01-01 13:14:15",
"username":"theuser"
}

我想根据今天的偏移量使用以下规则进行计数,例如 10 天前。

  • 任何最近一次登录时间早于 10 天的用户都被计为“非活动用户”
  • 任何在 10 天前首次登录的用户都被计为“新用户”
  • 其他任何人都被计为“活跃用户”。

我可以使用它来获取每个用户的第一次和最新登录(我发现这也可以通过 top_hits 聚合来完成)

GET mytest/_search?filter_path=**.buckets
{
    "aggs" : {
        "username_grouping" : {
            "terms" : {
                "field" : "username"
            },
            "aggs" : {
                "first_login" : {
                    "min": { "field" : "timestamp" }
                },
                "latest_login" : {
                    "max": { "field" : "timestamp" }
                }
            }
        }
    }
}

我正在考虑将其用作日期范围聚合的来源,但无法正常工作。

这是否可以在一个查询中实现,如果不能,是否可以在单独的查询中计算“非活动用户”和“新用户”计数?

这里有一些示例数据,假设今天的日期是 2019-08-20 并且偏移量为 10 天,这将为每种类型的用户提供 1 的计数

PUT _template/mytest-index-template
{
  "index_patterns": [ "mytest" ],
  "mappings": {
    "properties": {
      "timestamp": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" },
      "username": { "type": "keyword" }
    }
  }
}

POST /mytest/_bulk
{"index":{}}
{"timestamp":"2019-01-01 13:14:15","username":"olduser"}
{"index":{}}
{"timestamp":"2019-01-20 18:55:05","username":"olduser"}
{"index":{}}
{"timestamp":"2019-01-31 09:33:19","username":"olduser"}
{"index":{}}
{"timestamp":"2019-08-16 08:02:43","username":"newuser"}
{"index":{}}
{"timestamp":"2019-08-18 07:31:34","username":"newuser"}
{"index":{}}
{"timestamp":"2019-03-01 09:02:54","username":"activeuser"}
{"index":{}}
{"timestamp":"2019-08-14 07:34:22","username":"activeuser"}
{"index":{}}
{"timestamp":"2019-08-19 06:09:08","username":"activeuser"}

提前致谢。

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    首先,抱歉。这将是一个很长的答案。

    使用Date Range Aggregation 怎么样?

    您可以将“from”和“to”设置为特定字段并“标记”它们。这将帮助您确定谁是老用户和活跃用户。

    我可以这样想:

    {
    "aggs": {
        "range": {
            "date_range": {
                "field": "timestamp",
                "ranges": [
                    { "to": "now-10/d", "key": "old_user" }, #If they have more than 10 days inactive.
                    { "from": "now-10d/d", "to": "now/d", "key": "active_user" } #Ig they have at least logged in in the last 10 days.
                ],
                "keyed": true
            }
        }
    }
    

    第一个对象可以理解为:“所有具有 'timestamp' 字段且相差 10 天或更长时间的文档都是 old_users”。在数学中表示为:

    "from"(空值,我们可以称之为'-infinite')

    第二个对象可以理解为:“所有具有 'timestamp' 字段且相差 10 天或更短的文档都是 active_users”。在数学中表示为:

    "FROM" 10 天前

    好的,我们已经弄清楚如何“标记”您的用户。但是如果你这样运行查询,你会在结果中发现类似这样的内容:

    user1: old_user
    user1: old_user
    user1: active_user
    user2: old_user
    user2: old_user
    user2: active_user
    user2: old_user
    user3: old_user
    user3: active_user
    

    这是因为您将所有时间戳都存储在一个索引中,并且它将在您的所有文档上运行。我假设你只想玩最后一个时间戳。您可以执行以下操作之一:

    1. 玩水桶路径。

    我正在考虑对时间戳字段进行最大聚合,为其创建一个 bucket_path 并在该 bucket_path 上运行 date_range 聚合。这可能是背部的疼痛。如果您有问题,请为此创建另一个问题。

    1. 将字段“is_active”添加到您的文档中。您可以通过两种方式做到这一点:

    2a。每次用户登录时,在后端代码中添加一个脚本来进行比较。像这样:

    #You get the user_value from your back-end code
    {
        "query":{
            "match": {
                "username": user_value
            }
        },
        "_source": "timestamp" #This will only bring the field timestamp
        "size": 1 #This will only bring back one doc
        "sort":[
            { "timestamp" : {"order" : "desc"}} #This will sort the timestamsps descending
        ]
    }
    

    在您的后端获取结果。如果您获得的时间戳超过 10 天,请将值 "is_active": 0 #Or a value you want like 'no' 添加到您即将被索引的文档中。其他情况"is_active": 1 #Or a value you want like 'yes'

    2b。在 logstash 中运行一个脚本来解析信息。这将要求您:

    • 玩转 Ruby 脚本
    • 从后端通过套接字发送信息

    希望这有帮助! :D

    【讨论】:

      【解决方案2】:

      感谢 Kevin,我想我有一个可行的解决方案。而不是使用最大和最小日期,只需获取登录计数并使用基数聚合来获取用户数。我想要的最终数字只是查询返回的三个值的差异。

      GET mytest/_search?filter_path=aggregations.username_groups.buckets.key,aggregations.username_groups.buckets.username_counts.value,aggregations.active_and_inactive_and_new.value
      {
        "size": 0,
        "aggs": {
          "active_and_inactive_and_new": {
            "cardinality": {
              "field": "username"
            }
          },
          "username_groups": {
            "range": {
              "field": "timestamp",
              "ranges": [
                {
                  "to": "now-10d/d",
                  "key": "active_and_inactive"
                },
                {
                  "from": "now-10d/d",
                  "key": "active_and_new"
                }
              ]
            },
            "aggs": {
              "username_counts": {
                "cardinality": {
                  "field": "username"
                }
              }
            }
          }
        }
      }
      

      【讨论】:

      • 这是一个很好的解决方案!请记住,基数会对您的表现产生一点影响(我对此有一些不好的记忆),最大阈值为 40,000。如果您自己的答案解决了您的问题,请考虑接受它,这样人们就不会将此问题视为“未解决”。
      猜你喜欢
      • 2017-12-28
      • 2020-06-28
      • 2016-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-04
      • 2018-01-09
      • 1970-01-01
      相关资源
      最近更新 更多