MongoDB: 更新嵌套数组文档中的子属性

MongoDB: Updating a sub properties inside a nested array document

我的结构如下所示:

course: { // course 
   id,
   coursename,
   sections: { // has an array of sections
    section: {
      id,
      sectionname
      cards: {  // each section has an array of cards
         card: {
            id  
            cardname 
         }
      }
    }
  }
}

现在,我获得了卡 ID。假设 cardID 是 301。我没有关于存储卡的课程或部分的详细信息。我只有卡ID。我想将卡名从 "Test 1" 更新为 "Test 2"。

请提供一些有关如何仅在给定 cardId 的情况下更新卡名称的信息。还请提供有关如何仅使用 cardId 访问单张卡的信息。

如果我运行下面的查询:

 db.course.find( {"sections.cards.id" : ObjectId("5859517447c4286d0f0639ee")},
{"sections.cards.$":1,_id:0})

返回整个部分对象,而不是返回单个卡片。请提供一些有关如何仅使用 cardId 访问单个卡的信息。

我拥有的文档示例:

{ 
   "_id" : ObjectId("58595041cfdc46100f92a985"), 
   "modelType" : "Course",
   "name" : "c1",
   "sections" : [ 
        { 
           "id" : ObjectId("5859504dcfdc46100f92a986"),
           "name" : "s1", 
           "cards" : [ 
               { 
                  "id" : ObjectId("58595057cfdc46100f92a987"),
                  "name" : "card1", 
               }, 
               { 
                  "id" : ObjectId("5859506ccfdc46100f92a988"),
                  "name" : "card2"
               }
         ]
      }
   ] 
}

请提供有关如何仅使用卡 ID 更新卡的信息。

假设我想更改卡的名称。如何实现?

我希望这个答案足以满足您的需求:

db.collection.aggregate([
   {$unwind: "$sections"}, // unwind the array
   {$unwind: "$sections.cards"}, // unwind the array
   {$match: {"sections.cards.id": ObjectId("5859506ccfdc46100f92a988")}}, // filter
   {$project: {_id: 0, id: "$sections.cards.id", name: "$sections.cards.name"}} // cut and clear any property that you don't need
])

这导致(我用你的样本测试了它):

{
   "name" : "card2",
   "id" : ObjectId("5859506ccfdc46100f92a988")
}

解释:

  1. 首先,您需要 aggregate(找到任何像 mongojsmongoose 的库以及如何在它们各自的手册中执行此操作),
  2. 然后 unwind 部分和里面的卡片。
  3. 完成这两个步骤后,您需要根据您的要求 filterunwind 操作产生的文档。在这种情况下:哪张卡与您的 ID 匹配
  4. Clear 您不需要的任何属性
  5. Return 结果

要更新文档中的属性:

不可能在不知道其索引的情况下更新数组中的元素,因此首先,您必须找出它的 index/position 内部。我能想到的最简单的方法是使用 for (因为你的是双层数组,我认为 方法会复杂得多):

// first, find the document, where xxx is the target card ID
db.collection.find({'sections.cards.id': ObjectId("xxx")}, function(err, reply){
     // iterates through the documents
     for(var z = 0; z < reply.length; z++){
         // iterates through the `sections`
         for(var y = 0; y < reply[z].sections.length; y++){
             // iterates through the `cards`
             for(var x = 0; x < reply[z].sections[y].cards.length; x++){
                  if(reply[z].sections[y].cards[x].id.toString() === "xxx")                   
                  {
                       // do what you want to do the card here
                       reply[z].sections[y].cards[x].name = "updated name";
                  }
             }
         }

         // save the updated doc one-by-one,
         db.collection.update({_id: reply._id}, reply);
     }


});

最佳方案

最好的解决办法是:首先不要有这种文件。无模式数据库方法并不意味着您可以将所有内容放入文档。下次设计文档架构时,您必须考虑内部数据的功能和可访问性。

您可以根据需要合理化文件。在这种情况下,您应该拆分 course 文档,因为很明显 card 属性 应该是 course 的独立集合。如果您担心需要加入这两个集合,请不要担心。自 3.2 mongodb 为此类应用程序引入 $lookup

它不仅会让您的生活更轻松,还会使 mongo 查询 运行 更快。