【问题标题】:MongoDB - Update / Replace array field with subdocument to embed its contentMongoDB - 使用子文档更新/替换数组字段以嵌入其内容
【发布时间】:2023-03-05 20:37:01
【问题描述】:

说明

重做我的数据结构后,我想从不同的集合中嵌入一个文档(或其中的一部分)。注意 event.tags[]。目前,只存储了他们的字符串化 _id(作为纯字符串,没有 ObjectId!),但我想迁移他们的内容,比如{tags.name, tags._id}。我想用tag-documents 的内容更新/替换原来的event.tags

数据

收集“事件”

db.events.insertMany([{
    "_id" : ObjectId("5f6c5b0594911c0b7231643d"),
    "title" : "Test Event",
    "tags" : [ 
        "5fd8cef2368c625a9d7516cb"
    ]
},
{
    "_id" : ObjectId("5fb3896afc13ae2ed1000000"),
    "title" : "Test Event 2",
    "tags" : [ 
        "5fd8cef2368c625a9d7516cb", 
        "5fd8cfb04a4a6063ab4ca4bf", 
        "5fd8cfb04a4a6063ab4ca4c0"
    ]
}
]);

集合“标签”

db.tags.insertMany([{
    "_id" : ObjectId("5fd8cef2368c625a9d7516cb"),
    "name" : "Foo",
    "createdAt" : ISODate("2020-12-15T14:57:54.096Z")
},
{
    "_id" : ObjectId("5fd8cfb04a4a6063ab4ca4bf"),
    "name" : "Bar",
    "createdBy" : "embo@team-con.de",
    "createdAt" : ISODate("2020-12-16T14:57:54.096Z")
},
{
    "_id" : ObjectId("5fd8cfb04a4a6063ab4ca4c0"),
    "name" : "Foobar",
    "createdAt" : ISODate("2020-12-17T14:57:54.096Z")
}
]);

预期结果

我想实现嵌入 "tags""events.tags" "tags._id" 匹配的集合的一些/所有字段 作为嵌入/嵌套文档的数组。这将是迁移脚本的一部分,在 mongoDB API >4.0 thru node.js 上运行。

目标:我想用不同集合的某些信息(参见集合标签)替换字符串数组(参见集合事件)并将其保存回集合事件(迁移过程)。所以我假设,我必须从tag 集合中获取/查找一些东西,并且需要一种机制来用复杂对象({tags._id,tags.name})替换字符串(events.tags[x])

{
    "_id" : ObjectId("5f6c5b0594911c0b7231643d"),
    "title" : "Test Event",
    "tags" : [{
        "_id" : ObjectId("5fd8cef2368c625a9d7516cb"),
        "name" : "Foo",
        "createdAt" : ISODate("2020-12-15T14:57:54.096Z")
    }]
},
{
    "_id" : ObjectId("5fb3896afc13ae2ed1000000"),
    "title" : "Test Event 2",
    "tags" : [{
        "_id" : ObjectId("5fd8cef2368c625a9d7516cb"),
        "name" : "Foo"
    },
    {
        "_id" : ObjectId("5fd8cfb04a4a6063ab4ca4bf"),
        "name" : "Bar"
    },
    {
        "_id" : ObjectId("5fd8cfb04a4a6063ab4ca4c0"),
        "name" : "Foobar"
    }]
}

婴儿步

我尝试使用聚合管道,并在最后一步中将其内容匹配并查找到replaceWith。我不太熟悉通过聚合管道解决这个问题。

db.getCollection('events').aggregate([
    { $match: { tags: { "$exists" : true } } },
    {
       $lookup:
         {
           from: "tags",
           localField: "tags",
           foreignField: "_id",
           as: "taags"
         }
    },
    ])

【问题讨论】:

    标签: mongodb mongoose aggregation-framework


    【解决方案1】:

    您可以在$lookup 阶段之前使用$toObjectId 运算符将标签的id 从字符串转换为objectId,

    • $addFields 添加新字段并更新/格式化现有字段
    • $map 迭代 tags 数组的循环
    • $toObjectId将字符串objectId转换为objectId类型
    • 将名称tags 放入$lookup 阶段的as 属性中
      // $match stage here
      {
        $addFields: {
          tags: {
            $map: {
              input: "$tags",
              in: { $toObjectId: "$$this" }
            }
          }
        }
      },
      {
        $lookup: {
          from: "tags",
          localField: "tags",
          foreignField: "_id",
          as: "tags"
        }
      }
    

    Playground

    • 使用聚合查询,您不能更新当前事件集合,但您可以使用 $out 阶段生成新集合,从 MongoDB 4.4 开始支持,
    • 将此阶段置于最后阶段
      { $out: "updated_events" }
    

    第二个选项是准备一个脚本来执行手动过程,

    1. 从事件集合中查找所有记录
    2. 循环事件收集的结果
    3. 内部循环通过在标签集合中传递标签ID来执行查找查询
    4. 内部循环更新事件集合通过替换 3) 步骤的结果

    【讨论】:

    • 感谢您的快速回复。对 replaceAll 管道的使用有什么见解吗?
    • docs中的定义“用替换字符串替换输入字符串中搜索字符串的所有实例。”足以理解$replaceAll运算符。
    • 感谢您的努力和指导。尽管如此,我在用复杂的嵌套对象替换一组 ID 方面遇到了困难——没有像在小写/大写中重命名这样的普通字符串替换。我用$replaceWith 替换了$replaceAll,因为我链接错了。所以我仍然怀疑这是否适合我的需求。你同意吗?
    • 您想永久更新收藏中的文件吗?如果是,那么聚合只格式化结果意味着它只会获取文档,不能在集合中更新。
    • 是一个错误,你不能通过聚合更新任何集合,但是你可以使用 $out 阶段来产生新的集合,之后你可以删除旧的集合并将新的集合重命名为事件跨度>
    猜你喜欢
    • 2016-12-18
    • 2022-01-17
    • 2016-11-23
    • 2019-02-01
    • 2018-12-08
    • 2012-03-01
    • 2013-07-27
    • 1970-01-01
    相关资源
    最近更新 更多