如何在MongoDB中创建匹配嵌套值与父文档值的过滤器?
MongoDB文档内嵌套字段匹配查询问题
示例文档结构
{ "_id" : (guid), "basePrice" : 5000, "productCategories" : [{ "_id" : (Guid), "categoryName" : "", "isActive" : true, "products" : [{ "_id" : (guid), "productCode" : "GoKart", "price" : 5000 }] }] }
查询需求
获取满足以下条件的文档:
- 存在处于激活状态(
isActive: true)的productCategory - 该激活分类下存在
productCode为"GoKart"的产品,且产品的price等于父文档的basePrice
尝试过的无效写法
1. LINQ Where写法
.Where( x => x.ProductCategories.Any(pc => pc.IsActive && pc.Products.Any(p => p.Type.ShortCode == "GoKart" && p.Price == x.BasePrice ) ) )
问题:MongoDB驱动不支持在嵌套的Any表达式中引用根文档的x.BasePrice,无法转换为有效查询。
2. FilterBuilder结合$expr的错误写法
filterBuilder.And( Builders<Inventory>.Filter.ElemMatch(inv => inv.ProductCategorys, Builders<ProductCategory>.Filter.And( Builders<ProductCategory>.Filter.Eq(pc => pc.IsActive, true), Builders<ProductCategory>.Filter.ElemMatch(pc => pc.Products, Builders<Product>.Filter.Eq(p => p.ShortCode, "GoKart") ) ) ), Builders<Inventory>.Filter.Eq("$expr", Builders<Inventory>.Filter.Eq( "$basePrice" , "$ProductCategories.Products.Price" ) ) );
问题:$ProductCategories.Products.Price是二维数组结构,直接与$basePrice比较永远不相等,且$expr的使用方式不符合MongoDB聚合语法。
正确解决方案
由于需要在文档内部进行跨层级字段比较,必须使用MongoDB的聚合表达式$expr结合数组遍历操作符来实现。
原生MongoDB查询语句
db.inventory.find({ $expr: { $anyElementTrue: { $map: { input: "$productCategories", as: "pc", in: { $and: [ "$$pc.isActive", { $anyElementTrue: { $map: { input: "$$pc.products", as: "p", in: { $and: [ { $eq: ["$$p.productCode", "GoKart"] }, { $eq: ["$$p.price", "$basePrice"] } ] } } } } ] } } } } })
对应的C# FilterBuilder实现
var filter = Builders<Inventory>.Filter.Expr( new BsonDocument("$anyElementTrue", new BsonDocument("$map", new BsonDocument { { "input", "$productCategories" }, { "as", "pc" }, { "in", new BsonDocument("$and", new BsonArray { "$$pc.isActive", new BsonDocument("$anyElementTrue", new BsonDocument("$map", new BsonDocument { { "input", "$$pc.products" }, { "as", "p" }, { "in", new BsonDocument("$and", new BsonArray { new BsonDocument("$eq", new BsonArray { "$$p.productCode", "GoKart" }), new BsonDocument("$eq", new BsonArray { "$$p.price", "$basePrice" }) } ) } } ) ) } ) } } ) ) ); // 执行查询 var matchingDocuments = inventoryCollection.Find(filter).ToList();
逻辑说明
- 使用
$map遍历外层的productCategories数组,对每个分类判断是否激活 - 对激活的分类,再次用
$map遍历其products数组,判断产品的productCode是否为"GoKart"且price等于根文档的basePrice - 两层
$anyElementTrue确保只要存在任意一个符合条件的分类和产品,文档就会被匹配
内容的提问来源于stack exchange,提问作者Neo
相关产品推荐
相关产品推荐

