如何在Elasticsearch中自动计算近一个月用户次日留存率?
计算Elasticsearch中近一个月的用户次日留存率
要实现近一个月每日的次日留存率计算,核心是通过**日期直方图聚合(date_histogram)**按用户注册日期(create_time)按天分组,在每个日期桶内完成「当日注册用户数」和「次日登录用户数」的统计,最后通过桶脚本计算留存率。
完整查询DSL
{ "size": 0, "query": { "bool": { "filter": [ { "range": { "create_time": { "gte": "now-30d/d", "lt": "now/d" } } } ] } }, "aggs": { "daily_registrations": { "date_histogram": { "field": "create_time", "calendar_interval": "day", "format": "yyyy-MM-dd", "min_doc_count": 1 }, "aggs": { // 统计当日注册的唯一用户数 "registered_users": { "cardinality": { "field": "username" } }, // 筛选出当日注册且次日登录的用户 "retained_users": { "filter": { "script": { "source": """ def regDate = doc['create_time'].value.toLocalDate(); def loginDate = doc['update_time'].value.toLocalDate(); return loginDate.equals(regDate.plusDays(1)); """ } }, "aggs": { "retained_count": { "cardinality": { "field": "username" } } } }, // 计算次日留存率,处理除数为0的情况 "next_day_retention": { "bucket_script": { "buckets_path": { "retained": "retained_users>retained_count", "registered": "registered_users" }, "script": { "source": "params.registered == 0 ? 0 : params.retained / params.registered" }, "format": "0.00%" } } } } } }
关键部分说明
- 日期范围过滤:通过
query中的range限定create_time为近30天(now-30d/d表示30天前的凌晨,now/d表示今天凌晨),只统计这段时间的注册用户。 - 日期直方图聚合:
daily_registrations按天拆分注册日期,min_doc_count:1确保只返回有注册用户的日期桶。 - 次日登录筛选:使用脚本判断用户的
update_time是否为注册日期的次日,精准匹配次日登录的用户。 - 留存率计算:
bucket_script中加入除数为0的判断,避免出现NaN错误,同时用format将结果转为百分比格式。
输出示例
返回结果会按日期分组,每个桶包含当日注册用户数、次日留存用户数和留存率:
{ "aggregations": { "daily_registrations": { "buckets": [ { "key_as_string": "2025-05-23", "key": 1747900800000, "doc_count": 2, "registered_users": { "value": 2 }, "retained_users": { "doc_count": 1, "retained_count": { "value": 1 } }, "next_day_retention": { "value": 0.5, "value_as_string": "50.00%" } } // 其他日期桶... ] } } }
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

