Mongodb 如何使用新ID进行mongoexport和mongoimport集合,但保持关系

Mongodb 如何使用新ID进行mongoexport和mongoimport集合,但保持关系,mongodb,mongoimport,mongoexport,Mongodb,Mongoimport,Mongoexport,我使用mongoexport导出2个集合: mongoexport -h -u -p --db dev -c parent -q '{_id: ObjectId("5ae1b4b93b131f57b33b8d11")}' -o parent.json 及 从第一个我得到了一条记录(通过ID选择),从第二个我得到了多条记录(通过parentId选择),这是第一个记录的ID 如何使用mongoimport导入具有新id但保持关系->parent.id=child.parentId ?TLDR;跳到

我使用mongoexport导出2个集合:

mongoexport -h -u -p --db dev -c parent -q '{_id: ObjectId("5ae1b4b93b131f57b33b8d11")}' -o parent.json

从第一个我得到了一条记录(通过ID选择),从第二个我得到了多条记录(通过parentId选择),这是第一个记录的ID

如何使用mongoimport导入具有新id但保持关系->
parent.id=child.parentId


TLDR;跳到解决此问题的部分,或者如果您想澄清误解,请实际阅读

背景 让我们澄清一下基本的误解,MongoDB“本身”没有任何关系的概念,因为您所指的“关系”只是一个集合中属性中记录的值,它“引用”了另一个集合中存在的另一个值。这些“外键”无关在MongoDB中强制执行任何类型的约束,该约束定义数据是“相关的”,它们只是“值”

一旦你接受了这一事实,那么很明显,为什么任何工具,比如,甚至像基本查询操作,都不知道,仅仅因为你把一个值放在某个地方,它就“意味着”从其他地方获取数据

很久以前,有一个概念(在较小的程度上)被称为,它不仅存储了一个“值”,而且还存储了一些关于该值在“参考”术语中的位置的细节。这可以是集合或数据库和集合。然而,这个概念仍然相对短暂,在现代环境中没有用处。即使如此“元数据”存储后,数据库本身不包含与数据的“关系”概念。检索仍然是一个“客户端概念”,其中一些驱动程序能够看到数据,然后通过向服务器发出另一个查询来检索“相关”数据来“解析”数据

很自然,这是“充满漏洞”的,这个概念被抛弃,取而代之的是更现代的概念,尤其是

现代建筑 这一切真正归结为,就MongoDB本身而言,“关系”实际上是“客户概念”,从这个意义上说,“外部实体”实际上通过您定义为“平等引用”的数据来决定“这”与“那”相关

没有“数据库规则”来强制执行这一点,因此与各种传统的RDBMS解决方案相比,MongoDB的“对象存储”本质上说“……这不是我的工作,委托给其他人”,这通常意味着您在用于访问数据库的“客户机”逻辑中定义它

然而,有一些“工具”允许“服务器”实际执行该逻辑。这些工具基本上围绕着使用MongoDB时在服务器上执行“连接”的“现代方法”展开

因此,如果您有想要“导出”的“相关数据”,那么您基本上有两个选项:

  • 使用$lookup创建“视图” MongoDB是在3.2版本中引入的。它们本质上是“聚合管道”,伪装成一个集合。对于所有正常操作,如
    .find()
    或甚至
    mongoexport
    而言,此管道看起来就像一个集合,可以这样访问

    db.createView("related_view", "parent", [
      { "$lookup": {
        "from": "children",
        "localField": "_id",
        "foreignField": "parentId",
        "as": "children"
      }}
    ])
    
    有了这个“视图”,您只需使用为“视图”定义的名称调用
    mongoexport

    正如我们将要做的,每个“父”项现在将包含一个数组,其中包含外键“children”中的“related”内容

    As生成作为BSON文档的输出,与所有MongoDB相同的约束条件适用于任何文档,因为生成的“连接”在任何文档中都不能超过16MB。因此,如果数组导致父文档增长超过此限制,则将输出作为嵌入文档中的“数组”使用是不可取的

    在这种情况下,您通常使用“反规范化”输出内容,就像在典型SQL联接中显示的内容一样。在这种情况下,将为每个“相关”成员复制“父”文档,并且输出文档与来自相关子级的文档相匹配,但与所有父级信息和“子级”匹配“作为一种独特的嵌入式财产

    这只意味着在这样一个“视图”中添加:

    由于我们只是为每个“相关子项”输出一个文档,因此不太可能违反BSON限制。对于父项和子项都非常大的文档,这仍然是一种可能性,尽管很少见。对于这种情况,将有不同的处理,我们可以在后面提到

  • 在脚本中直接使用$lookup 如果您没有支持“视图”的MongoDB版本,但仍然有并且没有BSON限制的限制,那么您基本上仍然可以使用
    mongo
    shell“脚本化”聚合管道的调用,并将其输出为JSON

    该过程类似,但我们没有使用“视图”和
    mongoexport
    ,而是手动包装一些可以从命令行调用到shell中的命令:

    mongo --quiet --eval '
        db.parent.aggregate([ 
          { "$lookup": {
            "from": "children",
            "localField": "_id",
            "foreignField": "parentId",
            "as": "children"
          }}
        ]).forEach(p => printjson(p))'
    
    同样,与之前的流程大致相同,如果您正在寻找,也可以选择在管道中进行

  • 编写“加入”脚本 如果您在没有支持的MongoDB实例上运行(而且您不应该这样做,因为低于3.0的版本没有更多的官方支持),或者实际上您有一个场景,“加入”将为每个超过BSON限制的“父”文档创建数据,那么另一个选项是“编写脚本”通过执行查询来获取“相关”数据并将其输出的整个连接过程

    mongo --quiet --eval '
        db.parent.find().forEach(p =>
          printjson(
            Object.assign(p, {
            children: db.children.find({ parentId: p._id }).toArray()
            })
          )
        )'
    
    甚至以“未缠绕”或“非标准化”形式:

总结 底线是“MongoDB本身”不知道“关系”,而这确实取决于您提供的细节。无论是以“视图”的形式,您可以访问它,还是通过其他方式定义显式声明此“关系”的“术语”所需的“代码”,因为就数据库本身而言,这
db.createView("related_view", "parent", [
  { "$lookup": {
    "from": "children",
    "localField": "_id",
    "foreignField": "parentId",
    "as": "children"
  }},
  { "$unwind": "$children" }
])
mongo --quiet --eval '
    db.parent.aggregate([ 
      { "$lookup": {
        "from": "children",
        "localField": "_id",
        "foreignField": "parentId",
        "as": "children"
      }}
    ]).forEach(p => printjson(p))'
mongo --quiet --eval '
    db.parent.find().forEach(p =>
      printjson(
        Object.assign(p, {
        children: db.children.find({ parentId: p._id }).toArray()
        })
      )
    )'
mongo --quiet --eval '
    db.parent.find().forEach(p =>
      db.children.find({ parentId: p._id }).forEach(child =>
        printjson(Object.assign(p,{ child }))
      )
    )'
db.parent.aggregate([
  { "$lookup": {
    "from": "children",
    "localField": "_id",
    "foreignField": "parentId",
    "as": "children"
  }},
  { "$out": "related_collection" }
])
var batch = [];

db.parent.aggregate([
  { "$lookup": {
    "from": "children",
    "localField": "_id",
    "foreignField": "parentId",
    "as": "children"
  }}
]).forEach(p => {
  batch.push({
    "updateOne": {
      "filter": { "_id": p._id },
      "update": {
        "$push": { "children": { "$each": p.children } }
      }
    }
  });

  if (batch.length > 1000) {
    db.parent.bulkWrite(batch);
    batch = [];
  })
});

if (batch.length > 0) {
  db.parent.bulkWrite(batch);
  batch = [];
}