【问题标题】:How to remove documents with broken reference in MongoDB?如何删除 MongoDB 中引用损坏的文档?
【发布时间】:2014-02-08 07:06:36
【问题描述】:

我在 Mongo 有两个集合:

db.user.find():
{
  "_id": { "$oid" : "52db05e6a2cb2f36afd63c47" },
  "name": "John",
  "authority_id": { "$oid" : "52daf174a2cb2f62aed63af3" },
}
{
  "_id": { "$oid" : "52db05e6a2cb2f36afd63d00" },
  "name": "Joe",
  "authority_id": { "$oid" : "52daf174a2cb2f62aed63af3" },
}

db.authority.find():
{
  "_id": { "$oid" : "52daf174a2cb2f62aed63af3" },
  "name": "Sample Authority"
}

用户通过 ObjectId 存储对授权 ID 的引用。

现在我的问题是:一些权限已被删除,不再收集。如果他们的 authority_id 指向已删除的权限,我需要找到一种方法来遍历“用户”集合并删除它们。

我试过这个:

db.user.find(
    { 
      $where: function() { 
        db.authority.find({ _id: this.authority_id }).count() == 0  
      }
     })

但那里无法访问“db”。是否可以在迭代中实现引用检查?

【问题讨论】:

  • 我可以使用 $nin 和一个变量来模拟:delete from user where authority_id not in (select id from authority)

标签: mongodb


【解决方案1】:

根据http://docs.mongodb.org/manual/reference/operator/query/where/,“$where 运算符表达式无法访问某些全局函数或属性,例如 db,这些函数或属性在 mongo shell 中可用”。

但是你可以试试 map reduce: http://cookbook.mongodb.org/patterns/pivot/

我会亲自编写代码,但您可能有不同的要求。

【讨论】:

  • 可以map reduce删除记录吗?代码内是可以的,但应该在服务器上,在 java-script 中。
  • 我不认为它可以更改任何数据,因为它只是一个聚合,但它可以将结果输出到一个集合。之后仍然需要另一个外部进程来处理输出。这有点糟糕:)
  • 好吧,您可以使用一个技巧将 map reduce 作业的结果作为更新文档的最终版本输出到同一集合,从而有效地替换它们。之后您可以 $unset 无关字段。听起来很冒险:)
  • 是的,......而且就像一个巨大的开销。不过感谢您的想法。
【解决方案2】:

您可以通过在 javascript shell 上迭代光标或使用任何 Mongo 驱动程序来删除损坏的条目。以下示例将为您提供在 javascript shell 上执行此操作的想法。

db.user.find().forEach( function(myDoc) {
    var cursor = db.authority.find({'_id' : myDoc.authority_id});
    if(cursor.hasNext() == false) {
        db.user.remove({_id : myDoc._id});
    }
});

【讨论】:

  • 请不要这样做cursor.hasNext() == false。请改用!cursor.hasNext(),或者,如果您需要对false 进行严格检查,请使用=== 并可能将false 放在首位 以便更清晰
【解决方案3】:

您可以使用聚合来查找所有孤立用户,然后将其删除。

const orphanUsers = db.user.aggregate([
    {
      // Join authority collection using authority_id
      $lookup: {
        from: "authority",
        localField: "authority_id",
        foreignField: "_id",
        as: "authority"
      }
    },
    // filter users without authority (means authority_id doesn't exist)
    { $match: { authority: [] } },
    // return only the _id
    { $project: { _id: "$_id" } }
])

// Delete all orphan users
db.user.deleteMany({
    _id: { $in: orphanUsers.map(({ _id }) => _id) }
})

【讨论】:

  • 这是最有效的方法,因为引入了聚合,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 2014-05-05
  • 1970-01-01
相关资源
最近更新 更多