2014-09-01 30 views
1

我有这样的文件:GROUP BY和删除文档基于字段数组大小

{ 
    "_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个这样的重复的Ids,所以如果有这样的事情作为批量检查/删除,这将是一件好事(可能是一个愚蠢的问题,对不起,Mongo对我来说都是新的! )

回答

4

删除“重复”是一个过程,并没有简单的方法来“识别”重复和“删除”他们作为一个单一的陈述。这里的另一个特殊之处是查询表单不能“典型地”确定数组的大小,并且当然不能在文档中不存在的情况下进行排序。

所有病例基本上回落到

  1. 标识是“重复”的文件列表,然后最好指法要删除特定文件,或者更重要的是该文件你“唐't'想从可能的重复项中删除。

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

考虑到这一点,你希望拥有的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中没有“子查询”,或者即使有超过“两个副本”的方式来选出您不希望从其他副本中删除的文档。但这是通用的方式。就像一个说明,如果数组的“大小”对于诸如“排序”等目的而言是重要的,那么最好的方法就是将“大小”作为文档的另一个属性,以便它使得这些操作更容易,无需像这里所做的那样“计算”。