2012-10-24 63 views
1

我正在收集一系列帳戶的推文,並希望按每個(給定)月份的推文或給定月份中每條推文的平均推文長度進行計算。用於在Mongo文檔(Ruby,mongoid)中緩存/存儲聚合的模式?

一旦這個計算在過去一段時間內完成一次(例如2010年5月用戶X推特4.2次/天),它不是需要重新計算的(大量刪除過去的推文是邊緣案例) ......因此,考慮到這種情況,根據需求執行這種彙總並在Mongo文檔中記錄它的基本模式是什麼。

例如:

t = TwitterAccount.find_by(:screen_name=>'Bob') 
puts t.tweet_rate(:month=>5, :year=>2010) 
# ... 

puts t.tweet_rate(:month=>5, :year=>2010) 
# cached/memoized result is stored inside document 

我可以的方式來寫我自己的類來處理這個猜測,但我想這是NoSQL的世界共同足夠的模式(我剛剛開始使用的MongoDB),其中新的屬性可以隨意添加。

(我使用Ruby 1.9和Mongoid作爲我的ORM,但不認爲概念事項)

回答

0

是的,在DB memoizing是一種常見的模式。使用MongoDB,您可以使用豐富的BSON來存儲嵌入式數組,以便您可以通過一個數據庫請求輕鬆高效地獲取數據。

在MongoDB中有一些嵌入式文檔數組的細微差別。以下測試顯示數組'$ elemMatch'查詢選擇器和'$'更新數組運算符的用法。它也有客戶端和服務器端更新的例子。

http://docs.mongodb.org/manual/reference/operators/

當你每月更新是罕見的,你可以預先分配,以避免越來越多的文檔強制的開銷。預分配需要一些額外的代碼行,並且您還必須處理年份紀元。

的Ruby 1.9.3,Mongoid 3.0.15,助力車1.3.1

class TwitterAccount 
    include Mongoid::Document 
    field :screen_name, type: String 
    embeds_many :tweet_rates 
end 

class TweetRate 
    include Mongoid::Document 
    field :year, type: Integer 
    field :month, type: Integer 
    field :rate, type: Float 
    embedded_in :twitter_account 
end 

測試/單位/ tweet_rate_test.rb

require 'test_helper' 

class TweetRateTest < ActiveSupport::TestCase 

    def setup 
    TwitterAccount.delete_all 
    TwitterAccount.create(:screen_name => 'Bob') 
    end 

    test "monthly append" do 
    t = TwitterAccount.find_by(:screen_name => 'Bob') 
    assert_equal('Bob', t.screen_name) 
    t.tweet_rates.create(:year => 2010, :month =>5, :rate => 12.3) 
    t.save! 
    tr = TwitterAccount.find_by(:screen_name => 'Bob').tweet_rates 
    assert_equal(1, tr.size) 
    end 

    test "prealloc for a year" do 
    t = TwitterAccount.find_by(:screen_name => 'Bob') 
    assert_equal('Bob', t.screen_name) 

    # prealloc for a whole year 
    year = 2012 
    (1..12).each do |month| 
     t.tweet_rates.create(:year => year, :month => month, :rate => -1.0) 
    end 
    t.save! 
    t = TwitterAccount.find_by(:screen_name => 'Bob') 
    assert_equal(12, t.tweet_rates.size) 

    # update a rate using client-side Ruby 
    month, rate = 10, 12.3 
    t.tweet_rates.detect{|tr| tr.year == year && tr.month == month}.rate = rate 
    t.save! 
    assert_equal(rate, TwitterAccount.find_by(:screen_name => 'Bob') 
            .tweet_rates.detect{|tr| tr.year == year && tr.month == month}.rate) # used #detect NOT overloaded #find 

    # update a rate using MongoDB server-side 
    month, rate = 11, 4.56 
    TwitterAccount.where({ :screen_name => 'Bob', 
          :tweet_rates => { '$elemMatch' => { :year => year, :month => 11 } } }) 
        .update('$set' => { 'tweet_rates.$.rate' => rate }) 
    assert_equal(rate, TwitterAccount.where({ :screen_name => 'Bob'}).first 
            .tweet_rates.detect{|tr| tr.year == year && tr.month == month}.rate) # use #detect and NOT overloaded #find 
    end 
end 

希望這有助於。