mongodb 聚合使用ID的文档填充ID数组

yxyvkwin  于 2023-01-04  发布在  Go
关注(0)|答案(2)|浏览(164)

我在mongodb中遇到了一些聚合函数,我想在author的文档中获取books文档,该文档只有books 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"
       }
  ],
}
v1uwarro

v1uwarro1#

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

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

as是文档中写入包含相关文档的“数组”的位置。如果指定了现有属性(如此处所做),则该属性将在输出中被新数组内容覆盖。
如果您有MongoDB 3.4之前的MongoDB,那么您可能需要先将"books"的数组$unwindlocalField

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操作中实际匹配。

qnzebej0

qnzebej02#

如果你的输入包含一个字符串数组,你想把它们转换成ObjectId,你可以通过使用projection,然后是map$toObjectId方法来实现。

db.authors.aggregate([
 { $project: {
      books: {
         $map: {
            input: '$books',
            as: 'book',
            in: { $toObjectId: '$$book' },
         },
      },
  },},
  { $lookup: {
       from: "$books",
       foreignField: "_id",
       localField: "books",
       as: "books"
     }
  },
])

理想情况下,您的数据库应该以这样一种方式进行格式化,即您的聚合存储为ObjectId,但在无法选择的情况下,这是一种可行的解决方案。

相关问题