var orderSchema = new Schema({ orderDate: Date,articles: [{ article: { type: Schema.Types.ObjectId,ref: 'Article' },quantity: 'Number' }] }),Order = mongoose.model('Order',orderSchema);
虽然我设法成功查询参考,即:
Order.find({}).populate('articles.article',null,{ price: { $lte: 500 } }).exec(function(err,data) { for (var order of data) { for (var article of order.articles) { console.log(article); } } });
Order.find({}).where({ 'articles.quantity': { $gte: 5 } }).populate('articles.article',{ /*price: { $lte: 500 }*/ }).exec(function(err,data) { for (var order of data) { for (var article of order.articles) { console.log(article); } } });
谢谢!
更新:
问题是,结果是一个完整的数组,或者什么都没有(请参阅更新的问题).我想只获得那些数量大于或等于5的记录.使用你的(和我的)方法,我得到根本没有记录(如果我设置$gte:5001)或两个记录(如果我设置$gte: 5000)
{ "_id": ObjectId('56fe76c12f7174ac5018054f'),"orderDate": ISODate('2016-04-01T13:25:21.055Z'),"articles": [ { "article": ObjectId('56fe76c12f7174ac5018054b'),"quantity": 5000,"_id": ObjectId('56fe76c12f7174ac50180551') },{ "article": ObjectId('56fe76c12f7174ac5018054c'),"quantity": 1,"_id": ObjectId('56fe76c12f7174ac50180552') } ],"__v": 1 }
解决方法
因此,过滤“数组”与您拥有的“查询”条件不同.
一个简单的“投影”只会将“第一个”匹配的项目返回到该条件.所以它可能不是你想要的,但作为一个例子:
Order.find({ "articles.quantity": { "$gte": 5 } }) .select({ "articles.$": 1 }) .populate({ "path": "articles.article","match": { "price": { "$lte": 500 } } }).exec(function(err,orders) { // populated and filtered twice } )
那种“有点”做你想要的,但问题实际上只会返回“articles”数组中的最多一个元素.
要正确执行此操作,您需要.aggregate()来过滤数组内容.理想情况下,这是通过MongoDB 3.2和$filter
完成的.但是这里还有一种特殊的方法来.populate():
Order.aggregate( [ { "$match": { "artciles.quantity": { "$gte": 5 } } },{ "$project": { "orderdate": 1,"articles": { "$filter": { "input": "$articles","as": "article","cond": { "$gte": [ "$$article.quantity",5 ] } } },"__v": 1 }} ],function(err,orders) { Order.populate( orders.map(function(order) { return new Order(order) }),{ "path": "articles.article","match": { "price": { "$lte": 500 } } },orders) { // Now it's all populated and mongoose documents } ) } )
所以这里发生的是数组的实际“过滤”发生在.aggregate()语句中,但当然结果不再是“mongoose文档”,因为.aggregate()的一个方面是它可以“改变“文档结构,因此mongoose”假设“就是这种情况,只返回一个”普通对象“.
这不是一个真正的问题,因为当你看到$project阶段时,我们实际上要求根据定义的模式存在于文档中的所有相同字段.因此即使它只是一个“普通对象”,也没有问题将其“转换”回到一个mongoose文档中.
这是.map()的用武之地,因为它返回一个转换后的“文档”数组,这对下一个阶段很重要.
现在你调用Model.populate()
,然后可以在“mongoose文档数组”上运行更多的“人口”.
结果最终是你想要的.
MongoDB早于3.2.x的旧版本
这里真正改变的唯一事情是聚合管道,所以为了简洁,所有这些都需要包含在内.
MongoDB 2.6 – 可以使用$map
和$setDifference
的组合过滤数组.结果是“set”但是当默认情况下mongoose在所有子文档数组上创建_id字段时这不是问题:
[ { "$match": { "artciles.quantity": { "$gte": 5 } } },"articles": { "$setDiffernce": [ { "$map": { "input": "$articles","in": { "$cond": [ { "$gte": [ "$$article.price",5 ] },"$$article",false ] } }},[false] ] },
较旧的修订必须使用$unwind
:
[ { "$match": { "artciles.quantity": { "$gte": 5 } }},{ "$unwind": "$articles" },{ "$match": { "artciles.quantity": { "$gte": 5 } }},{ "$group": { "_id": "$_id","orderdate": { "$first": "$orderdate" },"articles": { "$push": "$articles" },"__v": { "$first": "$__v" } }} ],
$lookup替代品
另一种替代方法是在“服务器”上执行所有操作.这是MongoDB 3.2及更高版本$lookup
的一个选项:
Order.aggregate( [ { "$match": { "artciles.quantity": { "$gte": 5 } }},"__v": 1 }},{ "$lookup": { "from": "articles","localField": "articles.article","foreignField": "_id","as": "articles.article" }},{ "$unwind": "$articles.article" },"__v": { "$first": "$__v" } }},"cond": { "$lte": [ "$$article.article.price",500 ] } } },orders) { } )
虽然这些只是简单的文档,但它与.populate()方法的结果相同.当然,如果你真的必须的话,你可以在所有情况下再次“演员”到mongoose文件.
“最短”的路径
这实际上可以追溯到原始语句,您基本上只是“接受”“查询”并不意味着“过滤”数组内容. .populate()可以愉快地这样做,因为它只是另一个“查询”,并且方便地填充在“文档”中.
因此,如果您真的没有通过删除原始文档数组中的其他数组成员来保存bandwith的“bucketloads”,那么只需.filter()将它们放在后处理代码中:
Order.find({ "articles.quantity": { "$gte": 5 } }) .populate({ "path": "articles.article",orders) { orders = orders.filter(function(order) { order.articles = order.articles.filter(function(article) { return ( ( article.quantity >= 5 ) && ( article.article != null ) ) }); return order.aricles.length > 0; }) // orders has non matching entries removed } )