You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过date_histogram统计年度内每月首次出现的唯一用户

计算年度内每月首次出现的唯一用户(避免重复统计)

你要统计指定年份里每个月首次出现的唯一用户数——就是说用户只要在当年某个月份被统计过,后续月份就不能再计入。原来用date_histogram加cardinality的方法会重复统计跨月活跃的用户,而scripted_metric虽然结果对,但内存占得太高,大数据集下根本跑不动。下面给两个性能更优的解决方案:

方案1:用Elasticsearch Transform预处理数据(性能最优,首选)

先通过Transform预计算每个用户在目标年份的首次访问月份,之后直接基于预处理好的索引做聚合查询,这是大数据场景下最省心且高效的方式。

步骤1:创建Transform

这个Transform会给每个用户生成一条记录,包含用户ID和他当年第一次访问的月份:

PUT _transform/first_seen_month_transform
{
  "source": {
    "index": ["你的源索引名"],
    "query": {
      "range": {
        "date": {
          "gte": "2024-01-01",
          "lte": "2024-12-31"
        }
      }
    }
  },
  "dest": {
    "index": "user_first_seen_2024"
  },
  "pivot": {
    "group_by": {
      "user": {
        "terms": {
          "field": "user"
        }
      }
    },
    "aggregations": {
      "first_seen_month": {
        "min": {
          "field": "date",
          "format": "yyyy-MM"
        }
      }
    }
  },
  "sync": {
    "time": {
      "field": "date",
      "delay": "1h"
    }
  }
}

步骤2:启动Transform

POST _transform/first_seen_month_transform/_start

步骤3:查询预处理后的索引

直接按first_seen_month聚合统计数量,结果完全符合你的需求,而且速度极快:

GET user_first_seen_2024/_search
{
  "size": 0,
  "aggs": {
    "monthly_counts": {
      "terms": {
        "field": "first_seen_month",
        "order": {
          "_key": "asc"
        }
      }
    }
  }
}

返回的结果格式大概是这样,和你期望的结构很接近,稍作整理就能用:

"aggregations": {
  "monthly_counts": {
    "buckets": [
      {
        "key": "2024-01",
        "doc_count": 4
      },
      {
        "key": "2024-02",
        "doc_count": 4
      },
      {
        "key": "2024-03",
        "doc_count": 3
      },
      {
        "key": "2024-04",
        "doc_count": 2
      },
      {
        "key": "2024-05",
        "doc_count": 1
      }
    ]
  }
}

方案2:单查询直接实现(无需预处理,适合小数据集)

如果不想做预处理,也可以用嵌套聚合直接查,但这个方法只适合用户量不大的场景——因为要把所有用户都拉出来统计:

GET 你的源索引名/_search
{
  "size": 0,
  "query": {
    "range": {
      "date": {
        "gte": "2024-01-01",
        "lte": "2024-12-31"
      }
    }
  },
  "aggs": {
    "users": {
      "terms": {
        "field": "user",
        "size": 10000  // 这里要设足够大,能覆盖所有用户,不然会丢数据
      },
      "aggs": {
        "first_seen": {
          "min": {
            "field": "date",
            "format": "yyyy-MM"
          }
        }
      }
    },
    "monthly_new_users": {
      "terms": {
        "script": "doc['first_seen'].value",
        "order": {
          "_key": "asc"
        }
      }
    }
  }
}

为啥原来的方案不行?

你之前用的date_histogram加cardinality,统计的是每个月的独立活跃用户——不管用户之前有没有出现过,只要当月有活动就算进去,自然不符合“只统计首次出现”的要求。而scripted_metric要在内存里存所有已经出现过的用户,数据量一大内存就爆,性能肯定拉胯。


内容的提问来源于stack exchange,提问作者Sheel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 05:12:37