MongoDB 3.6关联子文档实现左连接统计购买量需求求助
解决MongoDB 3.6中用户-门店-商品购买次数统计的聚合查询问题
刚接触MongoDB聚合确实容易懵,我一步步给你拆解怎么实现这个需求。首先得明确我们的目标:把users里的购买记录关联到products和stores集合拿到名称,再按用户、门店、商品分组统计购买次数。
先明确集合结构(补全你没写全的部分)
根据你的描述,我假设几个集合的关键结构是这样的(如果和实际有出入,你调整字段名就行):
- users集合:每个用户的
products数组里存的是购买条目,每个条目包含storeId(关联stores的id)、productId(关联products的id),还有purchaseCount(单次购买的数量;要是每次购买是一条独立记录,这个字段可以去掉,后面用$sum:1统计次数){ "_id": ObjectId("xxx"), "userId": "u001", "email": "user001@example.com", "products": [ { "storeId": "s001", "productId": "p001", "purchaseCount": 2 }, { "storeId": "s002", "productId": "p002", "purchaseCount": 1 } ] } - stores集合:用
id字段唯一标识门店,包含name等基础信息 - products集合:用
id字段唯一标识商品,包含name等基础信息
聚合查询分步实现
下面的聚合管道是针对MongoDB 3.6写的,每一步我都给你解释清楚:
db.users.aggregate([ // 1. 把用户的products数组拆成单独文档 // 要是想保留没买过东西的用户,加上preserveNullAndEmptyArrays: true { $unwind: { path: "$products", preserveNullAndEmptyArrays: true } }, // 2. 左连接products集合,通过productId获取商品信息 { $lookup: { from: "products", // 要关联的目标集合 localField: "products.productId", // users里的关联字段 foreignField: "id", // products里的匹配字段 as: "productInfo" // 关联结果存到这个字段(数组类型) } }, // 3. 把productInfo数组拆成单个对象(因为每个productId只会匹配一条商品记录) // 处理没有购买记录的情况,给个友好的默认值 { $addFields: { productInfo: { $ifNull: [ { $arrayElemAt: ["$productInfo", 0] }, // 取数组第一个匹配元素 { name: "无购买商品" } // 没有匹配到的话用默认值 ] } } }, // 4. 左连接stores集合,通过storeId获取门店信息 { $lookup: { from: "stores", localField: "products.storeId", foreignField: "id", as: "storeInfo" } }, // 5. 处理storeInfo数组,同样给默认值 { $addFields: { storeInfo: { $ifNull: [ { $arrayElemAt: ["$storeInfo", 0] }, { name: "无关联门店" } ] } } }, // 6. 分组统计:按用户、门店、商品分组,计算总购买次数 { $group: { _id: { userId: "$userId", userEmail: "$email", storeName: "$storeInfo.name", productName: "$productInfo.name" }, // 如果你的products里没有purchaseCount,就把这里改成$sum:1 totalPurchases: { $sum: { $cond: [ { $ifNull: ["$products.purchaseCount", false] }, "$products.purchaseCount", 0 ] } } } }, // 7. 调整输出格式,把_id里的字段拆出来,去掉多余的_id { $project: { _id: 0, userId: "$_id.userId", userEmail: "$_id.userEmail", storeName: "$_id.storeName", productName: "$_id.productName", totalPurchases: "$totalPurchases" } }, // 可选:按用户、门店、商品排序,结果更整齐 { $sort: { userId: 1, storeName: 1, productName: 1 } } ])
输出格式示例
执行完上面的查询,你会得到类似这样的JSON结果:
[ { "userId": "u001", "userEmail": "user001@example.com", "storeName": "中心店", "productName": "无线鼠标", "totalPurchases": 2 }, { "userId": "u001", "userEmail": "user001@example.com", "storeName": "分店", "productName": "机械键盘", "totalPurchases": 1 }, { "userId": "u002", "userEmail": "user002@example.com", "storeName": "无关联门店", "productName": "无购买商品", "totalPurchases": 0 } ]
关键知识点说明
- $unwind:把数组拆成单个文档,这样每个购买条目都能单独关联其他集合;加上
preserveNullAndEmptyArrays: true可以保留没有购买记录的用户。 - $lookup:MongoDB里的左连接,对应关系型数据库的
LEFT JOIN,3.6版本只支持单字段匹配,所以要确保localField和foreignField是对应的唯一标识。 - $group:用来分组统计,
_id里的字段就是分组的依据,$sum用来计算总次数。 - $addFields + $ifNull:处理关联不到数据的情况,给个友好的默认值,避免结果里出现空数组或null。
如果你的集合字段名和我假设的不一样,比如users里的门店字段不是storeId而是stores,直接把localField改成$products.stores就行,其他逻辑不变。
内容的提问来源于stack exchange,提问作者user1790300
相关产品推荐
相关产品推荐

