python – 即使没有改变任何东西,Pymongo replace_one modified_count总是1

为什么以及如何这样工作?

item = db.test.find_one()
result = db.test.replace_one(item, item)
print(result.raw_result)
# Gives: {u'n': 1, u'nModified': 1, u'ok': 1, 'updatedExisting': True}
print(result.modified_count)
# Gives 1

当mongodb shell中的等价物总是为0时

item = db.test.findOne()
db.test.replaceOne(item, item)
# Gives: {"acknowledged" : true, "matchedCount" : 1.0, "modifiedCount" : 0.0}

如何获得一致的结果并正确检测替换何时实际更改数据?

解决方法:

这是因为MongoDB以二进制(BSON)格式存储文档. BSON文档中的键值对可以有任何顺序(除了_id始终是第一个).
让我们先从mongo shell开始吧. mongo shell在读取和写入数据时保留键顺序.
例如:

> db.collection.insert({_id:1, a:2, b:3})
{ "_id" : 1, "a" : 2, "b" : 3 }

如果您使用此文档值执行replaceOne(),则会避免修改,因为存在现有的BSON.

> var doc = db.collection.findOne()
> db.collection.replaceOne(doc, doc)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }

但是,如果更改字段的顺序,它将检测到修改

> var doc_2 = {_id:1, b:3, a:2}
> db.collection.replaceOne(doc_2, doc_2)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

让我们进入Python世界.默认情况下,PyMongo将BSON文档表示为Python字典,未定义Python字典中的键的顺序.因此,您无法预测它将如何序列化为BSON.根据你的例子:

> doc = db.collection.find_one()
{u'_id': 1.0, u'a': 2.0, u'b': 3.0}

> result = db.collection.replace_one(doc, doc)
> result.raw_result
{u'n': 1, u'nModified': 1, u'ok': 1, 'updatedExisting': True}

如果对您的用例很重要,一种解决方法是使用bson.SON.例如:

> from bson import CodecOptions, SON
> opts=CodecOptions(document_class=SON)
> collection_son = db.collection.with_options(codec_options=opts)
> doc_2 = collection_son.find_one()
SON([(u'_id', 1.0), (u'a', 2.0), (u'b', 3.0)])

> result = collection_son.replace_one(doc_2, doc_2)
{u'n': 1, u'nModified': 0, u'ok': 1, 'updatedExisting': True}

您还可以观察到在PyMongo(v3.3.0)中使用了bson.SON,即_update() method.另请参阅相关文章:PyMongo and Key Order in SubDocuments.

更新以回答其他问题:

据我所知,没有一个“标准”函数可以将嵌套字典转换为SON.虽然您可以自己编写自定义字典到SON转换器,例如:

def to_son(value):
     for k, v in value.iteritems():
         if isinstance(v, dict):
             value[k] = to_son(v)
         elif isinstance(v, list):
             value[k] = [to_son(x) for x in v]
     return bson.son.SON(value)
# Assuming the order of the dictionary is as you desired. 
to_son(a_nested_dict)

或者使用bson作为中间格式

from bson import CodecOptions, SON, BSON
nested_bson = BSON.encode(a_nested_dict)
nested_son = BSON.decode(nested_bson, codec_options=CodecOptions(document_class=SON))

一旦处于SON格式,您可以使用SON.to_dict()转换回Python字典

上一篇:mongodb中的逻辑运算符使用python进行查询


下一篇:CentOS 7 下 RabbitMQ 集群搭建