【问题标题】:Update multiple documents with aggregated values in MongoDB在 MongoDB 中使用聚合值更新多个文档
【发布时间】:2017-06-23 19:06:43
【问题描述】:

问题 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] } })) } }
    )
});

非常慢。知道如何更有效地做到这一点吗?

【问题讨论】:

    标签: mongodb aggregation-framework updates document


    【解决方案1】:

    对于这两个问题,可以只执行聚合,输出到将替换现有集合的新集合:

    问题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 选择要保留的字段
    • 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" }
    

    货物:

    • 它只使用聚合,所以应该更快

    【讨论】:

    • 这很好,感谢您的精彩解释和示例。
    猜你喜欢
    • 2018-12-14
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-25
    • 2023-03-17
    相关资源
    最近更新 更多