【问题标题】:Patterns for caching/storing aggregations in Mongo documents (Ruby, mongoid)?在 Mongo 文档(Ruby、mongoid)中缓存/存储聚合的模式?
【发布时间】:2012-10-14 22:36:15
【问题描述】:

我正在收集一组帐户中的推文,并希望进行计算,例如每(给定)月的推文,或给定月份每条推文的平均推文长度。

一旦此计算在过去一次完成(例如,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,但不要认为这在概念上很重要)

【问题讨论】:

    标签: ruby mongodb design-patterns mongoid


    【解决方案1】:

    是的,在数据​​库中进行记忆是一种常见的模式。使用 MongoDB,您可以使用 BSON 的丰富性来存储嵌入式数组,以便您可以通过一个 DB 请求轻松高效地获取数据。

    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
    

    test/unit/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-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 2016-10-23
      • 2015-05-20
      • 1970-01-01
      相关资源
      最近更新 更多