用mongoose查询嵌套的文件

我知道这个问题已经被问了很多次了,但是我对mongo和mongoose也是一个新鲜的东西,我想不出来了!

我的问题:

我有一个这样的样子:

var rankingSchema = new Schema({ userId : { type : Schema.Types.ObjectId, ref:'User' }, pontos : {type: Number, default:0}, placarExato : {type: Number, default:0}, golVencedor : {type: Number, default:0}, golPerdedor : {type: Number, default:0}, diferencaVencPerd : {type: Number, default:0}, empateNaoExato : {type: Number, default:0}, timeVencedor : {type: Number, default:0}, resumo : [{ partida : { type : Schema.Types.ObjectId, ref:'Partida' }, palpite : [Number], quesito : String }] }); 

这将返回一个这样的文件:

 { "_id" : ObjectId("539d0756f0ccd69ac5dd61fa"), "diferencaVencPerd" : 0, "empateNaoExato" : 0, "golPerdedor" : 0, "golVencedor" : 1, "placarExato" : 2, "pontos" : 78, "resumo" : [ { "partida" : ObjectId("5387d991d69197902ae27586"), "_id" : ObjectId("539d07eb06b1e60000c19c18"), "palpite" : [ 2, 0 ] }, { "partida" : ObjectId("5387da7b27f54fb425502918"), "quesito" : "golsVencedor", "_id" : ObjectId("539d07eb06b1e60000c19c1a"), "palpite" : [ 3, 0 ] }, { "partida" : ObjectId("5387dc012752ff402a0a7882"), "quesito" : "timeVencedor", "_id" : ObjectId("539d07eb06b1e60000c19c1c"), "palpite" : [ 2, 1 ] }, { "partida" : ObjectId("5387dc112752ff402a0a7883"), "_id" : ObjectId("539d07eb06b1e60000c19c1e"), "palpite" : [ 1, 1 ] }, { "partida" : ObjectId("53880ea52752ff402a0a7886"), "quesito" : "placarExato", "_id" : ObjectId("539d07eb06b1e60000c19c20"), "palpite" : [ 1, 2 ] }, { "partida" : ObjectId("53880eae2752ff402a0a7887"), "quesito" : "placarExato", "_id" : ObjectId("539d0aa82fb219000054c84f"), "palpite" : [ 2, 1 ] } ], "timeVencedor" : 1, "userId" : ObjectId("539b2f2930de100000d7356c") } 

我的问题是,第一:如何过滤quesito结果的嵌套文件? 是否可以分页这个结果,因为这个数组将会增加。 最后一个问题,这是一个很好的方法来处理这种情况?

感谢你们 !

如上所述,你的模式意味着你实际上已经embedded了数据,即使你正在存储一个外部引用。 所以不清楚你是在做embedded和引用还是自己embedded。

这里最重要的警告是匹配“文档”和实际过滤数组内容之间的区别。 由于您似乎在讨论对您的数组结果进行“分页”,因此这里主要集中在这样做,但仍然提到警告。

数组中的多个“过滤”匹配需要聚合框架。 您通常可以“投射”数组元素的单个匹配,但是在期望多于一个的情况下需要这样做:

 Ranking.aggregate( [ // This match finds "documents" that "contain" the match { "$match": { "resumo.quesito": "value" } }, // Unwind de-normalizes arrays as documents { "$unwind": "$resumo" }, // This match actually filters those document matches { "$match": { "resumo.quesito": "value" } }, // Skip and limit for paging, which really only makes sense on single // document matches { "$skip": 0 }, { "$limit": 2 }, // Return as an array in the original document if you really want { "$group": { "_id": "$_id", "otherField": { "$first": "$otherField" }, "resumo": { "$push": "$resumo" } }} ], function(err,results) { } ) 

或者使用$map操作符在$project “过滤”MongoDB 2.6。 但是仍然需要$unwind才能“页面”排列位置,但是由于数组首先被“过滤”,因此处理的可能性较小:

 Ranking.aggregate( [ // This match finds "documents" that "contain" the match { "$match": { "resumo.quesito": "value" } }, // Filter with $map { "$project": { "otherField": 1, "resumo": { "$setDifference": [ { "$map": { "input": "$resumo", "as": "el", "in": { "$eq": ["$$el.questio", "value" ] } } }, [false] ] } }}, // Unwind de-normalizes arrays as documents { "$unwind": "$resumo" }, // Skip and limit for paging, which really only makes sense on single // document matches { "$skip": 0 }, { "$limit": 2 }, // Return as an array in the original document if you really want { "$group": { "_id": "$_id", "otherField": { "$first": "$otherField" }, "resumo": { "$push": "$resumo" } }} ], function(err,results) { } ) 

这里$skip$limit的内部用法在处理单个文档时只是有意义,只是对数组进行“过滤”和“分页”。 可以用多个文档来做到这一点,但是非常重要,因为没有办法只是“切片”数组。 这使我们接下来的一点。

真正的embedded式数组,对于不需要任何过滤的分页,您只需使用$slice操作符,该操作符就是为此目的而devise的:

 Ranking.find({},{ "resumo": { "$slice": [0,2] } },function(err,docs) { }); 

您的替代方法是只引用外部集合中的文档,然后将parameter passing给mongoose.populate()来筛选和“分页”结果。 模式本身的变化只是:

  "resumo": [{ "type": "Schema.Types.ObjectId", "ref": "Partida" }] 

现在外部引用的集合保存对象的细节,而不是直接embedded到数组中。 .populate()与过滤和分页的使用是:

  Ranking.find().populate({ "path": "resumo", "match": { "questio": "value" }, "options": { "skip": 0, "limit": 2 } }).exec(function(err,docs) { docs = docs.filter(function(doc) { return docs.comments.length; }); }); 

当然,可能存在的问题是,您现在不能再实际查询包含“embedded”信息的文档,因为它现在在另一个集合中。 这会导致所有文档被拉取,尽pipe可能通过其他查询条件,但是然后手动testing它们以查看它们是否被发送来检索这些项目的过滤查询“填充”。

所以这真的取决于你在做什么,你的方法是什么。 如果你经常打算在内部arrays上“search”,那么embedded通常会更适合你。 另外,如果你真的只在“分页”中感兴趣,那么$slice操作符就可以很好地用于embedded式文档。 但是要注意增长的embedded式数组太大了。

使用带有mongoose的引用模式有助于解决一些大小问题,并且有方法来协助“分页”结果并对其进行过滤。 缺点是,你不能再查询父母本身的那些元素“内部”。 因此,内部元素的父母select在这里不是很合适。 另外请记住,虽然并非所有数据都被embedded,但仍然会引用外部文档的_id值。 所以你仍然可以结束大arrays,这可能是不可取的。

对于任何大事,请考虑你自己可能会做的工作,并从“孩子”项目后退工作,然后匹配父母(S)。

我不确定你可以直接用mongoose过滤子文件。 然而,你可以用Model.find({'resumo.quesito': 'THEVALUE'})获得父文档(你也应该和它的索引)

然后,当你有父母,你可以通过比较quesito来得到孩子

附加doc可以在这里find: http : //mongoosejs.com/docs/subdocs.html