【问题标题】:Update/remove subdocument in MongoDB collection更新/删除 MongoDB 集合中的子文档
【发布时间】:2013-08-23 15:42:21
【问题描述】:

注意:请使用mongodb shell执行代码。

假设我有一份学生文档,如下所示

{
    "_id" : 4,
"grades" : [
    {
        "grade" : 80,
        "mean" : 75,
        "std" : 8
    },
    {
        "grade" : 85,
        "mean" : 90,
        "std" : 5
    },
    {
        "grade" : 85,
        "mean" : 90,
        "std" : 5
    },
    {
        "grade" : 85,
        "mean" : 95,
        "std" : 6
    },
    {
        "grade" : 90,
        "mean" : 85,
        "std" : 5
    }
]
}

我们有两个问题:

问题 1 :假设您要更新所有子文档 _id=4 && grades.grade = 85 && grades.std = 5,std=6 您将编写如下

db.students.update( {'$and':[ { _id: 4},{ "grades.grade": 85 }, {"grades.std": 5 } ]}, { $set: { "grades.$.std" : 6 } } );

现在,如果您多次(3 次)执行上述语句,那么理想情况下它应该更新第 2、3 个子文档

但最后一个子文档也正在更新,因为它有 std=5 匹配,但我们给出的条件是 $and 而不是 $or,然后最后一个文档怎么更新了?

问题 2: 现在假设您要删除与查询条件匹配的子文档本身。 我尝试了以下语句

db.students.update({_id:4,'grades.grade':85},{'$unset':{'grades.$':1}})

因为 $unset 在子文档/数组的情况下会设置 null,如何解决这个问题?

如何在您的 mongodb 控制台中复制?

db.students.insert( { "_id" : 4, "grades" : [ { grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 85, mean: 90, std: 5 }, { grade: 85, mean: 95, std: 6 }, { grade: 90, mean: 85, std: 5 } ] });

【问题讨论】:

    标签: mongodb mongodb-query mongodb-update


    【解决方案1】:

    您需要使用$elemMatch 来匹配数组内部。 MongoDB 中的查询规则指定对于数组元素的条件,任何条件匹配的任何数组元素都被视为匹配。使用$elemMatch,所有条件都需要在同一个元素上匹配。

    【讨论】:

    • 你能写下同样的shell语句吗?
    • 谢谢 .. 我尝试使用 $elemMatch 并且它有效。但是对于第二个问题。我不想执行 2 update 语句只是为了删除一个子文档。有没有办法?通过一条语句执行
    【解决方案2】:

    点表示法匹配其条件可以在任何数组元素中满足的文档。

    如果您只想在一个数组元素中匹配符合所有条件的文档(至少),那么正如 drmirror 已经回答的那样,您需要使用运算符 $elemMatch,另请参阅:$elemMatch Operator

    你的情况是:

    db.students.update( {'$and':[ { _id: 4},{ "grades":{"$elemMatch": {"grade": 85, "std": 5 }}} ]}, { $set: { "grades.$.std" : 6 } } );
    

    关于您问题的第二部分,如何删除数组元素,请参阅以下帖子。

    In mongoDb, how do you remove an array element by its index

    简而言之,这是一个两步操作,首先将元素设置为null(就像您已经做的那样),然后将其 $pull(s) 关闭。

    【讨论】:

      【解决方案3】:

      '$' 运算符只更新给定的第一个匹配项:

      db.students.insert({ "_id" : 4, "grades" : [ 
          { grade: 80, mean: 75, std: 8 }, 
          { grade: 85, mean: 90, std: 5 }, 
          { grade: 85, mean: 90, std: 5 }, 
          { grade: 85, mean: 95, std: 6 }, 
          { grade: 90, mean: 85, std: 5 } ]});
      

      要更新,您需要像这样使用$elemMatch 定位:

      db.students.update( { _id: 4, "grades": {$elemMatch: {"grade": 85, "std": 5 }}}, 
                          { $set: { "grades.$.std" : 6 } });
      

      但是,您有两个匹配 {"grades.grade": 85, "grades.std": 5} 和 $ 的成绩只更新第一个,因此您需要循环直到全部更新:

      db.students.update( { _id: 4, "grades": {$elemMatch: {"grade": 85, "std": 5 }}}, 
                          { $set: { "grades.$.std" : 6 } })
      while (db.getLastErrorObj()['n'] > 0) {
          db.students.update( { _id: 4, "grades": {$elemMatch: {"grade": 85, "std": 5 }}}, 
                              { $set: { "grades.$.std" : 6 } })
      }
      

      问题 2:同样适用 - 您需要循环到 $pull 匹配元素:

      db.students.update({_id:4,'grades': {$elemMatch: {'grade':85}}}, {'$pull': {'grades': {'grade':85}}})
      

      【讨论】:

      • 感谢第一个问题的回答,我已经尝试过@drmirror 的回答。 $pull 将在子文档的情况下设置 null,然后我必须执行 2 个更新语句,一个用于将其更新为 null,另一个用于从子文档中删除 null。一个更新语句是否可能?
      • 在 2.4 中,它拉取子文档而不是设置为空。
      • 再次感谢:),抱歉没有投票,我对stackoverflow有点新
      • 使用 do while 可能更简单
      【解决方案4】:

      对于问题 #1,请参阅:https://stackoverflow.com/a/20601288/773953

      基本上你必须构建你想要手动设置的数组。

      【讨论】:

        猜你喜欢
        • 2017-08-22
        • 1970-01-01
        • 1970-01-01
        • 2017-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        相关资源
        最近更新 更多