MongoDB:select所有字段+子集合中所有匹配的元素

这里是我的mongoosecollections的一个文件:

{ _id: 55ae7be99e772a7c025a0a7b, id: 'foo', isBoolean: true, kind: 'bar', values: [ { y: 0, x: Wed Aug 26 2015 11:12:56 GMT+0200 (Mitteleuropäische Sommerzeit), _id: 55ae7ae05596dd740eb8a204 }, { y: 0, x: Wed Aug 26 2015 11:12:57 GMT+0200 (Mitteleuropäische Sommerzeit), _id: 55ae7ae05596dd740eb8a203 }, { y: 1, x: Wed Aug 26 2015 11:12:56 GMT+0200 (Mitteleuropäische Sommerzeit); _id: 55ae7be91fa1511c1795c5ae } ] } 

所以,我需要find所有具有特定值的文档。 之后,我需要返回与所有领域的文件,并find价值元素。


Wenn我尝试了

 .find({'values.x': mTime1}) .select({ '_id' : 1 , 'id' : 1 , 'kind' : 1 , 'isBoolean' : 1 , 'values' : {$elemMatch: {x: time1}} }) 

我只收到第一个发现的价值:

  { ... values: [ { exceeds: null, y: 0, x: Wed Aug 26 2015 11:12:56 GMT+0200 (Mitteleuropäische Sommerzeit), _id: 55ae7d86870b92b8056bed4c } ] } 

下一个版本

 .aggregate({"$unwind" : "$values"}, {"$match" : {"values.x": time1}}, {"$group" : { '_id' : '$_id', 'values' : {$addToSet: "$values"} }); 

返回除其他字段以外的所有匹配值…


我的目标是:

 { _id: 55ae7be99e772a7c025a0a7b, id: 'foo', isBoolean: true, kind: 'bar', values: [ { y: 0, x: Wed Aug 26 2015 11:12:56 GMT+0200 (Mitteleuropäische Sommerzeit), _id: 55ae7ae05596dd740eb8a204 }, { y: 1, x: Wed Aug 26 2015 11:12:56 GMT+0200 (Mitteleuropäische Sommerzeit); _id: 55ae7be91fa1511c1795c5ae } ] } 

你有什么想法,如何实现与mongoose? 🙂


更新:感谢tsturzl ,我解决了它与下一个function(不更改模型):

  self.aggregate( {'$unwind' : '$values'}, {'$match' : { 'values.x': mTime1} }, {'$group' : { '_id' : '$_id', 'values' : {$push: '$values'} }} ) .exec( function(err, results) { if(err) return done(err); var values = {}; // hashMap to group values results.forEach(function(item) { if(item.values) // prevent empty results values[item._id] = item.values; }); self.find({_id:{$in: _.keys(values)}}) .exec(function(err, items) { if(err) return done(err); var results = items.map(function(item) { item.values = values[item._id]; return item; }); done(err, results); // callback results }); }); 

在投影中使用elemMatch的问题是它访问单个项目。 与数组arr[1]类似,elemMatch获取数组中项目的索引,然后在该索引处投影该数组项目。 所以你只能使用这种方法检索一个子文档。

您可以使用与此类似的聚合

 [ {$match: {'values.x': mTime1}}, //match before to reduce size of unwound {$unwind: '$values'}, {$match: {'values.x': mTime1}}, {$group: { _id: '$_id', id: {$first: '$id'}, kind: {$first: '$kind'}, isBoolean: {$first: '$isBoolean'}, values: {$push: '$values'} } ] 

我已经testing了这一点在一个子文档数组上正常工作。

您的方法可能最适合重组。 您应该重新编制数据,以便您的值具有自己的收集和参考_id 。 在这个原因中,我会将参考值存储在值集合中。

从此集合中删除values字段

 { _id: 55ae7be99e772a7c025a0a7b, id: 'foo', isBoolean: true, kind: 'bar' } 

价值观型号:

 { y: Number, x: Date, parentId: {type: ObjectId, ref: "myColl"} //make sure you require ObjectId and rename the reference } 

然后你可以做这样的事情

 ValuesModel.find({ x: mTime1 }).exec(function(err, results) { var ids = {}; //hashMap to group values var idsArr = []; //array of ids results.forEach(function(item) { if(!ids.hasOwnProperty(items.parentId.toString())) { ids[items.parentId.toString()] = []; idArr.push(item.parentId); } ids[items._id.toString()].push(item); }); myColl.find({_id:{$in: idsArr}}) .exec(function(err, items) { var results = items.map(function(item) { item.values = ids[item._id.toString()]; return item; }); done(results); //callback results }); }); 

这将获取您查询的所有值,然后将它们分组在hashMap中,并将所有parentIds推送到一个数组。 然后我查询这个parentIds数组。 我把.values ,由.values中的id引用它,并在父文档中为.values创build一个新的字段。 这将阻止您使用不可扩展的聚合,并允许您轻松查询值表。 如果你只想find一个值,你可以简单地使用mongoose填充方法。 这种方法的缺点是你需要在代码中做更多的工作,而且你有两次往返。 但是,这应该仍然比聚合更有效率。

这可以用来创build一个可重用的方法来简化你的代码,如果你查询的价值很多

 function queryValues(query, done) { ValuesModel.find(query).exec(function(err, results) { if(err) return done(err); var ids = {}; //hashMap to group values var idsArr = []; //array of ids results.forEach(function(item) { if(!ids.hasOwnProperty(items.parentId.toString())) { ids[items.parentId.toString()] = []; idArr.push(item.parentId); } ids[items._id.toString()].push(item); }); myColl.find({_id:{$in: idsArr}}) .exec(function(err, items) { if(err) return done(err); var results = items.map(function(item) { item.values = ids[item._id.toString()]; return item; }); done(null, results); //callback results }); }); } 

然后你可以调用queryValues({x: mTime1}, function(err, results){...}); ,你可以传递任何你想要的查询,并且函数将处理填充父文档,而不用获取重复的数据以获得最大的效率。

我还可以推荐的一件事是,在模型定义中将此方法定义为模式静态方法,以便您可以将代码放在一边,而不必担心。 请参阅: http : //mongoosejs.com/docs/api.html#schema_Schema-static