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



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

我已经通过MongoVUE的map reduce接口尝试了以下一组map、reduce和finalize函数,但它们在3000000个记录集合上运行不到一秒钟后就没有返回任何结果,该集合在指示字段上肯定有重复项。显然出了问题,但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

您的map reduce代码工作时没有任何问题,尽管它适用于非常小的数据集。我认为reduce函数中的return values[0];将是复制粘贴错误。你可以通过mongo shell尝试同样的方法。

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

你错了,db.collection.aggregate(pipeline, options)是在version 2.2中引入的。

以下是使用aggregation框架的方法,但这不是首选方法,因为您的数据集非常庞大,并且在v2.4中,$sort运算符的内存限制为RAM的10%。

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"});

运行地图缩减,

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函数不过滤按键。所以你不能只得到重复的密钥。你会得到所有的关键点,在地图上减少输出。在您的finalize函数中,您可以不显示它们的值,这就是您正在做的。

相关内容

  • 没有找到相关文章

最新更新