Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mongodb Mongo DB-在组内排序和跳过_Mongodb_Aggregation Framework - Fatal编程技术网

Mongodb Mongo DB-在组内排序和跳过

Mongodb Mongo DB-在组内排序和跳过,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我正在使用MongoDB,我想对组中的记录进行排序和跳过 以下是数据示例: { "_id" : ObjectId("51cd7274267d959cb9f95cea"), "creation_time" : 100, "delivered" : true, "id" : 1, "user_id" : 10 } 现在我想要的是每个用户的所有文档的\u id(user\u id),其中用户的文档数大于4。在跳过这4个文档之后,我还需要文档的\u id。因此,

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

以下是数据示例:

{
    "_id" : ObjectId("51cd7274267d959cb9f95cea"),
    "creation_time" : 100,
    "delivered" : true,
    "id" : 1,
    "user_id" : 10
}
现在我想要的是每个用户的所有文档的
\u id
user\u id
),其中用户的文档数大于4。在跳过这4个文档之后,我还需要文档的
\u id
。因此,如果一个用户有6个文档,那么我需要最后2个文档的
\u id
(按创建时间排序),以便我可以将这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名的文档:

[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 */
} )

考虑太多之后,我想出了一个使用MapReduce的解决方案,因为使用聚合框架似乎是不可能的

下面是reduce函数,它通过
user\u id
对文档进行简单的分组

var mapf = function () {
    emit(this.user_id, {
        _id: this._id,
        creation_time: this.creation_time
    })
}
在reduce函数中,我检查是否至少有四个条目。如果为true,
数组按
创建时间
排序,并跳过前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\u four\u用户
集合中

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

希望能对你有所帮助

为什么在
组中使用
$ceation\u time
?难道许多文档不可能具有相同的用户id但创建时间不同吗?@Schaliasos如果我不使用
$creation\u time
,那么它会将一个用户的所有文档分组在一行中。但是我希望所有的文档都是给一个用户的,这样我就可以对这些文档进行进一步的操作(排序和跳过)。现在的问题是,作为最终结果,这个分组的意义是什么。所以这个问题的答案是,我想对每个用户的所有文档执行
排序
跳过
,而不是对所有用户的所有文档执行。我想先通过
user\u id
对它们进行分组,以便可以进行进一步的操作。如果你有更好的主意,你可以提出建议。那么也许你真的不需要
语句。哦,现在我明白了。尝试先对它们进行排序,然后对它们进行分组。先排序不会解决问题,因为
group
操作会在排序之前忽略排序。确实可以使用map reduce完成此操作,但您的实现不正确。作为一个简单的测试,尝试使用一个示例数据集运行它,其中一个用户id恰好只有一个文档。它也不能在非常大的数据集上正常工作,而且它肯定会在碎片集合上崩溃。你是对的@AsyaKamsky。这个MR函数的问题是reduce函数缺少幂等性。事实上,Yatendra Goel做了一些moodifications并找到了解决方案,在这里可以找到:实际上我在这里给了他解决方案:它也可以用mapreduce完成,但可能是多个查询(如果索引)在整个集合上的速度将明显快于mapreduce。如果将四个要保留的文档嵌入到user_id对象中是有意义的,您可以利用2.4的功能来维护封顶数组—每个用户可以拥有包含四个最新文档的数组—这也简化了清理过程,因为在将较新文档插入其数组时会自动完成清理。