pymongo mongodb:如何仅在记录已存在时更新字段?

huangapple go评论59阅读模式
英文:

pymongo mongodb: how to update the field of a record only if it already exists?

问题

假设我有一个如下所示的Mongo文档,其中包含两条记录:

[
    {"a": "a1", "b": "b1", "c": "c1", "d": "d1"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

假设我有一个数据字典如下:

{"c": "c3", "d": "d3", "e": "e3"}

如果字段已存在,我想要找到并更新一条记录,示例如下:

_collection.find_one_and_update(
    {"a": "a1", "b": "b1"},
    {"$set": data},
    # 这里有一些选项?
)

我希望结果如下所示:

[
    {"a": "a1", "b": "b1", "c": "c3", "d": "d3"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

而不是:

[
    {"a": "a1", "b": "b1", "c": "c3", "d": "d3", "e": "e3"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

我应该如何做到这一点?关键是data是动态的,可能包含无法控制的字段(键值对)。


您可以使用以下方法来实现所需的更新操作,即只更新已存在的字段,而不添加新字段:

_collection.update_one(
    {"a": "a1", "b": "b1"},
    {"$set": {key: value for key, value in data.items() if key in {"c", "d"}}}
)

这将只更新data中在文档中已存在的字段("c"和"d"),而不会添加新字段。

英文:

suppose I have a mongo document as below that has two records:

[
    {"a": "a1", "b": "b1", "c": "c1", "d": "d1"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

and suppose I have a data dict as below:

{"c": "c3", "d": "d3", "e": "e3"}

I want to find one and update if the field already exists:

_collection.find_one_and_update(
            {"a": "a1", "b": "b1"},
            {"$set": data},
# SOME OPTIONS HERE?
        )

I want the result to be

[
    {"a": "a1", "b": "b1", "c": "c3", "d": "d3"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

not

[
    {"a": "a1", "b": "b1", "c": "c3", "d": "d3", "e", "e3"},
    {"a": "a2", "b": "b2", "c": "c2", "d": "d2"}
]

How should I do this? The point is that data is dynamic and might contain fields (key-value pairs) out of control.

答案1

得分: 2

这是您提供的代码的翻译部分:

// 在使用聚合管道进行更新的更干净实现的[这里](https://mongoplayground.net/p/GI-DMclMm4z)。
db.collection.update(
  {
    "a": "a1",
    "b": "b1"
  },
  [
    {
      $set: {
        merged: {
          "$mergeObjects": [
            "$$ROOT",
            {
              "c": "c3",
              "d": "d3",
              "e": "e3"
            }
          ]
        }
      }
    },
    {
      $set: {
        merged: {
          "$objectToArray": "$merged"
        },
        rootArray: {
          "$objectToArray": "$$ROOT"
        }
      }
    },
    {
      $set: {
        merged: {
          // 如果键存在于原始文档中,则过滤合并/更新结果
          "$filter": {
            "input": "$merged",
            "as": "m",
            "cond": {
              "$in": [
                "$$m.k",
                "$rootArray.k"
              ]
            }
          }
        }
      }
    },
    {
      "$replaceRoot": {
        "newRoot": {
          "$arrayToObject": "$merged"
        }
      }
    }
  ]
)
// 对于通用解决方案,可以首先使用“$mergeObjects”对提供的负载和“$$ROOT”文档执行无条件更新。
// 然后使用“$objectToArray”将更新结果和“$$ROOT”文档转换为键值对数组。
// 使用“$filter”在更新结果中使用“$in”来查看键是否在原始文档中。
// 最后,使用“$merge”将结果更新回集合中。
db.collection.aggregate([
  {
    $match: {
      "a": "a1",
      "b": "b1"
    }
  },
  // 首先计算无条件更新/合并结果
  {
    $set: {
      merged: {
        "$mergeObjects": [
          "$$ROOT",
          {
            "c": "c3",
            "d": "d3",
            "e": "e3"
          }
        ]
      }
    }
  },
  {
    $set: {
      merged: {
        "$objectToArray": "$merged"
      },
      rootArray: {
        "$objectToArray": "$$ROOT"
      }
    }
  },
  {
    $set: {
      merged: {
        // 如果键存在于原始文档中,则过滤合并/更新结果
        "$filter": {
          "input": "$merged",
          "as": "m",
          "cond": {
            "$in": [
              "$$m.k",
              "$rootArray.k"
            ]
          }
        }
      }
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$arrayToObject": "$merged"
      }
    }
  },
  {
    "$merge": {
      "into": "collection",
      "on": "_id"
    }
  }
])

Mongo Playground

英文:

Edit: Thanks to @cmgchess' comment, here is a cleaner implementation with update with aggregation pipeline.

db.collection.update({
  "a": "a1",
  "b": "b1"
},
[
  {
    $set: {
      merged: {
        "$mergeObjects": [
          "$$ROOT",
          {
            "c": "c3",
            "d": "d3",
            "e": "e3"
          }
        ]
      }
    }
  },
  {
    $set: {
      merged: {
        "$objectToArray": "$merged"
      },
      rootArray: {
        "$objectToArray": "$$ROOT"
      }
    }
  },
  {
    $set: {
      merged: {
        // filter the merge/update result if the key exists in original document
        "$filter": {
          "input": "$merged",
          "as": "m",
          "cond": {
            "$in": [
              "$$m.k",
              "$rootArray.k"
            ]
          }
        }
      }
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$arrayToObject": "$merged"
      }
    }
  },
  
])

For a generic solution, you can first use $mergeObjects to perform an unconditional update of the payload you are giving and the $$ROOT document. Then use $objectToArray to convert the update result and $$ROOT document to arrays of k-v tuples. Perform a $filter on the update result with $in to see if the key is in the original document. Finally, use $merge to update the result back into the collection.

db.collection.aggregate([
  {
    $match: {
      "a": "a1",
      "b": "b1"
    }
  },
  // compute the unconditional update/merge result first
  {
    $set: {
      merged: {
        "$mergeObjects": [
          "$$ROOT",
          {
            "c": "c3",
            "d": "d3",
            "e": "e3"
          }
        ]
      }
    }
  },
  {
    $set: {
      merged: {
        "$objectToArray": "$merged"
      },
      rootArray: {
        "$objectToArray": "$$ROOT"
      }
    }
  },
  {
    $set: {
      merged: {
        // filter the merge/update result if the key exists in original document
        "$filter": {
          "input": "$merged",
          "as": "m",
          "cond": {
            "$in": [
              "$$m.k",
              "$rootArray.k"
            ]
          }
        }
      }
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$arrayToObject": "$merged"
      }
    }
  },
  {
    "$merge": {
      "into": "collection",
      "on": "_id"
    }
  }
])

Mongo Playground

huangapple
  • 本文由 发表于 2023年5月31日 23:51:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76375304.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定