MongoDB使用MapReduce查找重复共享多个字段的重复项

时间:2014-10-01 18:17:33

标签: javascript mongodb mapreduce duplicates

我正在尝试在用于生产的Mongo 2.4版数据库中找到重复项,因此无法更新。由于2.4中不存在聚合,我无法使用聚合管道来查找重复项,因此我尝试使用MapReduce找到解决方案。

我已经通过MongoVUE的Map Reduce界面尝试了以下一组map,reduce和finalize函数,并且在3,000,000条记录集合上运行不到一秒之后它们没有返回任何内容,这些集合肯定有重复的指示字段。显然出现了问题,但MongoVUE没有显示任何错误消息或有用的指示。

function Map() {
  emit(
    {name: this.name, LocationId: this.LocationId, 
     version: this.version},
    {count:1, ScrapeDate: this.ScrapeDate}
  );
}

function Reduce(key, values) {
  var reduced = {count:0, ScrapeDate:''2000-01-01''};
  values.forEach(function(val) {
    reduced.count      += val.count;
    if (reduced.ScrapeDate.localeCompare(val.ScrapeDate) < 0)
      reduced.ScrapeDate=val.ScrapeDate;
  });

  return reduced;
  return values[0];
}

function Finalize(key, reduced) {
  if (reduced.count > 1)
    return reduced;
}

我只需找到共享相同nameLocationIdversion的多个记录的任何实例,理想情况下会显示此类的最新ScrapeDate个记录。

1 个答案:

答案 0 :(得分:1)

您的map-reduce代码在没有任何问题的情况下工作,但对于非常小的数据集。我认为reduce函数中的return values[0];将是一个复制粘贴错误。您可以通过mongo shell尝试相同的操作。

  

由于2.4中不存在聚合,我无法使用聚合管道查找重复项,因此我试图找到解决方案   使用MapReduce。

你在这里弄错了,db.collection.aggregate(pipeline, options)中引入了version 2.2

以下是如何使用aggregation框架完成的,但由于您的数据集非常庞大,并且$sort运算符的内存限制为RAM的10%,因此不会优先使用它。 V2.4。

db.collection.aggregate(
 [
   // sort the records, based on the 'ScrapeDate' field, in descending order.
   {$sort:{"ScrapeDate":-1}},

   // group by the key fields, and take the 'ScrapeDate' of the first document,
   // Since it is in sorted order, the first document would contain the  
   // highest field value.
   {$group:{"_id":{"name":"$name","LocationId":"$LocationId","version":"$version"}
            ,"ScrapeDate":{$first:"$ScrapeDate"}
            ,"count":{$sum:1}}
           },

   // output only the group, having documents greater than 1.
   {$match:{"count":{$gt:1}}}
]
);

使用Map-reduce功能,它在我的测试数据上运行没有问题。

db.collection.insert({"name":"c","LocationId":1,"version":1,"ScrapeDate":"2000-01-01"});
db.collection.insert({"name":"c","LocationId":1,"version":1,"ScrapeDate":"2001-01-01"});
db.collection.insert({"name":"c","LocationId":1,"version":1,"ScrapeDate":"2002-01-01"});
db.collection.insert({"name":"d","LocationId":1,"version":1,"ScrapeDate":"2002-01-01"});

运行map-reduce,

db.collection.mapReduce(Map,Reduce,{out:{"inline":1},finalize:Finalize});

O / P:

{
        "results" : [
                {
                        "_id" : {
                                "name" : "c",
                                "LocationId" : 1,
                                "version" : 1
                        },
                        "value" : {
                                "count" : 3,
                                "ScrapeDate" : "2002-01-01"
                        }
                },
                {
                        "_id" : {
                                "name" : "d",
                                "LocationId" : 1,
                                "version" : 1
                        },
                        "value" : null
                }
        ],
        "timeMillis" : 0,
        "counts" : {
                "input" : 4,
                "emit" : 4,
                "reduce" : 1,
                "output" : 2
        },
        "ok" : 1,
}

请注意,对于没有任何重复项的记录,输出包含value:null

这是由于您的finalize功能:

function Finalize(key, reduced) {
  if (reduced.count > 1)
    return reduced; // returned null by default for keys with single value,
                    // i.e count=1
}

finalize功能过滤掉键。因此,您只能获得重复的密钥。您将获得map-reduce输出中的所有键。在最终确定功能中,您可以不显示其值,这就是您正在做的事情。