在Mongoid Document的范围内需要group_by

时间:2012-04-19 01:27:02

标签: ruby-on-rails mongoid

我有一个这样的模型:

class Event
  include Mongoid::Document
  include Mongoid::Timestamps

  scope :range,  lambda {|start, finish| 
    where(:created_at => {'$gte' => start,'$lt' => finish}) if start && finish
  }
end

我需要一个按发生当天分组的事件数量的哈希值:

{"2011-11-07"=>2, "2011-10-25"=>10, "2011-04-03"=>1, "2011-05-13"=>1, "2011-03-23"=>1, "2011-11-08"=>4, "2011-06-12"=>1, "2011-10-26"=>6}

我可以使用这个相当笨重的链条从控制台获得完全相同的内容:

Event.range(4.months.ago, Time.now).to_a.
  group_by{ |e| e.created_at.to_date }.
  map{ |date, events| [date.to_s, events.count]}.
  inject({}) { |r, i| r[i.first] = i.last; r }

我真的想将它放入范围或类方法中,以便我可以写:

Event.range(4.months.ago, Time.now).daily

关于如何做到这一点的任何想法?

更新

仅供参考,我已尝试过以下各种解决方案。

scope :daily,  lambda { to_a.group_by{ |e| e.created_at.to_date }.
                        map{ |date, events| [date.to_s, events.count]}.
                        inject(ActiveSupport::OrderedHash.new) { |r, i| r[i.first] = i.last; r } }

def self.daily
  to_a.group_by{ |e| e.created_at.to_date }.
    map{ |date, events| [date.to_s, events.count]}.
    inject(ActiveSupport::OrderedHash.new) { |r, i| r[i.first] = i.last; r }
end

两者都失败了。回溯:

> Event.range(2.week.ago, Time.now).daily
/my_app/event.rb:37: warning: default `to_a' will be obsolete
NoMethodError: undefined method `created_at' for Event:Class
    from /my_app/event.rb:37:in `daily'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/core_ext/enumerable.rb:26:in `group_by'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/core_ext/enumerable.rb:25:in `each'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/core_ext/enumerable.rb:25:in `group_by'
    from /my_app/event.rb:37:in `daily'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/criteria.rb:366:in `send'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/criteria.rb:366:in `method_missing'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/named_scope.rb:120:in `with_scope'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/criteria.rb:365:in `send'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/criteria.rb:365:in `method_missing'
    from (irb):57
    from :0

1 个答案:

答案 0 :(得分:1)

类方法不起作用,因为Event.range(4.months.ago, Time.now).daily每天需要在标准上定义方法,并且范围不起作用,因为范围需要返回条件。目前最好的选择是每天创建一个类方法,并像Event.daily(start, finish)

一样使用它
class Event
  include Mongoid::Document
  include Mongoid::Timestamps

  scope :range,  lambda {|start, finish| 
    where(:created_at => {'$gte' => start,'$lt' => finish}) if start && finish
  }

  def self.daily(start, finish)
    daily_events = {}
    self.range(start, finish).each do |event|
      date_str = event.created_at.to_date.to_s
      daily_events[date_str] ||= 0
      daily_events[date_str] += 1
    end
    return daily_events
  end
end