我有这样的文件:
{
"_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对我来说都是新手!)
答案 0 :(得分:4)
删除"重复"这是一个过程,并没有简单的方法来识别"重复的"删除"他们作为一个单一的声明。这里的另一个特点是查询表单不能通常"确定数组的大小,当然不能按照文档中尚未存在的数据进行排序。
所有案例基本上归结为
识别"重复"的文档列表,然后理想地指出要删除的特定文档,或者更多地指出您不会删除的文档。 T"想从可能的重复项中删除。
处理该列表以实际执行删除。
考虑到这一点,你希望有一个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"作为文档的另一个属性,因此无需计算"就像在这里一样。