如何在mongo中有条件地插入文档?

时间:2012-12-07 15:30:52

标签: mongodb pymongo

我有一份文件

{ key : 'key1', value : 'value1', update_time : 100 }

我希望仅在更新(更长)的更新时间内进行更改。我现在正在做的是:

def update_key1(new_value, new_time):
    record = find_one( { key : 'key1' } )
    if not record or record['update_time'] < new_time:
        update( { key : 'key1', value : new_value, update_time : new_time }, upsert=True)

显然这是对数据库的额外往返,但更重要的是,文档没有锁定,并发调用可能会导致数据库中剩余较低的new_time值。是否有办法在条件为真时执行upsert?

编辑:只是为了澄清,目的不是为每个密钥创建多个文档,然后对查找进行排序。虽然这可以解决我的问题,但这些值会发生很大的变化并且会浪费很多空间。

5 个答案:

答案 0 :(得分:1)

您可以进行conditional更新:

db.MyObjects.update( { key : "key1", value: { $lt : aValue }},
                    { $set : { value : aValue }});

答案 1 :(得分:1)

如果WPCoder使用和upsert = True的响应不是您正在寻找的内容,那么您可能需要$ setOnInsert,但尚未实现:https://jira.mongodb.org/browse/SERVER-340 。它应该实现2.4.0。

答案 2 :(得分:1)

如果不能原子地完成整个事情,那么你想要做出改变的现有条件有两种,你可以原子地处理它们:

  • 没有密钥记录
  • 该密钥的记录存在且其update_time早于new_time

更新密钥的现有记录:

def update_if_stale(key, new_value, new_time):
    collection.update({'key': key,
                       'update_time': {'$lt': new_time}
                       },
                      {'$set': {'value': new_value,
                                'update_time': new_time
                                }
                       }
                      )

如果以前不存在密钥记录,则插入:

def insert_if_missing(key, new_value, new_time):
    collection.update({'key': key},
                      {'$setOnInsert': {'value': new_value,
                                        'update_time': new_time
                                        }
                       },
                      upsert=True
                      )

(MongoDB 2.4中添加了$setOnInsert

您可以将这些组合在一起以获得所需内容,例如:

def update_key(key, new_value, new_time):
    insert_if_missing(key, new_value, new_time)        
    update_if_stale(key, new_value, new_time)

但是,根据系统中可能的删除/插入时间标度,您可能需要多次调用(更新/插入/更新)或其他恶作剧。

除此之外:如果您希望记录丢失,update_time字段将被视为要更新的陈旧记录,请将{'$lt': new_time}}更改为{'$not': {'$gte': new_time}}

答案 3 :(得分:1)

如果您在key上添加unique constraint,则updateOne带有update_time过滤器和upsert=True

  1. 插入丢失的记录
  2. 更新陈旧记录
  3. 当您尝试使用陈旧的输入进行更新时引发错误(因为更新不符合过滤条件,并且由于约束条件插入将失败)
collection.updateOne({'key': key,
                      'update_time': {'$lt': new_time}
                      },
                     {'$set': {'value': new_value,
                               'update_time': new_time
                               }
                      },
                     upsert=True
                     )

您可以捕获错误并检查code: 11000(违反唯一约束)以专门处理那些尝试更新为过去状态的情况。

答案 4 :(得分:0)

似乎您正在寻找findAndModify。对不起,我不熟悉python,所以我无法给你一个代码片段,但命令应该非常简单。