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

时间:2015-10-21 16:34:37

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我有一系列选民登记数据。采取以下形式:

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"]
}

我需要能够检测votingHistory数组子集的大小。例如,这是我尝试过的一个查询:

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

此查询的目的是查找2015-7-21之后至少有8张选票的所有选民。有没有办法让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"]
}

我想找到VotingHistory数组中的2个或更多元素代表2013-7-21之后的日期。这个例子中只有名称1。

2 个答案:

答案 0 :(得分:1)

对于有效的查询,我建议通过将表示日期的字符串文字更改为实际日期来修改您的mongoose模式。

您可以先在架构定义中修改此项,例如

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访问器,它基本上访问"集合对象"来自底层"节点本机驱动程序"在哪个猫鼬实现自己。这样,您可以对registrationDate字段为字符串

的文档执行以下更新
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);

另一种方法是使用 dot notation 来"技巧" mongodb寻找至少有第9个votingHistory数组元素的文档:

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

对于基于 aggregation framework 的解决方案(基于日期是适当的MongoDB日期的假设),以下管道将为您提供所需的结果:

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);

答案 1 :(得分:0)

如果你愿意使用下面的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"}}
])