MongoDB聚合关联Supplier后求和异常:匹配条件失效排查
问题分析与解决
问题背景
拥有Transaction和Supplier两个MongoDB集合,想要聚合指定国家境内外的交易总金额,以此对比进口与国内采购情况,但关联Supplier集合后匹配条件失效,返回的是所有交易的总金额。
原聚合代码:
let aggs = [ { $match : { type : "out" }, }, { $lookup: { from:"suppliers", let: { country : "$location.country" }, localField:"supplier", foreignField:"_id", as:"suppliers", pipeline: [ { $match: { $expr: { $eq: [ '$suppliers.location.country', "United Kingdom" ] } }, }, ] } }, { $group: { _id: null, total: { $sum : { $divide: ['$price', 100] } } }, } ];
Transaction Schema:
const TransactionSchema = mongoose.Schema({ item: {type: String, required: true }, category: {type: String }, type: { type: String, enum: ['in', 'out'] }, price: { type: Number, required: true, get: getPrice, set: setPrice }, description: { type: String }, supplier: { type: mongoose.SchemaTypes.ObjectId, ref: "Supplier" }, customer: { type: mongoose.SchemaTypes.ObjectId, ref: "Customer" }, project: { type: String }, invoice_ref: { type: String }, payment_method: { type: String }, parent_transaction: { type: mongoose.SchemaTypes.ObjectId, ref: "Transaction" }, date: { type: Date }, createdAt: { type: Date, default: Date.now(), immutable: true }, updatedAt: { type: Date, default: Date.now() } });
Supplier Schema:
const SupplierSchema = mongoose.Schema({ name: { type: String, unique: true }, description: { type: String }, website: { type: String }, email: { type: String }, location: { address: { type: String }, town: { type: String }, county: { type: String }, postcode: { type: String }, country: { type: String } } });
错误原因
- $lookup子管道匹配路径错误:在lookup的子管道中,当前处理的是
suppliers集合的单条文档,所以应该用$location.country而非$suppliers.location.country——$suppliers是你定义的输出数组名,子管道上下文里不存在这个字段。 - 多余且无效的
let定义:你定义了let: { country : "$location.country" },但Transaction集合根本没有location字段(该字段属于Supplier),这个定义完全没用,还会造成逻辑混淆。 - 未过滤无匹配供应商的交易:即使lookup没有找到符合条件的供应商,原Transaction文档仍会被保留,只是
suppliers数组为空,这些不符合条件的交易也会被计入总和,必须过滤掉这类文档。
修正后的代码
仅统计UK供应商交易总额
let aggs = [ { $match : { type : "out" } }, { $lookup: { from:"suppliers", localField:"supplier", foreignField:"_id", as:"suppliers", pipeline: [ { $match: { "location.country": "United Kingdom" } } ] } }, // 过滤掉未匹配到符合条件供应商的交易 { $match: { suppliers: { $ne: [] } } }, { $group: { _id: null, total: { $sum : { $divide: ['$price', 100] } } } } ];
同时统计境内(UK)与境外(非UK)交易总额
如果需要一次性对比两类交易的总额,可以用以下逻辑:
let aggs = [ { $match : { type : "out" } }, { $lookup: { from:"suppliers", localField:"supplier", foreignField:"_id", as:"supplierInfo" } }, // 展开供应商信息(lookup返回数组,需转为单个对象) { $unwind: "$supplierInfo" }, // 根据供应商国家分组统计 { $group: { _id: { isUK: { $eq: ["$supplierInfo.location.country", "United Kingdom"] } }, total: { $sum : { $divide: ['$price', 100] } } } }, // 重命名字段让结果更直观 { $project: { _id: 0, type: { $cond: ["$_id.isUK", "国内采购(UK)", "进口采购(非UK)"] }, total: 1 } } ];
内容的提问来源于stack exchange,提问作者Ashley Finlayson
相关产品推荐
相关产品推荐

