MongoDB聚合框架下将数组转换为带计数对象的实现疑问
用单一MongoDB聚合管道实现统计结果的键值对转换
当然可以通过单一聚合管道完成你想要的转换,不需要额外脚本处理!我们只需要在现有管道基础上补充几个分组和转换阶段,就能把数组格式的统计项转换成你期望的键值对对象结构。
完整聚合管道方案
[ // 1. 过滤指定时间范围的记录 { "$match": { "date_added": { "$gte": ISODate("2019-12-25T19:00:00.000Z") } } }, // 2. 先按主维度+各统计维度分组,统计每个细分项的数量 { "$group": { "_id": { "object_id": "$object_id", "object_type": "$object_type", "action": "$action", "date": { "$dateToString": { "format": "%Y-%m-%d %H:00:00", "date": "$date_added" } }, "country": "$country", "browser": "$browser", "os": "$os", "device": "$device", "category": "$category" }, "count": { "$sum": 1 } } }, // 3. 再按主维度分组,收集各统计维度的键值对数组 { "$group": { "_id": { "object_id": "$_id.object_id", "object_type": "$_id.object_type", "action": "$_id.action", "date": "$_id.date" }, "total": { "$sum": "$count" }, "countries": { "$push": { "k": "$_id.country", "v": "$count" } }, "browsers": { "$push": { "k": "$_id.browser", "v": "$count" } }, "systems": { "$push": { "k": "$_id.os", "v": "$count" } }, "devices": { "$push": { "k": "$_id.device", "v": "$count" } }, "categories": { "$push": { "k": { "$toString": "$_id.category" }, "v": "$count" } } } }, // 4. 将数组转换为键值对对象,并整理输出字段 { "$project": { "_id": 0, "object_id": "$_id.object_id", "object_type": "$_id.object_type", "action": "$_id.action", "date": "$_id.date", "total": 1, "countries": { "$arrayToObject": "$countries" }, "browsers": { "$arrayToObject": "$browsers" }, "systems": { "$arrayToObject": "$systems" }, "devices": { "$arrayToObject": "$devices" }, "categories": { "$arrayToObject": "$categories" } } }, // 5. 按总数降序排序 { "$sort": { "total": -1 } } ]
关键步骤解释
- 二级分组:第一步
$group不仅按主维度(对象ID、类型、操作、小时日期)分组,还把每个统计维度(国家、浏览器等)也加入分组键,这样就能统计出每个细分项的独立数量。 - 收集键值对数组:第二步
$group回到主维度分组,用$push把每个细分项的键(k)和值(v)收集成数组,比如countries会变成[{k: "gb", v:1}, {k:"pe", v:1}]这样的结构。 - 数组转对象:最后用
$arrayToObject操作符,把上述数组直接转换成你需要的键值对对象。注意category是数字类型,需要用$toString把它转成字符串作为对象的键,避免MongoDB的类型限制。
关于脚本处理的备选方案
如果出于某些原因(比如老版本MongoDB不支持$arrayToObject,该操作符从3.4版本开始支持),你也可以先用现有管道返回数组格式的统计结果,再用脚本(比如Node.js、Python)把数组转换成键值对对象。但这种方式需要把更多数据传输到客户端处理,效率远不如直接在数据库端用聚合管道完成转换。
内容的提问来源于stack exchange,提问作者Fawaz Tahir
相关产品推荐
相关产品推荐

