MongoDB如何单次查询合并view与click两个集合的统计结果
解决方案
你可以用MongoDB的$unionWith聚合阶段实现单次查询拿到合并后的统计结果,无需业务层自行合并数据,性能和可维护性都优于两次查询的方案。
核心思路
- 先对
view集合做条件过滤,给每条符合条件的记录加类型标记,区分是浏览数据 - 用
$unionWith把click集合的符合条件数据也合并到聚合流程中,同样加点击类型标记 - 统一按日期(和发布商ID,按需选择)分组,用条件求和分别统计浏览量和点击量
单发布商查询代码
以查询发布商ID为1的结果为例,查询语句如下:
db.view.aggregate([ // 过滤view集合的符合条件数据 { $match: { publisher_id: 1, created_at: {$gte: ISODate("2021-11-01"), $lt: ISODate("2021-11-28")} } }, // 给view数据加类型标记 { $addFields: { type: "view" } }, // 合并click集合的符合条件数据 { $unionWith: { coll: "click", pipeline: [ { $match: { publisher_id: 1, created_at: {$gte: ISODate("2021-11-01"), $lt: ISODate("2021-11-28")} } }, { $addFields: { type: "click" } } ] } }, // 按日期分组统计两个指标 { $group: { _id: {$dateToString: {format: "%Y/%m/%d", date: "$created_at"}}, view_count: { $sum: {$cond: [{$eq: ["$type", "view"]}, 1, 0]} }, click_count: { $sum: {$cond: [{$eq: ["$type", "click"]}, 1, 0]} } } }, // 可选:按日期升序排序 { $sort: {_id: 1} } ])
执行后直接返回你需要的_id(日期)、view_count、click_count结构的结果。
全发布商批量查询代码
如果需要一次性查询所有发布商的统计结果,只需调整分组维度即可:
db.view.aggregate([ { $match: { created_at: {$gte: ISODate("2021-11-01"), $lt: ISODate("2021-11-28")} } }, { $addFields: {type: "view"} }, { $unionWith: { coll: "click", pipeline: [ { $match: { created_at: {$gte: ISODate("2021-11-01"), $lt: ISODate("2021-11-28")} } }, {$addFields: {type: "click"}} ] } }, // 分组维度增加发布商ID { $group: { _id: { date: {$dateToString: {format: "%Y/%m/%d", date: "$created_at"}}, publisher_id: "$publisher_id" }, view_count: {$sum: {$cond: [{$eq: ["$type", "view"]},1,0]}}, click_count: {$sum: {$cond: [{$eq: ["$type", "click"]},1,0]}} } } ])
方案优势
- 仅发起一次数据库请求,减少网络开销,避免业务层数据合并的逻辑错误
- 所有计算在数据库层完成,MongoDB原生聚合的性能远高于业务层遍历合并数据,数据量越大优势越明显
- 统计维度调整灵活,如需按周/月统计、增加其他筛选条件,只需修改对应阶段的逻辑,无需维护多套查询语句
注意:
$unionWith是MongoDB 4.4及以上版本支持的特性,如果你使用的是更早版本,可以通过$lookup关联两个集合后再统计,性能会稍差一些。
内容的提问来源于stack exchange,提问作者AungPyaeSone
相关产品推荐
相关产品推荐

