2013-08-18 70 views
2

最近我一直在玩MongoDB的聚合框架,並認爲這將是一個很好的解決方案,我一直試圖圍繞我的頭。使用MongoDB聚合框架進行不同的排序和分組

所以,說我寫的討論板軟件和我有帖子下面的文檔結構:

{ 
    '_id': ObjectId, 
    'created_at': datetime, 
    'poster_id': ObjectId, 
    'discussion_id': ObjectId, 
    'body': string 
} 

而且我有以下的(簡化)樣本存儲在posts集合中的文檔:

{ 
    '_id': 1, 
    'created_at': '2013-08-18 12:00:00', 
    'poster_id': 1, 
    'discussion_id': 1, 
    'body': 'imma potato' 
} 

{ 
    '_id': 2, 
    'created_at': '2013-08-18 13:00:00', 
    'poster_id': 1, 
    'discussion_id': 1, 
    'body': 'im still a potato' 
} 

{ 
    '_id': 3, 
    'created_at': '2013-08-18 14:00:00', 
    'poster_id': 2, 
    'discussion_id': 1, 
    'body': 'you are definitely a potato' 
} 

{ 
    '_id': 4, 
    'created_at': '2013-08-18 15:00:00', 
    'poster_id': 3, 
    'discussion_id': 1, 
    'body': 'Wait... he is potato?' 
} 

{ 
    '_id': 5, 
    'created_at': '2013-08-18 16:00:00', 
    'poster_id': 2, 
    'discussion_id': 1, 
    'body': 'Yes! He is potato.' 
} 

{ 
    '_id': 6, 
    'created_at': '2013-08-18 16:01:00', 
    'poster_id': 3, 
    'discussion_id': 1, 
    'body': 'IF HE IS POTATO... THEN WHO WAS PHONE!?' 
} 

我想要做的是返回一個明確的地圖poster_id s與他們的最新帖子_id按最新文章降序排列。因此,在最後,考慮到上面的示例代碼,映射看起來非常相似:

{ 
    3:6, 
    2:5, 
    1:2 
} 

這裏是我使用Python中pymongo的執行MongoDB的聚合框架的寫了一個方法的一個例子:

def get_posters_with_latest_post_by_discussion_ids(self, discussion_ids, start=None, end=None, skip=None, limit=None, order=-1): 
    '''Returns a mapping of poster ids to their latest post associated with 
    the given list of discussion_ids. A date range, ordering and paging properties 
    can be applied. 
    ''' 
    pipelines = [] 

    if order: 
     pipelines.append({ '$sort': { 'created_at': order } }) 

    if skip: 
     pipelines.append({ '$skip': skip }) 

    if limit: 
     pipelines.append({ '$limit': limit }) 

    match = { 
     'discussion_id': { 
      '$in': discussion_ids 
     } 
    } 

    if start and end: 
     match['created_at'] = { 
      '$gte': start, 
      '$lt': end 
     } 

    pipelines.append({ '$match': match }) 
    pipelines.append({ '$project': { 'poster_id': '$poster_id' } }) 
    pipelines.append({ '$group': { '_id': '$poster_id', 'post_id': { '$first': '$_id' } } }) 

    results = self.db.posts.aggregate(pipelines) 

    poster_to_post_map = {} 
    for result in results['result']: 
     poster_to_post_map[result['_id']] = result['post_id'] 

    return poster_to_post_map 

現在我已經有了映射,我可以分別查詢postersposts集合以查看完整文檔,然後將它們組合在一起顯示。

現在,問題不是它不起作用,而是......種。假設我的帖子數量要多得多,我想通過他們的最新帖子翻閱海報清單。如果我的頁面限制是「每頁10張海報」,並且在最終的10個文檔中存在一個包含2個或更多帖子的單個海報,我實際上在我的地圖中返回的項目少於10個。

例如,我有10個帖子,1個帖子在初始結果中有3個帖子。然後,彙總框架將放棄其他2個帖子,並將最新消息與該用戶相關聯,從而生成包含8個條目的地圖,而不是10個。

這非常令人沮喪,因爲我無法可靠地對結果進行分頁。我也無法準確確定我是否在結果的最後一頁,因爲一組結果可能會或可能不會返回0個或更多匹配。

什麼,如果有的話,我在這裏做錯了嗎?

我試圖做到的是足夠簡單和聚合框架似乎是一個非常適合我的問題。

這將是足夠簡單,如果它是一個傳統的關係型數據庫的存儲過程,但是這就是我們,當我們移動到無模式文檔存儲犧牲;關係在數據庫的上下文之外進行管理。

無論如何,代碼應該很容易遵循,我會回答你可能有的任何問題。

無論哪種方式,感謝您抽出時間來閱讀。:)

編輯:解決

這裏是未來的觀衆解決方案的要點是:https://gist.github.com/wilhelm-murdoch/6260469

回答

2

它實際上如果你想想聚合框架是如何描述一個很容易修復。

docs摘自:

概念,從集合文件通過聚集 管道,因爲他們通過其將這些對象。 熟悉類UNIX shell(例如bash)的用戶,其概念是 ,類似於用於將文本過濾器串在一起的管道(即|)。

您可能在之前已經閱讀過,但是再次解釋的原因是您可以按任意順序將操作傳遞到該管道中 - 而且不止一次。例如,在MYSQL中,LIMIT總是列在查詢的最後,並在所有其他分組函數之後應用於結果集。

在MongoDB中,操作按照您添加到管道中的順序運行。所以操作順序很重要。

看上面你的代碼,看起來你實際上正在獲取所有東西 - 並且取決於你的IF語句,首先對它進行排序,應用你的偏移和限制,然後在投影和分組之前匹配該結果集。

所以 - 長話短說 - 它只是看起來像你需要重新排序的東西:

pipelines = [] 

match = { 
    'discussion_id': { 
     '$in': discussion_ids 
    } 
} 

if start and end: 
    match['created_at'] = { 
     '$gte': start, 
     '$lt': end 
    } 

pipelines.append({ '$match': match }) 

if order: 
    pipelines.append({ '$sort': { 'created_at': order } }) 

pipelines.append({ '$project': { 'poster_id': '$poster_id' } }) 
pipelines.append({ '$group': { '_id': '$poster_id', 'post_id': { '$first': '$_id' } } }) 

if skip: 
    pipelines.append({ '$skip': skip }) 

if limit: 
    pipelines.append({ '$limit': limit }) 

results = self.db.posts.aggregate(pipelines) 
+1

我的F - 國王發誓基督,但總有一些平凡的小細節我似乎忽視。主席先生,恐怕我只有一票可供。謝謝! **致敬 –

+0

另外,對於未來的其他人,這裏有一個要點我一起來說明@ kmfk的解決方案:https://gist.github.com/wilhelm-murdoch/6260469 –

+1

@WilhelmMurdoch - 很高興爲您提供幫助。 :] – kmfk