【问题标题】:Update multiple docs in a collection using switch case使用 switch case 更新集合中的多个文档
【发布时间】:2017-01-23 16:20:29
【问题描述】:

我在我的 NodeJS 应用程序中使用 MongoDB 本机驱动程序。

我的数据库中有一个shifts 集合需要更新。我的班次集合中的示例文档

{
    "_id" : ObjectId("588425105560bd2ba0065fa4"),
    "from" : ISODate("2017-01-23T03:20:00.000Z"),
    "to" : ISODate("2017-01-23T06:20:00.000Z"),
    "jobId" : ObjectId("586efda790541421b0432897"),
    "hourlyRate" : 15
}

{
    "_id" : ObjectId("588425105560bd2ba0065fa5"),
    "from" : ISODate("2017-01-25T03:20:00.000Z"),
    "to" : ISODate("2017-01-25T06:20:00.000Z"),
    "jobId" : ObjectId("586efda790541421b0432897"),
    "hourlyRate" : 15
}

我需要做的是以下 -

更新所有符合条件的文档的hourlyRate

  • 匹配jobId(很简单)
  • 如果from 是工作日,则设置hourlyRate = 20
  • 如果from 是星期六,则设置hourlyRate = 25
  • 如果from 是星期日,则设置hourlyRate = 30

我希望尽可能在单个查询中完成。

到目前为止我的解决方案:

使用 switch case 并使用日期聚合函数中的 $dayOfWeek 确定日期类型。但是,我无法将 switch 与 updateMany 结合使用。

任何帮助将不胜感激。

【问题讨论】:

    标签: node.js mongodb switch-statement mongodb-query aggregation-framework


    【解决方案1】:

    您可以使用特殊运算符运行以下聚合管道,例如 $switch,这是 MongoDB Server 3.4 及更高版本中的新功能:

    MongoDB 服务器 3.4

    db.collection('shifts').aggregate([
        {
            "$match": {
                "jobId": ObjectId(job._id),
                "from": { "$gte": new Date() }
            }
        },
        {
            "$project": {
                "hourlyRate": {
                    "$switch": {
                        "branches": [
                            {
                                "case": { 
                                    "$not": { 
                                        "$in": [
                                            { "$dayOfWeek": "$from" }, 
                                            [1, 7] 
                                        ] 
                                    } 
                                }, 
                                "then": 20 
                            },
                            { 
                                "case": { 
                                    "$eq": [
                                        { "$dayOfWeek": "$from" }, 
                                        7
                                    ] 
                                }, 
                                "then": 25 
                            },
                            { 
                                "case": { 
                                    "$eq": [
                                        { "$dayOfWeek": "$from" }, 
                                        1 
                                    ] 
                                }, 
                                "then": 30 
                            }
                        ]
                    }   
                }               
            }
        }       
    ], function(err, docs) {
        var ops = [],
            counter = 0;
    
        docs.forEach(function(doc) {
            ops.push({
                "updateOne": {
                    "filter": { "_id": doc._id },
                    "update": { "$set": { "hourlyRate": doc.hourlyRate } }
                }
            });
            counter++;
    
            if (counter % 500 === 0) {
                db.collection('shifts').bulkWrite(ops, function(err, r) {
                    // do something with result
                });
                ops = [];
            }
        })
    
        if (counter % 500 !== 0) {
            db.collection('shifts').bulkWrite(ops, function(err, r) {
                // do something with result
            }
        }       
    });
    

    MongoDB 服务器 3.2

     db.collection('shifts').aggregate([
        {
            "$match": {
                "jobId": ObjectId(job._id),
                "from": { "$gte": new Date() }
            }
        },
        {
            "$project": {
                "hourlyRate": {
                    "$cond": [
                        {
                            "$not": { 
                                "$setIsSubset": [
                                    [{ "$dayOfWeek": "$from" }], 
                                    [1, 7] 
                                ] 
                            } 
                        }, 20,                                
                        { 
                            "$cond": [
                                { "$eq": [
                                    { "$dayOfWeek": "$from" }, 
                                    7
                                ] },
                                25,
                                { 
                                    "$cond": [ 
                                        { "$eq": [
                                            { "$dayOfWeek": "$from" }, 
                                            1 
                                        ] },
                                        30,
                                        "$hourlyRate"
                                    ]
                                }
                            ]
                        }                   
                    ]                   
                }               
            }
        }
    ], function(err, docs) {
        var ops = [],
            counter = 0;
    
        docs.forEach(function(doc) {
            ops.push({
                "updateOne": {
                    "filter": { "_id": doc._id },
                    "update": { "$set": { "hourlyRate": doc.hourlyRate } }
                }
            });
            counter++;
    
            if (counter % 500 === 0) {
                db.collection('shifts').bulkWrite(ops, function(err, r) {
                    // do something with result
                });
                ops = [];
            }
        })
    
        if (counter % 500 !== 0) {
            db.collection('shifts').bulkWrite(ops, function(err, r) {
                // do something with result
            }
        }       
    })
    

    【讨论】:

    • 我只是再次测试它,我意识到执行此查询会覆盖班次集合中的所有文档。例如,如果我使用 jobId = 1 的 $match 条件执行上述查询,那么它会正确更新 jobId = 1 的所有文档,但同时删除所有其他文档!你能检查一下吗?谢谢
    • 请检查我的问题以了解我如何在聚合中使用 $match。我正在使用您答案的 $addFields 版本(用于 mongo 3.4 的版本)
    • 我认为这就是 $out 的工作方式。 “如果 $out 操作指定的集合已经存在,那么在聚合完成后,$out 阶段会自动将现有集合替换为新的结果集合”。但是我不想替换我的整个集合,因为我只更新特定 jobIds 的 $hourlyRate 而不是所有文档。
    • 我的错,完全错过了$match 过滤器部分,因此通过$out 进行了“完整”集合更新。尽管如此,我已经用另一种使用批量 API 的方法更新了我的答案,请检查。
    • 太棒了。我会接受这个答案。你需要做的只是一个小的更正..在 ops.push 中,你需要使用 doc._id 而不是 doc.id.. 整个解决方案工作正常:)。你能编辑一下吗?
    猜你喜欢
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多