根据字段数组大小分组和删除文档

时间:2014-09-01 08:44:29

标签: javascript mongodb mongodb-query aggregation-framework

我有这样的文件:

{
    "_id" : ObjectId("53bcedc39c837bba3e1bf1c2"),
    id : "abc1",
    someArray: [ 1 , 10 , 11]
}

{
    "_id" : ObjectId("53bcedc39c837bba3e1bf1c4"),
    id : "abc1",
    someArray: [ 1 , 10]
}
... other similar documents with different Ids

我想浏览整个集合并删除someArray最小的文档,按id分组。所以在这个例子中,我按abc1分组(我得到2个文档)然后第二个文档将是要删除的文档,因为它在someArray中的计数最少。

没有$count累加器,所以我不知道如何使用$group

另外,会有1000个像这样重复的ID,所以如果有批量检查/删除这样的东西会很好(可能是一个愚蠢的问题,对不起,Mongo对我来说都是新手!)

1 个答案:

答案 0 :(得分:4)

删除"重复"这是一个过程,并没有简单的方法来识别"重复的"删除"他们作为一个单一的声明。这里的另一个特点是查询表单不能通常"确定数组的大小,当然不能按照文档中尚未存在的数据进行排序。

所有案例基本上归结为

  1. 识别"重复"的文档列表,然后理想地指出要删除的特定文档,或者更多地指出您不会删除的文档。 T"想从可能的重复项中删除。

  2. 处理该列表以实际执行删除。

  3. 考虑到这一点,你希望有一个2.6或更高版本的现代MongoDB,你可以从aggregate方法获得一个游标。您还希望这些版本中提供Bulk Operations API以获得最佳速度:

    var bulk = db.collection.initializeOrderedBulkOp();
    var counter = 0;
    
    db.collection.aggregate([
        { "$project": {
            "id": 1,
            "size": { "$size": "$someArray" }
        }},
        { "$sort": { "id": 1, "size": -1 } },
        { "$group": {
            "_id": "$id",
            "docId": { "$first": "$_id" }
        }}
    ]).forEach(function(doc) {
        bulk.find({ "id": doc._id, "_id": { "$ne": doc.docId }).remove();
        counter++;
    
        // Send to server once every 1000 statements only
        if ( counter % 1000 == 0 ) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();  // need to reset
        }
    });
    
    // Clean up results that did not round to 1000
    if ( counter % 1000 != 0 )
        bulk.execute();
    

    对于旧版本的MongoDB,您仍然可以做同样的事情,但.aggregate()的结果必须低于16MB,这是BSON限制。这仍然应该很多,但对于旧版本,您还可以使用mapReduce输出到集合。

    但是对于一般的聚合响应,您会得到一系列结果,而且您​​也没有其他方便的方法来查找数组的大小。还有一点工作:

    var result = db.collection.aggregate([
        { "$unwind": "$someArray" },
        { "$group": {
            "_id": "$id",
            "id": { "$first": "$id" },
            "size": { "$sum": 1 }
        }},
        { "$sort": { "id": 1, "size": -1 } },
        { "$group": {
            "_id": "$id",
            "docId": { "$first": "$_id" }
        }}
    ]);
    
    result.result.forEach(function(doc) {
        db.collection.remove({ "id": doc._id, "_id": { "$ne": doc.docId } });
    });
    

    所以没有光标可以获得大结果而且没有批量操作所以每一个"删除"需要单独发送到服务器。

    所以在MongoDB中没有"子查询"或者甚至当有超过"两个重复的时候#34;一种单独输出您不想从其他副本中删除的文档的方法。但这是实现它的一般方法。

    正如一张纸条,如果"尺寸"对于诸如"排序"之类的目的,数组对你来说很重要,那么你最好的工作就是保持这个" size"作为文档的另一个属性,因此无需计算"就像在这里一样。