Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 对内存中的数据运行mongo查询_Python_Mongodb - Fatal编程技术网

Python 对内存中的数据运行mongo查询

Python 对内存中的数据运行mongo查询,python,mongodb,Python,Mongodb,我有一个mongodb集合,需要每小时对其运行许多计数操作(每个操作都有不同的查询)。当我第一次设置时,收集量很小,这些计数操作大约在一分钟内运行,这是可以接受的。现在它们需要大约55分钟,因此它们几乎连续运行 与每个计数操作相关联的查询相当复杂,我认为没有办法让它们都使用索引运行(即作为计数扫描操作) 我提出的唯一可行的解决方案是: 每小时运行一次完整的收集扫描,将每个文档从数据库中取出 一旦每个文档都在内存中,就自己对其运行所有计数操作 如果没有我的解决方案,服务器每小时会运行几十次完整

我有一个mongodb集合,需要每小时对其运行许多计数操作(每个操作都有不同的查询)。当我第一次设置时,收集量很小,这些计数操作大约在一分钟内运行,这是可以接受的。现在它们需要大约55分钟,因此它们几乎连续运行

与每个计数操作相关联的查询相当复杂,我认为没有办法让它们都使用索引运行(即作为计数扫描操作)

我提出的唯一可行的解决方案是:

  • 每小时运行一次完整的收集扫描,将每个文档从数据库中取出
  • 一旦每个文档都在内存中,就自己对其运行所有计数操作
如果没有我的解决方案,服务器每小时会运行几十次完整的收集扫描。在我的解决方案中,服务器只运行一个。这就把我带到了一个奇怪的地方,在那里我需要接受复杂的查询并自己重新实现它们,这样我就可以每小时计算出自己的计数

所以我的问题是,mongo驱动程序(在我的例子中是pymongo,但我总的来说很好奇)是否支持解释查询文档,但在本地针对内存中的数据而不是mongodb服务器上的数据运行它们

起初,这感觉像是一个奇怪的请求,但实际上,在我的特定用例中,在相当多的地方,这种方法可能会大大减轻数据库的负载。所以我想知道它是否会在其他生产部署中不时出现。

MongoDB内存存储引擎 如果您想使用MongoDB语法仅在RAM中使用复杂查询来处理数据,您可以将MongoDB配置为完全避免磁盘I/O。
对我来说,拥有复杂查询和最佳性能的能力是最好的选择

Python内存数据库: 您可以使用以下选项之一:

  • -一个快速、纯Python、非类型化的内存数据库引擎,使用Python语法来管理数据,而不是SQL
  • -如果您需要一个简单的数据库,该数据库具有干净的API,无需大量配置即可工作,TinyDB可能是您的正确选择。但不是一个快速的解决方案
它们应该允许直接在RAM中处理数据,但我不确定这是否比前面的选项更好

自己的自定义解决方案(例如,用Python编写) 有些服务仅在应用程序级别处理RAM中的数据。如果您的解决方案不复杂,查询也很简单,那么这就可以了。但由于一段时间查询变得更加复杂,代码需要某种抽象级别(对于高级CRUD),就像以前的数据库一样


最后一个解决方案可能具有最好的性能,但开发和支持它需要更多的时间。

以下是我目前用来解决此问题的代码。我对它进行了足够的测试,以使它符合我的用例,但它可能不是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
}