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

Mongodb 如何在mongo中有条件地向上插入文档?,mongodb,pymongo,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 : 'k

我有一份文件

{ 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)
def更新键1(新值、新时间):
record=find_one({key:'key1'})
如果未记录或记录['update_time']<新时间:
更新({key:'key1',value:new_value,update_time:new_time},upsert=True)
显然,这是到数据库的额外往返,但更重要的是,文档没有锁定,并发调用可能会导致数据库中剩余的较低的new_time值。是否有方法仅在条件为真时执行upsert


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

似乎是您要寻找的。抱歉,我不熟悉python,因此无法提供代码片段,但命令应该非常简单。

您可以执行以下更新:


如果WPCoder对and upsert=True的响应不是您想要的,那么您可能需要$setOnInsert,它尚未实现:。它应该在2.4.0中实现。

除了能够以原子方式完成整个任务外,还有两种现有情况需要进行更改,您可以以原子方式处理每种情况:

  • 不存在密钥的记录
  • 密钥的记录存在,并且其
    更新时间
    早于
    新时间
更新密钥的现有记录:

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
                      )
$setOnInsert
已添加到MongoDB 2.4中)

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

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\u time
字段的记录视为要更新的过期记录,请将
{'$lt':new\u time}
更改为
{'$not':{'$gte':new\u time}

如果您添加了一个on
,然后使用
update\u time
过滤器和
upsert=True

  • 插入丢失的记录
  • 更新过期记录
  • 尝试使用过时输入进行更新时引发错误(因为更新将与筛选条件不匹配,并且插入将由于约束而失败)

  • 您可以捕获错误并检查
    code:11000
    (唯一约束冲突),以专门处理尝试更新到过去状态的情况。

    我认为findAndModify无法解决此问题。问题产生于确定是否对记录执行修改。我在文档中看不到这样做的方法。但是如果对象不存在,我想插入它。如果我把它改成upsert,它会添加一个新文档,如果它不是最新的,对吗?是的,如果你的查询是正确的,它应该是这样工作的。确保提供了您的_id列,否则它将创建一个新文档。@WiredPairie执行upsert的原因是您不知道是否存在匹配的文档;你怎么知道它要提供的id?你不需要为
    \u id
    @wiredPairie使用
    ObjectId
    ,你的建议有效,但会导致“重复密钥错误收集”。有没有更干净的方法呢?我也有过类似的问题!你能解决这个问题吗?我有完全相似的用例,即使在使用$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)
    
    collection.updateOne({'key': key,
                          'update_time': {'$lt': new_time}
                          },
                         {'$set': {'value': new_value,
                                   'update_time': new_time
                                   }
                          },
                         upsert=True
                         )