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")
}
解释:
- 首先,您需要 aggregate(找到任何像
mongojs
或 mongoose
的库以及如何在它们各自的手册中执行此操作),
- 然后 unwind 部分和里面的卡片。
- 完成这两个步骤后,您需要根据您的要求 filter 由
unwind
操作产生的文档。在这种情况下:哪张卡与您的 ID 匹配
- Clear 您不需要的任何属性
- 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 查询 运行 更快。
我的结构如下所示:
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")
}
解释:
- 首先,您需要 aggregate(找到任何像
mongojs
或mongoose
的库以及如何在它们各自的手册中执行此操作), - 然后 unwind 部分和里面的卡片。
- 完成这两个步骤后,您需要根据您的要求 filter 由
unwind
操作产生的文档。在这种情况下:哪张卡与您的 ID 匹配 - Clear 您不需要的任何属性
- 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 查询 运行 更快。