使用Python从用户构建基于JSON的mongoDB查询

Ken*_*ers 3 python mongodb

我需要一个用于mongodb的自定义查询生成器。我已经完成了可用于查询的文档(字段)列表的用户界面。用户可以选择“结果列”,“条件”,“分组依据”和“排序依据”。让我解释一下使用SQL语言的情况。.参见示例:

SELECT col1, col2 FROM table WHERE col1=1 AND col2="foo" OR col3 > "2012-01-01 00:00:00" OR col3 < "2012-01-02 00:00:00" AND col5 IN (100, 101, 102) GROUP BY col4, col5 ORDER BY col1 DESC, col2 ASC
Run Code Online (Sandbox Code Playgroud)

所以

  • SELECT col1,col2-结果列
  • WHERE col1 = 1 AND col2 =“ foo” OR col3>“ 2012-01-01 00:00:00” OR col3 <“ 2012-01-02 00:00:00”-条件
  • GROUP BY col4,col5-按语句分组
  • ORDER BY col1 DESC,col2 ASC-按语句排序

列数,条件,分组依据和排序依据应由Python根据与用户界面一起使用的JSON数据生成。

我很好奇,是否有可能用它的MapReduce为mongoDB做?可能您看到了与此相关的任何模块?另外,如果您对MongoDB感到满意,能否将此SQL查询转换为MongoDB查询?

dcr*_*sta 5

最简单(且可扩展性最高)的解决方案可能是将过滤条件转换为MongoDB查询,并在客户端进行聚合。

以上面的示例为例,我们将其分解并构造一个MongoDB查询(我将使用 PyMongo进行,但如果愿意,可以使用Mongoengine或其他ODM进行相同的操作):

WHERE col1 = 1 AND col2 =“ foo” OR col3>“ 2012-01-01 00:00:00” OR col3 <“ 2012-01-02 00:00:00”-条件

这是PyMongo find()方法的第一个参数。我们必须使用$or运算符显式构建逻辑AND / OR树:

from bson.tz_util import utc
cursor = db.collection.find({'$or': [
    {'col1': 1, 'col2': 'foo'},
    {'col3': {'$gt': datetime(2012, 01, 01, tzinfo=utc)}},
    {'col3': {'$lt': datetime(2012, 01, 02, tzinfo=utc)}},
]})
Run Code Online (Sandbox Code Playgroud)

请注意,与日期/时间字段进行比较时,MongoDB不会将字符串转换为日期,因此我在这里使用Python datetime模块明确地做到了。的datetime模块中类假定0为未指定参数的默认值。

SELECT col1,col2-结果列

我们可以使用字段选择来仅检索所需的字段:

from bson.tz_util import utc
cursor = db.collection.find({'$or': [
    {'col1': 1, 'col2': 'foo'},
    {'col3': {'$gt': datetime(2012, 01, 01, tzinfo=utc)}},
    {'col3': {'$lt': datetime(2012, 01, 02, tzinfo=utc)}},
]}, fields=['col1', 'col2'])
Run Code Online (Sandbox Code Playgroud)

GROUP BY col4,col5-按语句分组

使用标准的MongoDB查询无法有效地做到这一点(尽管我稍后将展示如何使用新的Aggregation Framework在服务器端完成所有操作)。相反,知道要对这些列进行分组,我们可以通过按以下字段进行排序来简化应用程序代码:

from bson.tz_util import utc
from pymongo import ASCENDING
cursor = db.collection.find({'$or': [
    {'col1': 1, 'col2': 'foo'},
    {'col3': {'$gt': datetime(2012, 01, 01, tzinfo=utc)}},
    {'col3': {'$lt': datetime(2012, 01, 02, tzinfo=utc)}},
]}, fields=['col1', 'col2', 'col4', 'col5'])
cursor.sort([('col4', ASCENDING), ('col5', ASCENDING)])
Run Code Online (Sandbox Code Playgroud)

ORDER BY col1 DESC,col2 ASC-按语句排序

在应用所需的聚合函数后,应该在应用程序代码中完成此操作(假设我们要对col4求和,并取col5的最大值):

from bson.tz_util import utc
from pymongo import ASCENDING
cursor = db.collection.find({'$or': [
    {'col1': 1, 'col2': 'foo'},
    {'col3': {'$gt': datetime(2012, 01, 01, tzinfo=utc)}},
    {'col3': {'$lt': datetime(2012, 01, 02, tzinfo=utc)}},
]}, fields=['col1', 'col2', 'col4', 'col5'])
cursor.sort([('col4', ASCENDING), ('col5', ASCENDING)])

# groupby REQUIRES that the iterable be sorted to work 
# correctly; we've asked Mongo to do this, so we don't
# need to do so explicitly here.
from itertools import groupby
groups = groupby(cursor, keyfunc=lambda doc: (doc['col1'], doc['col2'])
out = []
for (col1, col2), docs in groups:
    col4sum = 0
    col5max = float('-inf')
    for doc in docs:
        col4sum += doc['col4']
        col5max = max(col5max, doc['col5'])
    out.append({
        'col1': col1,
        'col2': col2,
        'col4sum': col4sum,
        'col5max': col5max
    })
Run Code Online (Sandbox Code Playgroud)

使用聚合框架

如果您使用的是MongoDB 2.1或更高版本(2.1.x是导致即将推出2.2.0稳定版本的开发系列),则可以使用Aggregation Framework在服务器端执行所有这些操作。为此,请使用以下aggregate命令:

from bson.son import SON
from pymongo import ASCENDING, DESCENDING
group_key = SON([('col4', '$col4'), ('col5': '$col5')])
sort_key = SON([('$col1', DESCENDING), ('$col2', ASCENDING)])
db.command('aggregate', 'collection_name', pipeline=[
    # this is like the WHERE clause
    {'$match': {'$or': [
        {'col1': 1, 'col2': 'foo'},
        {'col3': {'$gt': datetime(2012, 01, 01, tzinfo=utc)}},
        {'col3': {'$lt': datetime(2012, 01, 02, tzinfo=utc)}},
        ]}},
    # SELECT sum(col4), max(col5) ... GROUP BY col4, col5
    {'$group': {
        '_id': group_key,
        'col4sum': {'$sum': '$col4'},
        'col5max': {'$max': '$col5'}}},
    # ORDER BY col1 DESC, col2 ASC
    {'$sort': sort_key}
])
Run Code Online (Sandbox Code Playgroud)

aggregate命令返回一个BSON文档(即Python字典),该文档受MongoDB的通常限制:如果要返回的文档大小大于16MB,它将失败。此外,对于内存中排序($sort此聚合结束时要求),如果排序需要服务器上物理RAM的10%以上,则聚合框架将失败(这是为了防止收回昂贵的聚合) Mongo用于数据文件的所有内存)。