Mongodb 如何使用新ID进行mongoexport和mongoimport集合,但保持关系
我使用mongoexport导出2个集合: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 -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" }} ])
: 正如我们将要做的,每个“父”项现在将包含一个数组,其中包含外键“children”中的“related”内容 As生成作为BSON文档的输出,与所有MongoDB相同的约束条件适用于任何文档,因为生成的“连接”在任何文档中都不能超过16MB。因此,如果数组导致父文档增长超过此限制,则将输出作为嵌入文档中的“数组”使用是不可取的 在这种情况下,您通常使用“反规范化”输出内容,就像在典型SQL联接中显示的内容一样。在这种情况下,将为每个“相关”成员复制“父”文档,并且输出文档与来自相关子级的文档相匹配,但与所有父级信息和“子级”匹配“作为一种独特的嵌入式财产 这只意味着在这样一个“视图”中添加: 由于我们只是为每个“相关子项”输出一个文档,因此不太可能违反BSON限制。对于父项和子项都非常大的文档,这仍然是一种可能性,尽管很少见。对于这种情况,将有不同的处理,我们可以在后面提到mongoexport
- 在脚本中直接使用$lookup
如果您没有支持“视图”的MongoDB版本,但仍然有并且没有BSON限制的限制,那么您基本上仍然可以使用
shell“脚本化”聚合管道的调用,并将其输出为JSON 该过程类似,但我们没有使用“视图”和mongo
,而是手动包装一些可以从命令行调用到shell中的命令:mongoexport
同样,与之前的流程大致相同,如果您正在寻找,也可以选择在管道中进行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() }) ) )'
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 = [];
}