1

私は一連のアカウント全体でツイートを収集しており、(指定された) 1 か月あたりのツイート数、または指定された月のツイートあたりの平均ツイート長などの計算を行いたいと考えています。

この計算が過去に 1 回行われると (たとえば、2010 年 5 月にユーザー X が 1 日 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 として使用していますが、概念的には問題ないと思います)

4

1 に答える 1

0

はい、DB でのメモ化は一般的なパターンです。MongoDB では、BSON の豊富な機能を利用して埋め込み配列を格納できるため、1 回の DB 要求で簡単かつ効率的にデータを取得できます。

MongoDB に埋め込まれたドキュメント配列には、いくつかのニュアンスがあります。次のテストは、配列の '$elemMatch' クエリ セレクターと '$' Update Array 演算子の使用法を示しています。また、クライアント側とサーバー側の両方の更新の例も含まれています。

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

毎月の更新は頻繁ではありませんが、事前に割り当てて、ドキュメントの増大によるオーバーヘッドを回避できます。事前割り当てには数行の追加コードが必要であり、年のエポックも処理する必要があります。

Ruby 1.9.3、モンゴイド 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

これが役立つことを願っています。

于 2012-12-20T21:41:41.557 に答える