如何检查pymongo游标是否有查询结果
我需要检查一个 find
语句是否返回了非空的查询结果。
我之前是这样做的:
query = collection.find({"string": field})
if not query: #do something
然后我意识到我的 if
语句从来没有被执行,因为 find
返回的是一个游标,不管查询结果是空还是不空。
因此,我查看了 文档,发现有两个方法可以帮助我:
count(with_limit_and_skip=False)
,根据描述:返回这个查询结果集中文档的数量。
这似乎是检查的好方法,但这意味着我需要计算游标中的所有结果,以知道它是否为零,对吧?这样做有点耗费资源吧?
retrieved
,根据描述:到目前为止检索到的文档数量。
我在一个空查询集上测试过,它返回零,但我不太清楚它具体是干什么的,也不知道这是否适合我。
那么,检查一个 find()
查询是否返回空结果集的最佳方法是什么呢?上面提到的方法中有哪个适合这个目的吗?性能方面怎么样?还有其他方法吗?
为了更清楚:我需要知道查询是否为空,并且我想找到在性能和符合 Python 风格方面的最佳方法。
6 个回答
我最后选择使用简单的计数器,因为我不想无缘无故地调用服务器两次:
cursor = someCollection.find( query )
ct = 0
for doc in cursor:
ct += 1
# some code
if ct == 0:
# cursor was empty
根据我的测试,最快的方法是
if query.first():
# do something
In [51]: %timeit query = MyMongoDoc.objects(); query.first()
100 loops, best of 3: 2.12 ms per loop
In [52]: %timeit query = MyMongoDoc.objects(); query.count()
100 loops, best of 3: 4.28 ms per loop
(使用的是MongoDB 2.6.7,日期是2015年3月26日)
另一种解决办法是把游标转换成列表。如果游标没有任何数据,那么就返回一个空列表;如果有数据,就返回一个包含所有数据的列表。
doc_list = collection.find({}); #find all data
have_list = True if len(list(doc_list)) else False;
你可以试试用 find_one
来代替 find
吗?这样你就可以直接检查一下有没有结果,或者结果是 None
(表示没有找到)。如果“string”这个字段是被索引的,你可以传入 fields = {"string":1, "_id" :0}
,这样就能让查询只用索引来查找,这样速度会更快哦。
编辑: 虽然在2014年这个说法是对的,但现代版本的pymongo和MongoDB已经改变了这个行为。购买时请注意:
.count()
是用来查找查询结果数量的正确方法。这个 count()
方法不会消耗你的游标(cursor)里的数据,所以你可以在遍历结果集之前安全地使用 .count()
来检查数量。
在MongoDB 2.4中,count
方法的性能得到了很大提升。唯一可能会让你的 count
变慢的因素是查询是否有索引。如果你想知道查询上是否有索引,可以执行类似下面的操作:
query = collection.find({"string": field})
print query.explain()
如果你在结果中看到 BasicCursor
,那么你需要在这个查询的 string
字段上创建一个索引。
编辑: 正如 @alvapan 指出的那样,pymongo 在3.7+版本中弃用了这个 方法,现在建议你在单独的查询中使用 count_documents
。
item_count = collection.count_documents({"string": field})
正确计算查询返回的项目数量的方法是,在遍历查询后检查 .retrieved
计数器,或者一开始就使用 enumerate
来遍历查询:
# Using .retrieved
query = collection.find({"string": field})
for item in query:
print(item)
print('Located {0:,} item(s)'.format(query.retrieved))
或者,另一种方法是:
# Using the built-in enumerate
query = collection.find({"string": field})
for index, item in enumerate(query):
print(item)
print('Located {0:,} item(s)'.format(index+1))