2012-04-18 56 views
2

我需要mongodb的自定义查询构建器。我已经完成了可用于查询的文档(字段)列表的用户界面。用户可以选择“结果列”,“条件”,“分组依据”和“排序依据”。 让我在使用SQL语言解释..看到的例子:使用Python构建基于JSON的mongoDB查询

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 

所以

  • SELECT COL1,COL2 - 结果列
  • WHERE COL1 = 1 AND COL2 = “富” OR col3>“2012-01-01 00:00:00”或col3 <“2012-01-02 00:00:00” - 条件
  • GROUP BY col4,col5 - 组声明
  • ORDER BY col1 DESC,col2 ASC - 按统计排序ement

应该由Python根据用户界面提供的JSON数据生成列计数,条件,分组顺序。

我只是好奇,是否有可能做mongoDB与它的MapReduce或不?可能你看到任何模块? 另外,如果你对MongoDB很好,你可以把这个SQL查询翻译成MongoDB查询吗?

回答

4

最简单(也是最具扩展性)的解决方案可能是将过滤条件转换为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” - conditions

这是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)}}, 
]}) 

注意,对日期/时间字段进行比较时的MongoDB不字符串转换为日期,所以我已经明确地做所以这里使用Python datetime模块。该模块中的datetime类假定0作为非指定参数的默认值。

SELECT COL1,COL2 - 结果列

我们可以用field selection只检索我们需要的字段:

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']) 

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)]) 

ORDER BY COL1 DESC,ASC COL2:相反,知道我们要组由这些列,我们可以通过这些字段排序使应用程序代码做这样简单

这应该在应用程序代码来完成应用所需的聚合函数后(假设我们要总结过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 
    }) 

使用聚合框架

如果您正在使用MongoDB 2.1或更高版本(2.1.x是预计即将发布的2.2.0稳定版的开发系列),则可以使用聚合框架完成所有这些工作服务器端。要做到这一点,使用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} 
]) 

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

+0

惊人的答案! – KennyPowers 2012-04-18 15:14:31

+0

非常感谢!它为我做了什么最好的答案:) – KennyPowers 2012-04-18 15:14:59

1

而你的问题。当然,你可以对Mongo进行这些查询,并且mapreduce无关。 如果你想Mongo快速入门,你可以尝试ORM,如mongoengine

+0

感谢您的链接! – KennyPowers 2012-04-18 13:43:02

+0

但是如何在不使用MapReduce的情况下将此查询转换为mongo?它使用组声明。无论如何,这将是很高兴看到这个SQL查询如何可以变成mongo的一个 – KennyPowers 2012-04-18 13:45:04

+0

这是一个很好的来自SQL http://docs.mongodb.org/manual/reference/sql-comparison/和groupby运营商的新手你可以在aggragation框架中找到。 – Denis 2013-03-05 11:01:41