2012-04-18 58 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