Mongodb - 在一个组内排序和跳过

时间:2013-07-01 09:19:46

标签: mongodb aggregation-framework

我使用的是MongoDB,我希望对组内的记录进行排序和跳过。

以下是示例数据:

{
    "_id" : ObjectId("51cd7274267d959cb9f95cea"),
    "creation_time" : 100,
    "delivered" : true,
    "id" : 1,
    "user_id" : 10
}

现在我想要的是每个用户(_id)的所有文档的user_id,其中没有。用户的文档大于4.在跳过这4个文档后,我还想要_id个文档。因此,如果用户有6个文档,那么我想要最后2个文档的_id(按creation_time排序),以便我可以将这2个旧文档存档到另一个数据库。

我使用以下查询:

db.newsdb.aggregate([
{
    $match: {
        delivered: true
    }
},
{
    $group: {
        _id: {
            user_id: "$user_id",
            creation_time: "$creation_time"
        }
    }
}
])

现在问题是我想对每个用户的文档执行$sort$skip操作,而不是对所有用户的文档执行操作。所以我想要这样的东西:

{
    $group: {
        _id: {
            user_id: "$user_id",
            creation_time: "$creation_time"
        }
    },
    $sort: {
        user_id:1,
        creation_time:1
    },
    $skip: 4
}

但似乎mongo db不支持它。我收到以下错误:

Error: Printing Stack Trace
    at printStackTrace (src/mongo/shell/utils.js:37:7)
    at DBCollection.aggregate (src/mongo/shell/collection.js:897:1)
    at (shell):1:11
Mon Jul  1 14:47:55.762 JavaScript execution failed: aggregate failed: {
    "errmsg" : "exception: A pipeline stage specification object must contain exactly one field.",
    "code" : 16435,
    "ok" : 0
} at  src/mongo/shell/collection.js:L898

2 个答案:

答案 0 :(得分:0)

目前在聚合框架中无法做到这一点。

您需要为每个用户单独进行查询。您可以做的最好的事情是循环遍历所有执行查询的用户,以便为您提供不是前4名的文档:

[user list].forEach(function(u) { 
 var listToArchive = db.newsdb.find({user_id: u},{_id:1}).sort({creation_time:-1}).skip(4);
 /* do what you need to with listToArchive _id's */
} )

答案 1 :(得分:0)

在思考太多之后,我想出了一个使用map-reduce的解决方案,因为使用聚合框架似乎是不可能的。

这是reduce函数,即user_id的简单组文档。

var mapf = function () {
    emit(this.user_id, {
        _id: this._id,
        creation_time: this.creation_time
    })
}

在减少功能时,我检查是否至少有四个条目。如果为true,则values数组按creation_time排序,并跳过前4个文档。

var redf = function (key, values) {
    var result = {};
    if (values.length > 4) {
        values.sort(function (a, b) {
            return a.creation_time > b.creation_time;
        });

        // unfortunately, mongodb doesn't support array as result of reduce function
        result['oids'] = values.slice(3);
    } 

    return result;
}

现在是时候运行map-reduce命令了。结果将插入plus_four_users集合。

db.newsdb.mapReduce(mapf, redf, { out : "plus_four_users" })

这将产生类似的结果:

> db.newsdb.find({}, { user_id : 1, creation_time : 1 })
{ "_id" : ObjectId("51d612423dab6225ca6e6d36"), "creation_time" : 100, "user_id" : 10 }
{ "_id" : ObjectId("51d612503dab6225ca6e6d37"), "creation_time" : 200, "user_id" : 10 }
{ "_id" : ObjectId("51d612553dab6225ca6e6d38"), "creation_time" : 300, "user_id" : 10 }
{ "_id" : ObjectId("51d612593dab6225ca6e6d39"), "creation_time" : 400, "user_id" : 10 }
{ "_id" : ObjectId("51d6125d3dab6225ca6e6d3a"), "creation_time" : 500, "user_id" : 10 }
{ "_id" : ObjectId("51d6126f55ebf2ff5a13d1c9"), "creation_time" : 600, "user_id" : 10 }
{ "_id" : ObjectId("51d6127455ebf2ff5a13d1ca"), "creation_time" : 300, "user_id" : 11 }
{ "_id" : ObjectId("51d6127955ebf2ff5a13d1cb"), "creation_time" : 400, "user_id" : 11 }
{ "_id" : ObjectId("51d6127c55ebf2ff5a13d1cc"), "creation_time" : 500, "user_id" : 11 }
{ "_id" : ObjectId("51d6127f55ebf2ff5a13d1cd"), "creation_time" : 600, "user_id" : 11 }
{ "_id" : ObjectId("51d6128555ebf2ff5a13d1ce"), "creation_time" : 700, "user_id" : 11 }



 > db.plus_four_users.find().pretty()
{
    "_id": 10,
    "value": {
        "oids": [
            {
                "_id": ObjectId("51d6125d3dab6225ca6e6d3a"),
                "creation_time": 500
            },
            {
                "_id": ObjectId("51d6126f55ebf2ff5a13d1c9"),
                "creation_time": 600
            }
        ]
    }
}
{
    "_id": 11,
    "value": {
        "oids": [
            {
                "_id": ObjectId("51d6128555ebf2ff5a13d1ce"),
                "creation_time": 700
            }
        ]
    }
}

希望能帮到你!