【问题标题】:MongoDB: Removing duplicate document based on ObjectId?MongoDB:删除基于 ObjectId 的重复文档?
【发布时间】:2012-07-30 16:54:14
【问题描述】:

这确实是一个悬而未决的问题。如果这有点含糊,我很抱歉,但我正在尝试收集其他人的想法,因为我对 Mongo 很陌生

情况

  • 我意识到我的收藏有多个重复的文档(基于名称键)
  • 这些文件may be samemight got changed 在随后从文件转储期间(我们希望保留以后的更改)
  • 由于没有插入日期,因此很难通过查看文档来判断哪个是最新的(糟糕的架构设计)

通缉

  • 删除之前插入的文档
  • 我读到集合中的每个文档都分配有一个 ObjectId(here),这使得文档独一无二

问题

  • 是否可以根据ObjectId知道之前插入了哪个文档,并使用Map Reduce将其删除?
  • 还有其他想法和建议吗?

【问题讨论】:

  • 给定一个文档,如何判断它是另一个文档的版本还是全新的文档?
  • 我不能,我只能说如果有多个具有相同name 键的文档是重复的
  • 看来你喜欢这个问题:)
  • 我想我知道解决方案。等一下。

标签: mongodb


【解决方案1】:

好的,因为对象 id 使用时间戳,因为它前导四个字节,您可以通过一些数学运算来做到这一点。

谢天谢地,mongo shell 有一种方法可以从对象 id 获取时间戳,您需要做更多的 javascript 来首先查询具有相同名称的文档,然后将它们存储在临时变量中(如果使用命令行)或在临时表中(如果使用驱动程序)并使用下面链接中显示的时间戳获取器解析每个单独的 id。

http://www.mongodb.org/display/DOCS/Optimizing+Object+IDs#OptimizingObjectIDs-Extractinsertiontimesfromidratherthanhavingaseparatetimestampfield.

请记住,对象 ID 仅精确到秒,因此这在快速插入模式下仍然无济于事。

但是无论您要求的哪种方式都可以在 map reduce 函数中或上面显示的通过命令行执行的方式中实现。

试一试,如果卡住了,请告诉我。如果我知道你的收藏结构,我可能会很快做出一些东西,但只有在你敲了几次头之后:)

【讨论】:

  • 刚刚意识到这张海报也使用了 Mongo 的扩展 JSON 支持。 stackoverflow.com/questions/10552621/… 我仍然认为我的方法更简单,但这也是一个优雅的解决方案。
  • 是否需要从ObjectID中提取日期?为什么不直接比较 ObjectID(或在这个 _id 字段上对查询进行排序)?插入时间越晚,ObjectID就越大。
【解决方案2】:

今天晚上我很无聊,所以我们开始吧。

第 1 步。让我们准备测试数据。

> db.users.insert({name: 'John', other_field: Math.random()})
> db.users.insert({name: 'Bob', other_field: Math.random()})
> db.users.insert({name: 'Mary', other_field: Math.random()})
> db.users.insert({name: 'John', other_field: Math.random()})
> db.users.insert({name: 'Jeff', other_field: Math.random()})
> db.users.insert({name: 'Ivan', other_field: Math.random()})
> db.users.insert({name: 'Mary', other_field: Math.random()})
> db.users.find()
{
    "_id" : ObjectId("501976e9bee9b253265bba8b"),
    "name" : "John",
    "other_field" : 0.9884713875252772
}
{
    "_id" : ObjectId("501976e9bee9b253265bba8c"),
    "name" : "Bob",
    "other_field" : 0.048004131996396415
}
{
    "_id" : ObjectId("501976e9bee9b253265bba8d"),
    "name" : "Mary",
    "other_field" : 0.20415803582615222
}
{
    "_id" : ObjectId("501976e9bee9b253265bba8e"),
    "name" : "John",
    "other_field" : 0.5514446987265585
}
{
    "_id" : ObjectId("501976e9bee9b253265bba8f"),
    "name" : "Jeff",
    "other_field" : 0.8685077449753242
}
{
    "_id" : ObjectId("501976e9bee9b253265bba90"),
    "name" : "Ivan",
    "other_field" : 0.2842514340422925
}
{
    "_id" : ObjectId("501976eabee9b253265bba91"),
    "name" : "Mary",
    "other_field" : 0.984048520281136
}

第 2 步。map-reduce

var map = function() {
  emit(this.name, this);
};

var reduce = function(name, vals) {
  var last_obj = null;
  vals.forEach(function(v) {
    if(!last_obj || v._id > last_obj._id) {
      last_obj = v;
    }
  });
  return last_obj;
};

db.users.mapReduce(map, reduce, {out: 'temp_coll'})

它基本上按名称对所有文档进行分组,然后选择具有最大 _id 的文档。

第 3 步。使用独特的数据做一些事情。

> db.temp_coll.find()
{
    "_id" : "Bob",
    "value" : {
        "_id" : ObjectId("501976e9bee9b253265bba8c"),
        "name" : "Bob",
        "other_field" : 0.048004131996396415
    }
}
{
    "_id" : "Ivan",
    "value" : {
        "_id" : ObjectId("501976e9bee9b253265bba90"),
        "name" : "Ivan",
        "other_field" : 0.2842514340422925
    }
}
{
    "_id" : "Jeff",
    "value" : {
        "_id" : ObjectId("501976e9bee9b253265bba8f"),
        "name" : "Jeff",
        "other_field" : 0.8685077449753242
    }
}
{
    "_id" : "John",
    "value" : {
        "_id" : ObjectId("501976e9bee9b253265bba8e"),
        "name" : "John",
        "other_field" : 0.5514446987265585
    }
}
{
    "_id" : "Mary",
    "value" : {
        "_id" : ObjectId("501976eabee9b253265bba91"),
        "name" : "Mary",
        "other_field" : 0.984048520281136
    }
}

例如,删除原始集合,迭代这个集合并将值插入新集合。完成后不要忘记删除临时集合。

重要

我没有费心从 objectid 中提取时间戳,因为我假设您不是每秒运行两次导入作业(甚至可能不是每秒)。

【讨论】:

  • 问题:_id 会一直增加吗?给定的 Object Id 由 time、machine、pid、inc 组成,除了 machine 会增加,但是 machine?
  • @daydreamer:除非你弄乱你的系统时钟,否则是的,它保证是单调递增的。
  • 听起来不错,是否有任何参考文档可以断言?我会尝试使用您的技术并会回复您,非常感谢您的帮助
  • 看看@Prasith 的回答。 ObjectId 包含时间戳,占用最高 4 个字节。还有here's the official doc
猜你喜欢
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-01
  • 2021-04-22
相关资源
最近更新 更多