使用他们的文档聚合填充 id 数组

Jah*_*mad 4 mongodb mongodb-query aggregation-framework

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

作者文档

{
  "_id" : "10",
  "full_name" : "Joi Dark",
  "books" : ["100", "200", "351"],
}
Run Code Online (Sandbox Code Playgroud)

和其他文件(书籍):

{
    "_id" : "100",
    "title" : "Node.js In Action",
    "ISBN" : "121215151515154",
    "date" : "2015-10-10"
}
Run Code Online (Sandbox Code Playgroud)

所以结果我想要这个:

{
  "_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"
       }
  ],
}
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 8

使用$lookup它在检索从被提名的收集数据from基础上的匹配localFieldforeignField

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

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

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

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

它为原始文档中的每个数组成员创建一个新文档,因此$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" }
  }}
])
Run Code Online (Sandbox Code Playgroud)

如果实际上您的_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 = [];
}
Run Code Online (Sandbox Code Playgroud)

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