【问题标题】:Aggregate Populate array of ids with Their Documents使用他们的文档聚合填充 id 数组
【发布时间】:2017-06-28 07:35:25
【问题描述】:

我正在为 mongodb 中的一些聚合函数而苦苦挣扎。 我想在作者的文档中获取书籍文档,其中只有书籍 ID 作为字符串 ID 数组,如下所示:

作者文档

{
  "_id" : "10",
  "full_name" : "Joi Dark",
  "books" : ["100", "200", "351"],
}

及其他文件(书籍):

{
    "_id" : "100",
    "title" : "Node.js In Action",
    "ISBN" : "121215151515154",
    "date" : "2015-10-10"
}

所以结果我想要这个:

{
  "_id" : "10",
  "full_name" : "Joi Dark",
  "books" : [
       {
           "_id" : "100",
           "title" : "Node.js In Action",
           "ISBN" : "121215151515154",
           "date" : "2015-10-10"
       },
       {
           "_id" : "200",
           "title" : "Book 2",
           "ISBN" : "1212151454515154",
           "date" : "2015-10-20"
       },
       {
           "_id" : "351",
           "title" : "Book 3",
           "ISBN" : "1212151454515154",
           "date" : "2015-11-20"
       }
  ],
}

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    使用$lookup,它根据localFieldforeignField 的匹配,从from 中的指定集合中检索数据:

    db.authors.aggregate([
      { "$lookup": {
        "from": "$books",
        "foreignField": "_id",
        "localField": "books",
        "as": "books"
      }}
    ])
    

    as 是在文档中写入包含相关文档的“数组”的位置。如果您指定一个现有属性(例如在此处完成),则该属性将被输出中的新数组内容覆盖。

    如果您在 MongoDB 3.4 之前有 MongoDB,那么您可能需要先将 $unwind 的数组 "books" 作为 localField

    db.authors.aggregate([
      { "$unwind": "$books" },
      { "$lookup": {
        "from": "$books",
        "foreignField": "_id",
        "localField": "books",
        "as": "books"
      }}
    ])
    

    这会为原始文档中的每个数组成员创建一个新文档,因此再次使用$unwind$group 来创建原始表单:

    db.authors.aggregate([
      { "$unwind": "$books" },
      { "$lookup": {
        "from": "$books",
        "foreignField": "_id",
        "localField": "books",
        "as": "books"
      }},
      { "$unwind": "$books" },
      { "$group": {
        "_id": "$_id",
        "full_name": { "$first" "$full_name" },
        "books": { "$push": "$books" }
      }}
    ])
    

    如果实际上您的_id 值在ObjectId 类型的外部集合中,但您在localField 中有值是“字符串”版本,那么您需要转换数据以便类型匹配。没有其他办法。

    通过shell运行这样的东西来转换:

    var ops = [];
    db.authors.find().forEach(doc => {
      doc.books = doc.books.map( book => new ObjectId(book.valueOf()) );
    
      ops.push({
        "updateOne": {
          "filter": { "_id": doc._id },
          "update": {
            "$set": { "books": doc.books }
          }
        }
      });
    
      if ( ops.length >= 500 ) {
        db.authors.bulkWrite(ops);
        ops = [];
      }
    });
    
    if ( ops.length > 0 ) {
      db.authors.bulkWrite(ops);
      ops = [];
    }
    

    这会将"books" 数组中的所有值转换为真正的ObjectId 值,这些值可以在$lookup 操作中实际匹配。

    【讨论】:

    • 它不工作,返回空数组。书籍文档有 _id 类型的 ObjectId,但在作者文档中我只将 ids 保存为字符串。
    • @JahangirAhmad 那么需要解决这个问题。您的问题在两个字段中都显示“字符串”。因此,修复您的数据以使“object id like”字符串实际变为ObjectId 值。您需要这方面的帮助吗?请更新您的问题以显示真实数据。 “更新”是指“另外”,不要覆盖您在原始问题中放置的数据。所以我只是回滚了你的修订。一旦得到答案,请勿更改您提出的问题的上下文。
    • @JahangirAhmad 您无法将字符串与ObjectId 匹配。您需要帮助了解如何转换它们吗?如果你这样做然后问。停止编辑问题。
    • 是的,请帮我转换一下
    • @JahangirAhmad 添加了一个 shell 脚本供您运行转换。
    猜你喜欢
    • 2021-12-15
    • 2020-07-30
    • 2021-12-14
    • 1970-01-01
    • 2021-07-22
    • 2021-11-16
    • 1970-01-01
    • 2017-10-29
    • 1970-01-01
    相关资源
    最近更新 更多