Pymongo在成功更新/删除后返回None
pymongo的文档上说:
默认情况下,系统会要求服务器确认更新是否成功,如果出现错误,会抛出
OperationFailure
。
我在用Pymongo 2.7.1和Python 2.7.5。以下操作返回None
,无论操作是否成功:
user_db.update({'email_id': user_email}, {'$pull': {'friend_list': existing_friend}})
user_db.update({'email_id': user_email}, {'$pull': {'friend_list': non_existing_friend}})
在第二个语句中,我试图从friend_list
中删除一个不存在的东西。
使用remove
时也是这样。它也返回None,无论操作是否成功,也就是说,不管它是否真的删除了文档:
user_db.remove({'name': 'john'}
我传入了w=1
,然后收到了以下响应:
{u'ok': 1.0, u'err': None, u'connectionId': 12037, u'n': 1, u'updatedExisting': True, u'lastOp': Timestamp(1403099751, 1)}
当我手动检查数据库时,发现它确实从friend_list
中删除了名字。现在如果我再次运行同样的操作,也就是试图删除一个在friend_list
中不存在的名字:
{u'ok': 1.0, u'err': None, u'connectionId': 12037, u'n': 1, u'updatedExisting': True, u'lastOp': Timestamp(1403099873, 1)}
结果和之前一样。
那么我怎么知道更新和删除操作是否成功呢?
编辑:正如回答中提到的,我之前使用的是连接而不是MongoClient。现在我已经更新了,remove
可以正常工作了。不过update
还是不太正常:
>>> conn = pymongo.MongoClient(MONGOHQ_URL)
>>> db = conn['test']
>>> test_collection = db.test
>>>
>>> test_collection.insert({'name': 'john'})
ObjectId('53a25612a760360253920619')
>>>
>>> test_collection.update({'name': 'john'}, {'$addToSet': {'friends': 'merry'}})
{u'ok': 1.0, u'err': None, u'connectionId': 12317, u'n': 1, u'updatedExisting': True, u'lastOp': Timestamp(1403147936, 1)}
>>>
>>> test_collection.update({'name': 'john'}, {'$pull': {'friends': 'merry'}})
{u'ok': 1.0, u'err': None, u'connectionId': 12317, u'n': 1, u'updatedExisting': True, u'lastOp': Timestamp(1403147959, 1)}
>>>
>>> test_collection.update({'name': 'john'}, {'$pull': {'friends': 'merry'}})
{u'ok': 1.0, u'err': None, u'connectionId': 12317, u'n': 1, u'updatedExisting': True, u'lastOp': Timestamp(1403147963, 1)}
>>>
最后一个语句试图从列表中删除一个项目,尽管这个项目已经不在列表中了。
2 个回答
我猜你正在使用 pymongo.Connection
,这个连接方式默认是进行不确认写入的,也就是说,它不会告诉你更新操作的结果。
不过,Connection已经不推荐使用了;你应该改用 pymongo.MongoClient
。MongoClient有很多优点,其中之一就是它默认会进行确认写入。如果你使用MongoClient,你就能得到每次更新的相关信息:
>>> import pymongo
>>> collection = pymongo.MongoClient().test.collection
>>> collection.insert({'_id': 1, 'array': ['a']})
1
>>> collection.update({'_id': 1}, {'$pull': {'array': 'a'}})
{u'nModified': 1, u'ok': 1, u'n': 1, 'updatedExisting': True, u'electionId': ObjectId('53a1a6bd30492fdb9fb5610a'), u'lastOp': Timestamp(1403111261, 1)}
你把几个术语搞混了。你提到的文档部分其实是在说写入确认,而不是更新了多少个文档。
简单来说,当驱动程序向MongoDB发送写入命令时,如果你使用的是默认的写入确认设置(w=1
),那么如果MongoDB没有确认更新成功,驱动程序就会报OperationFailure
错误。你可以在MongoDB的页面上找到更多详细信息。
实际上,你的两个更新都是成功的(MongoDB确认了更新已应用)。所以你没有收到错误。你遇到的问题是你的更新查询并没有实际更新任何文档。
如果你在MongoDB shell v2.6+中执行这个更新,而没有任何文档被修改,你会得到这样的结果:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
而一个实际改变了文档的更新结果会是这样的:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
补充:
我当时使用的是在Ubuntu上通过apt-get
安装的旧版本pymongo(2.6.3),更新查询的响应和你得到的是一样的。
我卸载了python-pymongo包,并使用pip
更新到最新版本的pymongo。对于修改了文档的更新,我得到了以下响应:
{'updatedExisting': True, u'nModified': 1, u'ok': 1, u'n': 1}
我在MongoDB 2.6上测试了这个代码。
from pymongo import MongoClient
m = MongoClient('localhost', 27017)
db = m.test
print db.test.update({}, {'$pull': {'a' : 1}})
补充2
这是因为你使用的是MongoDB 2.4。如果你在MongoHQ使用的是沙盒/免费版本,目前无法更改这个设置(链接)。
MongoDB 2.6在更新命令的响应中返回了updatedExisting
字段(这个字段对你很重要)。我猜这是因为在2.6版本中更改了写入协议:
新的写入操作协议将写入关注与写入操作结合在一起,消除了单独使用getLastError命令的需要。写入方法现在返回写入操作的状态,包括错误信息。