使用 MongoDB 中的聚合值更新多个文档
Update multiple documents with aggregated values in MongoDB
问题 1
我有一个名为 recipe
的集合,其中所有文档都有一个数组字段 ingredients
。我想计算那些数组项并将它们写入新字段 ingredient_count
.
问题 2
还有一个名为 ingredient
的集合。文档有一个 count
字段,它是所有食谱中的总使用次数。
我目前的做法
我现在的解决方案是一个脚本,它聚合集合并逐一更新所有文档:
// PROBLEM 1: update recipe documents
db.recipe.aggregate(
[
{
$project: {
numberOfIngredients: { $size: "$ingredients" }
}
}
]
).forEach(function(recipe) {
db.recipe.updateOne(
{ _id: recipe._id },
{ $set: { incredient_count: recipe.numberOfIngredients } }
)
});
// PROBLEM 2: update ingredient documents
db.ingredient.find().snapshot().forEach(function(ingredient) {
db.ingredient.updateOne(
{ _id: ingredient._id },
{ $set: { count: db.recipe.count({ ingredients: { $in: [ingredient.name] } })) } }
)
});
这非常慢。知道如何更有效地做到这一点吗?
对于这两个问题,可以只执行聚合输出到将替换现有集合的新集合:
问题 1
聚合包含一个 $project
用于计算要保留的字段列表的成分:
db.recipe.aggregate([{
$project: {
ingredients: 1,
numberOfIngredients: { $size: "$ingredients" }
}
}, {
$out: "recipeNew"
}])
给你:
{ "_id" : ObjectId("58155bc09c924e717c5c4240"), "ingredients" : [......], "numberOfIngredients" : 5 }
{ "_id" : ObjectId("58155bc19c924e717c5c4241"), "ingredients" : [......], "numberOfIngredients" : 3 }
聚合的结果被写入一个新的集合recipeNew
,可以替换现有的recipe
集合
问题2
聚合包含:
- 1
$unwind
删除成分数组
- 1
$group
对每种成分的出现次数求和并按成分分组 _id
- 1
$lookup
将成分集合连接到当前聚合以检索指定成分的所有字段
- 1
$unwind
删除导入成分项数组
- 1
$project
至 select 个要保留的字段
- 1
$out
将结果输出到新的集合
查询是:
db.recipe.aggregate([{
$unwind: "$ingredients"
}, {
$group: { _id: "$ingredients", IngredientsNumber: { $sum: 1 } }
}, {
$lookup: {
from: "ingredients",
localField: "_id",
foreignField: "_id",
as: "ingredientsDB"
}
}, {
$unwind: { path: "$ingredientsDB", preserveNullAndEmptyArrays: true }
}, {
$project: {
ingredientsNumber: "$IngredientsNumber",
name: "$ingredientsDB.name"
}
}, {
$out: "ingredientsTemp"
}])
这给出了:
{ "_id" : ObjectId("5812caaeb4829937f4599b54"), "ingredientsNumber" : 2, "name" : "ingredients5" }
{ "_id" : ObjectId("5812caaeb4829937f4599b53"), "ingredientsNumber" : 1, "name" : "ingredients4" }
{ "_id" : ObjectId("5812caaeb4829937f4599b52"), "ingredientsNumber" : 2, "name" : "ingredients3" }
{ "_id" : ObjectId("5812caaeb4829937f4599b51"), "ingredientsNumber" : 1, "name" : "ingredients2" }
{ "_id" : ObjectId("5812caaeb4829937f4599b50"), "ingredientsNumber" : 2, "name" : "ingredients1" }
此解决方案的缺点:
- 它使用
$project
所以你需要指定要保留的字段
- 您将获得一个新的
ingredientsTemp
集合,其中仅包含食谱中实际存在的成分,因此需要使用 $lookup
进行额外的聚合以将现有集合与您从中获得的集合相结合该聚合:
以下将加入现有的 ingredients
集合和我们创建的集合:
db.ingredients.aggregate([{
$lookup: {
from: "ingredientsTemp",
localField: "_id",
foreignField: "_id",
as: "ingredientsDB"
}
}, {
$unwind: { path: "$ingredientsDB", preserveNullAndEmptyArrays: true }
}, {
$project: {
name: "$name",
ingredientsNumber: "$ingredientsDB.ingredientsNumber"
}
}])
那么你会得到:
{ "_id" : ObjectId("5812caaeb4829937f4599b50"), "name" : "ingredients1", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b51"), "name" : "ingredients2", "ingredientsNumber" : 1 }
{ "_id" : ObjectId("5812caaeb4829937f4599b52"), "name" : "ingredients3", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b53"), "name" : "ingredients4", "ingredientsNumber" : 1 }
{ "_id" : ObjectId("5812caaeb4829937f4599b54"), "name" : "ingredients5", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b57"), "name" : "ingredients6" }
货物:
- 它只使用聚合,所以应该更快
问题 1
我有一个名为 recipe
的集合,其中所有文档都有一个数组字段 ingredients
。我想计算那些数组项并将它们写入新字段 ingredient_count
.
问题 2
还有一个名为 ingredient
的集合。文档有一个 count
字段,它是所有食谱中的总使用次数。
我目前的做法
我现在的解决方案是一个脚本,它聚合集合并逐一更新所有文档:
// PROBLEM 1: update recipe documents
db.recipe.aggregate(
[
{
$project: {
numberOfIngredients: { $size: "$ingredients" }
}
}
]
).forEach(function(recipe) {
db.recipe.updateOne(
{ _id: recipe._id },
{ $set: { incredient_count: recipe.numberOfIngredients } }
)
});
// PROBLEM 2: update ingredient documents
db.ingredient.find().snapshot().forEach(function(ingredient) {
db.ingredient.updateOne(
{ _id: ingredient._id },
{ $set: { count: db.recipe.count({ ingredients: { $in: [ingredient.name] } })) } }
)
});
这非常慢。知道如何更有效地做到这一点吗?
对于这两个问题,可以只执行聚合输出到将替换现有集合的新集合:
问题 1
聚合包含一个 $project
用于计算要保留的字段列表的成分:
db.recipe.aggregate([{
$project: {
ingredients: 1,
numberOfIngredients: { $size: "$ingredients" }
}
}, {
$out: "recipeNew"
}])
给你:
{ "_id" : ObjectId("58155bc09c924e717c5c4240"), "ingredients" : [......], "numberOfIngredients" : 5 }
{ "_id" : ObjectId("58155bc19c924e717c5c4241"), "ingredients" : [......], "numberOfIngredients" : 3 }
聚合的结果被写入一个新的集合recipeNew
,可以替换现有的recipe
集合
问题2
聚合包含:
- 1
$unwind
删除成分数组 - 1
$group
对每种成分的出现次数求和并按成分分组_id
- 1
$lookup
将成分集合连接到当前聚合以检索指定成分的所有字段 - 1
$unwind
删除导入成分项数组 - 1
$project
至 select 个要保留的字段 - 1
$out
将结果输出到新的集合
查询是:
db.recipe.aggregate([{
$unwind: "$ingredients"
}, {
$group: { _id: "$ingredients", IngredientsNumber: { $sum: 1 } }
}, {
$lookup: {
from: "ingredients",
localField: "_id",
foreignField: "_id",
as: "ingredientsDB"
}
}, {
$unwind: { path: "$ingredientsDB", preserveNullAndEmptyArrays: true }
}, {
$project: {
ingredientsNumber: "$IngredientsNumber",
name: "$ingredientsDB.name"
}
}, {
$out: "ingredientsTemp"
}])
这给出了:
{ "_id" : ObjectId("5812caaeb4829937f4599b54"), "ingredientsNumber" : 2, "name" : "ingredients5" }
{ "_id" : ObjectId("5812caaeb4829937f4599b53"), "ingredientsNumber" : 1, "name" : "ingredients4" }
{ "_id" : ObjectId("5812caaeb4829937f4599b52"), "ingredientsNumber" : 2, "name" : "ingredients3" }
{ "_id" : ObjectId("5812caaeb4829937f4599b51"), "ingredientsNumber" : 1, "name" : "ingredients2" }
{ "_id" : ObjectId("5812caaeb4829937f4599b50"), "ingredientsNumber" : 2, "name" : "ingredients1" }
此解决方案的缺点:
- 它使用
$project
所以你需要指定要保留的字段 - 您将获得一个新的
ingredientsTemp
集合,其中仅包含食谱中实际存在的成分,因此需要使用$lookup
进行额外的聚合以将现有集合与您从中获得的集合相结合该聚合:
以下将加入现有的 ingredients
集合和我们创建的集合:
db.ingredients.aggregate([{
$lookup: {
from: "ingredientsTemp",
localField: "_id",
foreignField: "_id",
as: "ingredientsDB"
}
}, {
$unwind: { path: "$ingredientsDB", preserveNullAndEmptyArrays: true }
}, {
$project: {
name: "$name",
ingredientsNumber: "$ingredientsDB.ingredientsNumber"
}
}])
那么你会得到:
{ "_id" : ObjectId("5812caaeb4829937f4599b50"), "name" : "ingredients1", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b51"), "name" : "ingredients2", "ingredientsNumber" : 1 }
{ "_id" : ObjectId("5812caaeb4829937f4599b52"), "name" : "ingredients3", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b53"), "name" : "ingredients4", "ingredientsNumber" : 1 }
{ "_id" : ObjectId("5812caaeb4829937f4599b54"), "name" : "ingredients5", "ingredientsNumber" : 2 }
{ "_id" : ObjectId("5812caaeb4829937f4599b57"), "name" : "ingredients6" }
货物:
- 它只使用聚合,所以应该更快