2012-07-03 38 views
7

可以說我有一個網站就像digg.com我有一大堆的文章,人們可以在自己喜歡的文章進行投票。MongoDB的架構設計 - 以投票帖子

我希望能夠查詢得到了一定的時間內(最後一小時,最後一天,上週)通過投票數量排序,得票最多的文章。

像往常一樣用MongoDB的存在實現這一幾種不同的方式,但我不知道哪一個是正確的。

  • 一個職位文檔,其中包含投票的陣列 - 投票本身是包含用戶ID,用戶名和投票日期的引用文件:
{ 
    "_id": "ObjectId(xxxx)", 
    "title": "Post Title", 
    "postdate": "21/02/2012+1345", 
    "summary": "Summary of Article", 

    "Votes": [ 
     { 
      "userid":ObjectId(xxxx), 
      "username": "Joe Smith", 
      "votedate": "03/03/2012+1436" 
     }, 
      ] 
    } 
  • 一個單獨表決集合,包含個人的詳細資料投票和對已投票的帖子的引用:
{ 
    "_id": "ObjectId(xxxx)", 
    "postId": ObjectId(xxxx), 
    "userId": ObjectId(xxxx), 
    "votedate": "03/03/2012+1436" 
} 

第一個是Documentey,但我不知道如何查詢votes數組以獲得過去24小時票數最多的文檔。

我傾向於第二個,因爲它會更容易查詢的投票通過投票,我認爲分組,但我不知道它會如何執行。這就是你如何在關係數據庫中做到這一點,但它看起來並不是很有說服力 - 但我不確定它是否是一個問題,是嗎?

或者我使用兩者的組合?我也會實時進行這種類型的聚合查詢,每次頁面加載。或者我只是每分鐘運行一次查詢,並將結果存儲在查詢結果集合中?

你將如何實現這個模式?

+0

相關的問題:http://stackoverflow.com/questions/ 9296793 /高效的文檔格式的存儲票在mongo - 分貝 –

+0

也相關:http://stackoverflow.com/questions/7046462/best-way-to-model-a-voting-system-in -mongodb – wmassingham

回答

9

常見的方法來跟蹤數票整體將保持後文檔中的票數,推動新價值的票陣列時,原子更新。

由於它是單個更新,因此可以保證計數將與數組中的元素數相匹配。

如果聚合的數量是固定的,並且網站非常繁忙,那麼可以擴展此範例並增加更多的計數器,例如月,日,小時的計數器,但這可能非常快速地失控。因此,您可以使用新的(在2.1.2 dev版本中提供,將在2.2版本中生成)。使用比Map/Reduce更簡單,它可以讓您非常簡單地進行計算,尤其是在您請注意將您的投票日期存儲爲ISODate()類型。

典型的管道用於聚合查詢最高票干將這個月可能會是這個樣子:

today = new Date(); 
thisMonth = new Date(today.getFullYear(),today.getMonth()); 
thisMonthEnd = new Date(today.getFullYear(),today.getMonth()+1); 

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$group: { _id: "$title", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 

這通過匹配投票日期限制了輸入到管道有票的帖子給你指望一個月,「展開」數組以獲得每票的一個文檔,然後做一個「group by」等價物總結每個標題的所有選票(我假設標題是唯一的)。然後按投票數降序排列,並將輸出限制在前十。

您還可以通過天(例如)聚集票,看看哪些日子是最活躍的投票的能力,該月:

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$project: { "day" : { "$dayOfMonth" : "$Votes.votedate" } } }, 
    {$group: { _id: "$day", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 
+0

如果您最終將自己的收藏存儲在自己的收藏中,而不是嵌入帖子中,那麼您不需要「$ unwind」步驟,其餘的聚合基本保持不變。 –

+0

請注意,我不希望兩次匹配所需的月份。首先$匹配消除了在所需月份中沒有任何投票的帖子,但是第二個$匹配(在$ unwind之後)確保我們只保留那個月發生的投票,然後我們將它們計算在內。第一個$匹配是爲了減少我們輸入流水線的文檔總數,除了性能以外,這不是必須的。 –

+0

我正在嘗試使用引用文檔的解決方案,但它不起作用。在我的測試中,我有6個帖子,其中只有一個人有一個投票。所有其他人在'posts_votes'集合中沒有記錄。如果我運行'{$ group:{_id:「$ votes.post_id」,票數:{$ sum:1}}}'我得到一個帶有_id爲null的記錄。如果我將'$ votes.post_id'更改爲'$ title',它將返回所有6個帖子,每個帖子只有一次投票(應該只有一個投票,其他所有人都有0)。還嘗試添加使用'$ votes.post_id'變量變量的'$ project'數組。 – Nathan

0

你所選擇的模式在很大程度上取決於你的用case..If你期待了很多票/意見,並希望他們獨立屬於後的處理它們,你可以讓他們在同一個帖子ID分類收集作爲'foriegn的關鍵'..但是,如果你想加載一個特定的職位時,所有的投票和自己的投票沒有他們的職位沒有任何意義,然後去嵌入(在你的情況,第一)方法。

+0

您可以嘗試使用更多文檔化方法的mapreduce來查詢votes數組,以便在過去24小時內獲得票數最多的文檔...由於mapreduce恰好是繁重的操作,因此最好僅偶爾運行它並使用緩存的結果。 –