2

我正在使用 MongoDB,我想对组内的记录进行排序和跳过。

下面是一个示例数据:

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

现在我想要的是_id每个用户的所有文档(user_id),其中没有。用户的文档数大于 4。我还想要_id跳过这 4 个文档后的文档数。因此,如果用户有 6 个文档,那么我想要_id最后 2 个文档(按创建时间排序),以便我可以将这 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
4

2 回答 2

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

希望能帮到你!

于 2013-07-05T01:04:42.480 回答
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 */
} )
于 2013-07-04T19:59:20.127 回答