最簡單(也是最具擴展性)的解決方案可能是將過濾條件轉換爲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用於數據文件的所有內存)。
驚人的答案! – KennyPowers 2012-04-18 15:14:31
非常感謝!它爲我做了什麼最好的答案:) – KennyPowers 2012-04-18 15:14:59