Python 对内存中的数据运行mongo查询
我有一个mongodb集合,需要每小时对其运行许多计数操作(每个操作都有不同的查询)。当我第一次设置时,收集量很小,这些计数操作大约在一分钟内运行,这是可以接受的。现在它们需要大约55分钟,因此它们几乎连续运行 与每个计数操作相关联的查询相当复杂,我认为没有办法让它们都使用索引运行(即作为计数扫描操作) 我提出的唯一可行的解决方案是:Python 对内存中的数据运行mongo查询,python,mongodb,Python,Mongodb,我有一个mongodb集合,需要每小时对其运行许多计数操作(每个操作都有不同的查询)。当我第一次设置时,收集量很小,这些计数操作大约在一分钟内运行,这是可以接受的。现在它们需要大约55分钟,因此它们几乎连续运行 与每个计数操作相关联的查询相当复杂,我认为没有办法让它们都使用索引运行(即作为计数扫描操作) 我提出的唯一可行的解决方案是: 每小时运行一次完整的收集扫描,将每个文档从数据库中取出 一旦每个文档都在内存中,就自己对其运行所有计数操作 如果没有我的解决方案,服务器每小时会运行几十次完整
- 每小时运行一次完整的收集扫描,将每个文档从数据库中取出
- 一旦每个文档都在内存中,就自己对其运行所有计数操作
对我来说,拥有复杂查询和最佳性能的能力是最好的选择 Python内存数据库: 您可以使用以下选项之一:
- -一个快速、纯Python、非类型化的内存数据库引擎,使用Python语法来管理数据,而不是SQL
- -如果您需要一个简单的数据库,该数据库具有干净的API,无需大量配置即可工作,TinyDB可能是您的正确选择。但不是一个快速的解决方案
最后一个解决方案可能具有最好的性能,但开发和支持它需要更多的时间。以下是我目前用来解决此问题的代码。我对它进行了足够的测试,以使它符合我的用例,但它可能不是100%正确的。我当然不会处理所有可能的查询文档
def check_doc_against_mongo_query(doc, query):
"""Return whether the given doc would be returned by the given query.
Initially this might seem like work the db should be doing, but consider a use case where we
need to run many complex queries regularly to count matches. If each query results in a full-
collection scan, it is often faster to run a single scan fetching the entire collection into
memory, then run all of the matches locally.
We don't support mongo's full query syntax here, so we'll need to add support as the need
arises."""
# Run our check recursively
return _match_query(doc, query)
def _match_query(doc, query):
"""Return whether the given doc matches the given query."""
# We don't expect a null query
assert query is not None
# Check each top-level field for a match, we AND them together, so return on mismatch
for k, v in query.items():
# Check for AND/OR operators
if k == Mongo.AND:
if not all(_match_query(doc, x) for x in v):
return False
elif k == Mongo.OR:
if not any(_match_query(doc, x) for x in v):
return False
elif k == Mongo.COMMENT:
# Ignore comments
pass
else:
# Now grab the doc's value and match it against the given query value
doc_v = nested_dict_get(doc, k)
if not _match_doc_and_query_value(doc_v, v):
return False
# All top-level fields matched so return match
return True
def _match_doc_and_query_value(doc_v, query_v):
"""Return whether the given doc and query values match."""
cmps = [] # we AND these together below, trailing bool for negation
# Check for operators
if isinstance(query_v, Mapping):
# To handle 'in' we use a tuple, otherwise we use an operator and a value
for k, v in query_v.items():
if k == Mongo.IN:
cmps.append((operator.eq, tuple(v), False))
elif k == Mongo.NIN:
cmps.append((operator.eq, tuple(v), True))
else:
op = {Mongo.EQ: operator.eq, Mongo.GT: operator.gt, Mongo.GTE: operator.ge,
Mongo.LT: operator.lt, Mongo.LTE: operator.le, Mongo.NE: operator.ne}[
k]
cmps.append((op, v, False))
else:
# We expect a simple value here, perform an equality check
cmps.append((operator.eq, query_v, False))
# Now perform each comparison
return all(_invert(_match_cmp(op, doc_v, v), invert) for op, v, invert in cmps)
def _invert(result, invert):
"""Invert the given result if necessary."""
return not result if invert else result
def _match_cmp(op, doc_v, v):
"""Return whether the given values match with the given comparison operator.
If v is a tuple then we require op to match with any element.
We take care to handle comparisons with null the same way mongo does, i.e. only null ==/<=/>=
null returns true, all other comps with null return false. See:
https://stackoverflow.com/questions/29835829/mongodb-comparison-operators-with-null
for details.
As an important special case of null comparisons, ne null matches any non-null value.
"""
if doc_v is None and v is None:
return op in (operator.eq, operator.ge, operator.le)
elif op is operator.ne and v is None:
return doc_v is not None
elif v is None:
return False
elif isinstance(v, tuple):
return any(op(doc_v, x) for x in v)
else:
return op(doc_v, v)
def对照mongo查询检查文档(文档,查询):
“”“Return给定的查询是否会返回给定的文档。”。
最初,这可能看起来像DB应该做的工作,但是考虑一个用例
需要定期运行许多复杂的查询来计算匹配项。如果每个查询都会产生一个完整的-
集合扫描,通常运行单个扫描会更快地将整个集合提取到
内存,然后在本地运行所有匹配。
这里我们不支持mongo的完整查询语法,因此需要根据需要添加支持
出现
#递归地运行我们的检查
返回匹配查询(单据,查询)
定义匹配查询(单据,查询):
“”“返回给定文档是否与给定查询匹配。”“”
#我们不希望出现空查询
断言查询不是无
#检查每个顶级字段是否匹配,我们和它们一起检查,因此返回不匹配项
对于query.items()中的k,v:
#检查和/或操作员
如果k==Mongo.AND:
如果不是全部(_匹配_查询(doc,x)用于v中的x):
返回错误
elif k==Mongo.OR:
如果没有任何(_匹配_查询(doc,x)用于v中的x):
返回错误
elif k==Mongo.COMMENT:
#忽略评论
通过
其他:
#现在获取文档的值,并将其与给定的查询值进行匹配
doc\u v=嵌套的dict\u get(doc,k)
如果不匹配文档和查询值(文档v,v):
返回错误
#所有顶级字段匹配,因此返回匹配
返回真值
定义匹配文档和查询值(文档v,查询v):
“”“返回给定的文档和查询值是否匹配。”“”
cmps=[]#我们将它们放在一起,后面的布尔表示否定
#检查操作员
如果isinstance(查询、映射):
#要处理“in”,我们使用元组,否则我们使用运算符和值
对于查询项()中的k,v:
如果k==Mongo.IN:
附加((operator.eq,元组(v),False))
elif k==Mongo.NIN:
附加((operator.eq,元组(v),True))
其他:
op={Mongo.EQ:operator.EQ,Mongo.GT:operator.GT,Mongo.GTE:operator.ge,
Mongo.LT:operator.LT,Mongo.LTE:operator.le,Mongo.NE:operator.NE}[
k]
附加((op,v,False))
其他:
#我们希望这里有一个简单的值,执行相等性检查
cmps.append((operator.eq,query_v,False))
#现在执行每个比较
返回所有(_invert(_match_cmp(op,doc_v,v),invert)的op,v,invert(在cmps中)
def_反转(结果,反转):
“”“如有必要,反转给定的结果。”“”
如果反转else结果,则返回not result
定义匹配cmp(操作,文档v,v):
“”“返回给定值是否与给定比较运算符匹配。
如果v是一个元组,那么我们需要op来匹配任何元素。
我们会小心的
import pandas as pd
from pandas.io.json import json_normalize
data = {
"data_points":[
{"a":1,"b":3,"c":2},
{"a":3,"b":2,"c":1},
{"a":5,"b":4,"d":3}
]
}
# convert json to data frame
df = json_normalize(data["data_points"])
# sum of column `a`
df['a'].sum()
output: 9
# sum of column `c` that has null values.
df['c'].sum()
output: 3.0
# count of column `c` that has null values.
df['c'].count()
output: 2
{
query1count: 12,
query2count: 512312,
query3count: 6
}