PyMongo按子文档日期的年份分组统计点赞、点踩与复制数据
PyMongo按子文档日期的年份分组统计点赞、点踩与复制数据
你遇到的错误原因很明确:$conversation.feedback.created_at是数组类型的字段(因为conversation本身是数组,每个元素都有独立的feedback),直接用$dateToString转换数组肯定会报错——MongoDB不知道该处理数组里的哪一个日期。
要解决这个问题,我们得先把conversation数组展开,让每个子项成为独立的处理单元,再进行统计。下面是完全贴合你需求的解决方案:
正确的聚合管道
pipeline = [ # 1. 展开conversation数组,每个子文档单独成一条记录 {"$unwind": "$conversation"}, # 2. 过滤掉无feedback的无效记录 {"$match": {"conversation.feedback": {"$ne": None}}}, # 3. 从feedback的创建日期中提取年份,用于后续分组 {"$addFields": { "feedback_year": { "$dateToString": { "format": "%Y", "date": "$conversation.feedback.created_at" } } }}, # 4. 按年份分组,分别统计三类操作的总数 {"$group": { "_id": "$feedback_year", "total_liked": { "$sum": {"$cond": ["$conversation.feedback.liked", 1, 0]} }, "total_disliked": { "$sum": {"$cond": ["$conversation.feedback.disliked", 1, 0]} }, "total_copied": { "$sum": {"$cond": ["$conversation.feedback.copied", 1, 0]} } }}, # 5. 转换为你期望的键值对格式 {"$group": { "_id": None, "yearly_stats": {"$push": {"k": "$_id", "v": { "total_liked": "$total_liked", "total_disliked": "$total_disliked", "total_copied": "$total_copied" }}} }}, {"$replaceRoot": { "newRoot": {"$arrayToObject": "$yearly_stats"} }} ] # 执行聚合查询 result = list(collection.aggregate(pipeline))
各阶段详细说明
$unwind:这是解决问题的核心步骤——把每个主文档里的conversation数组拆分成多条独立记录,让每个子项的feedback能被单独处理,避免了数组类型的日期转换错误。$match:过滤掉feedback为null的记录,这些条目没有统计价值,留着只会干扰结果。$addFields:从有效feedback的created_at中提取年份,生成新字段feedback_year,为后续分组做准备。$group:按年份分组,用$cond条件判断配合$sum完成统计:当liked为true时加1,否则加0,以此类推完成三类操作的计数。- 最后两个阶段:把分组结果转换成你想要的
{"年份": {统计指标}}键值对格式,让输出更直观易懂。
最终输出结果
运行这个管道后,你会得到完全符合预期的结果:
[ { "2025": { "total_liked": 1, "total_disliked": 1, "total_copied": 1 } } ]
额外提示
如果你的数据跨多个年份,这个管道会自动按年份分别统计;若某一年份只有部分指标有数据,对应的统计值会自动显示为0(比如某一年没有点赞记录,total_liked就为0)。
备注:内容来源于stack exchange,提问作者NPatel
相关产品推荐
相关产品推荐

