MongoDB使用mongoose驱动程序:如何查询文档的数组属性的子集?

我收集了选民登记资料。 这些forms如下:

voter = { name: "Some name", registrationDate: "2015-10-21T15:41:36+00:00", votingHistory: ["2015-10-21T15:41:36+00:00", "2015-7-21T15:41:36+00:00"] } 

我需要能够检测投票历史数组的一个子集的大小。 例如,这是我试过的一个问题:

 voters.find({ votingHistory : { $all : {$size : { $gt : 8 }}, { $gt : "2015-7-21T15:41:36+00:00" }} }) 

这个查询的意图是在2015年7月21日之后find所有至less有8个logging投票的选民。 有mongoose的方式来查询数组属性的子集的大小吗?

例如,有以下三个条目:

 { name: "name1", VotingHistory: ["2015-10-21T15:41:36+00:00", "2013-7-21T15:41:36+00:00"] }, { name: "name2", VotingHistory: ["2015-10-21T15:41:36+00:00", "2011-7-21T15:41:36+00:00"] }, { name: "name3", VotingHistory: ["2013-10-21T15:41:36+00:00", "2011-7-21T15:41:36+00:00", "2009-10-21T15:41:36+00:00", "2010-7-21T15:41:36+00:00"] } 

我想findVotingHistory数组中的2个或更多的元素表示2013年7月21日或之后的date。 在这个例子中只有名字1。

为了有效的查询,我build议修改你的mongoose模式,把表示date的string改为实际的date。

例如,您可以首先在模式定义中修改它

 var mongoose = require('mongoose'); var Schema = mongoose.Schema; var voterSchema = new Schema({ name: String, registrationDate: Date, votingHistory: [Date] }); var Voter = mongoose.model("Voter", voterSchema, "voters" ); 

一旦完成,您将需要使用Bulk Operations API修改现有的集合以利用您的更新。 对于任何给定的mongoose模型,都存在一个.collection访问器,它实质上是从本身实现mongoose的底层“节点本地驱动程序”访问“集合对象”。 有了这个,您可以在registrationDate字段是string的文档上进行以下更新

 mongoose.connection.on("open", function(err, conn) { var bulk = Voter.collection.initializeOrderedBulkOp(); var counter = 0; Voter.find({"registrationDate": {"$type": 2} }, function(err, docs) { async.each(docs, function(doc, callback) { var regDate = new Date(doc.registrationDate), history = doc.votingHistory.map(function (dt){ return new Date(dt); }); bulk.find({"_id": doc._id}).updateOne({ "$set": { registrationDate: regDate, votingHistory: history } }); counter++; if (counter % 1000 == 0) { bulk.execute(function(err,result) { bulk = Voter.collection.initializeOrderedBulkOp(); }); } else { callback(); } }, // When everything's done function(err) { if ( counter % 1000 != 0 ) bulk.execute(function(err,result) { console.log("more updates" ); }); console.log("done now"); } }); }); 

一旦更新完成,你可以做任何一种方法。 其中之一就是使用$where操作符:

 var voteDate = new Date(2015, 6, 21); Voter.find({ "$where": "this.votingHistory.length > 8", "votingHistory": { "$gt": voteDate } }).exec(callback); 

另一种是通过使用点符号来“欺骗”mongodb,寻找至less具有第九个votingHistory数组元素的文档:

 var voteDate = new Date(2015, 6, 21); Voter.find({ "votingHistory.8": { "$exists": true }, "votingHistory": { "$gt": voteDate } }).exec(callback); 

对于基于aggregation framework的解决scheme(基于date是适当的MongoDBdate的假设),下面的pipe道会给你想要的结果:

 var voteDate = new Date(2015, 6, 21), pipeline = [ { "$project": { "name": 1, "registrationDate": 1, "votingHistory": 1, "numberOfVotes": { "$size": "$votingHistory" } } }, { "$match": { "numberOfVotes": { "$gt": 8 }, "votingHistory": { "$gt": voteDate } } } ]; // you can then use the aggregate Voter.aggregate(pipeline) .exec(function (err, results){ // access your results here }); // or using the aggregation builder Voter.aggregate() .project({ "name": 1, "registrationDate": 1, "votingHistory": 1, "numberOfVotes": { "$size": "$votingHistory" } }) .match({ "numberOfVotes": { "$gt": 8 }, "votingHistory": { "$gt": voteDate } }) .exec(callback); 

如果你愿意像下面那样使用mongo agrregation,那么这是可能的

 db.voter.aggregate([ {$unwind:"$votingHistory"}, { $match: {votingHistory:{$gt:'2015-7-21T15:41:36+00:00'}}}, {$group:{_id: {voterId:'$_id',name:'$name',registrationDate:'$registrationDate'}, count:{$sum:1}}}, {$match:{count:{$gt:2}}}, {$project:{_id:"$_id.voterId", name:"$_id.name", registrationDate:"$_id.registrationDate"}} ])