Ruby on rails 具有聚合数据的Rails模型(不受表支持)

Ruby on rails 具有聚合数据的Rails模型(不受表支持),ruby-on-rails,activerecord,model,Ruby On Rails,Activerecord,Model,我想在rails中创建一个与数据库中的表不相关的模型。相反,模型应该动态地获取关于其他模型的聚合数据 例如: 我有一个餐厅模型存储在数据库的餐厅表中。我想有一个RestaurantStats模型,在那里我可以运行RestaurantStats.find_total_访客,或RestaurantStats.find_time_花费的时间等。。。并返回一组RestaurantStats模型,每个模型具有: [:餐厅id,:统计值] 显然在每一次发现中。。。方法,stat_值将表示不同的内容(对于fi

我想在rails中创建一个与数据库中的表不相关的模型。相反,模型应该动态地获取关于其他模型的聚合数据

例如:

我有一个餐厅模型存储在数据库的餐厅表中。我想有一个RestaurantStats模型,在那里我可以运行RestaurantStats.find_total_访客,或RestaurantStats.find_time_花费的时间等。。。并返回一组RestaurantStats模型,每个模型具有:

[:餐厅id,:统计值]

显然在每一次发现中。。。方法,stat_值将表示不同的内容(对于find_time_,它将花费秒数,对于find_total_访问者,它将是访问者数)。我们的想法是按花费的时间或游客总数返回前100名餐厅

到目前为止,我正在创建一个模型(不是从ActiveRecord继承的)


问题是如何以rails y的方式定义find_total_visitors、find_time_Consumed函数,以便填充餐厅id、stat_value字段?

使用self.(fieldname)设置值,然后保存这些值(在运行find或build之后)。

您确定不希望这些只是餐厅上的方法吗

class Restaurant < ActiveRecord::Base
  has_many :visitors

  def total_visitors
    visitors.count # Or whatever
  end

  def time_spent
    visitors.average(:visit_time) # Or whatever      
  end
end
class餐厅
鉴于您希望根据统计数据对搜索进行排序,将计数器缓存添加到餐厅模型似乎是您的最佳选择

就总访客而言,这很简单。就花费的总时间而言,它将稍微复杂一些,但仍然不是无法管理的。如果你在寻找一个平均值,那么事情会变得更复杂一些

以下是将柜台缓存添加到餐厅模型所需的代码。请注意,大多数新模型代码都在访问者的模型中

通过迁移将新列添加到
餐厅

class AddCounterCaches < ActiveRecord::Migration
  def self.up
    add_column :restaurants, :visitors_count, :integer, :default => 0
    add_column :restaurants, :total_time_spent, :integer, :default => 0
    Restaurant.reset_column_information
    Restaurant.find(:all).each do |r|
      count = r.visitors.length
      total = r.visitors.inject(0) {|sum, v| sum + v.time_spent}
      average = count == 0 ? 0 : total/count
      r.update_counters r.id, :visitors_count => count
       :total_time_spent => total, :average_time_spent => average
    end
  end

  def self.down
    remove_column :restaurants, :visitors_count        
    remove_column :restaurants, :total_time_spent
  end
end
class AddCounterCaches0
添加列:餐厅,:总花费时间,:整数,:默认值=>0
Restaurant.reset\u列\u信息
餐厅。查找(:全部)。每个都做|
计数=r.visitors.length
total=r.visitors.injection(0){| sum,v | sum+v.time_-speed}
平均数=计数==0?0:总数/计数
r、 更新\u计数器r.id,:访客\u计数=>count
:total_time_speed=>total,:average_time_speed=>average
结束
结束
def自动关闭
删除列:餐厅,:访客\u计数
删除列:餐厅,:花费的总时间
结束
结束
更新访问者模型以更新计数器缓存

class Vistor < ActiveRecord::Base
  belongs_to :restaurant, :counter_cache => true 

  after_save :update_restaurant_time_spent, 
    :if => Proc.new {|v| v.changed.include?("time_spent")}

  def :update_restaurant_time_spent
    difference = changes["time_spent"].last - changes["time_spent"].first
    Restaurant.update_counters(restaurant_id, :total_time_spent => difference)       
    restaurant.reload   
    avg = restaurant.visitors_count == 0 ? 
      0 : restaurant.total_time_spent / restaurant.visitors_count
    restaurant.update_attribute(:average_time_spent, avg)
  end     
end 
class Vistortrue
保存后:更新餐厅花费的时间,
:if=>Proc.new{v|v.changed.include?(“花费的时间”)}
def:更新\u餐厅\u花费的时间\u
差异=更改[“花费的时间”]。上次-更改[“花费的时间”]。第一次
餐厅。更新餐厅柜台(餐厅id:花费的总时间=>差异)
餐厅,重新装货
平均值=餐厅。游客人数=0?
0:餐厅。总时间\花费\餐厅。访客\计数
餐厅。更新属性(:平均花费的餐厅时间,平均)
结束
结束
N.B.代码尚未测试,因此可能包含小错误


现在,您可以按这些列排序,创建包含它们的命名作用域,或者在方法中使用它们。

有三个问题:1)查找统计数据的sql非常复杂(不仅仅是计数/平均值)。我可以使用find_by_sql吗?2) 我想返回按复杂SQL排序的餐厅,这样可以吗?3) 餐厅表中不存在:stat_value列,如何设置这些列?Mike有你90%的答案#find_by_sql将返回一个餐厅实例,ActiveRecord magic将允许从视图中访问sql返回的任何列。餐厅。按sql查找(“选择id,计数(访客。*)餐厅中的访客计数按访客顺序计数”)。@François:这是不真实的。此解决方案不提供访问者计数列,只提供计算统计信息的方法。因此,它们不能通过SQL进行排序。
class Vistor < ActiveRecord::Base
  belongs_to :restaurant, :counter_cache => true 

  after_save :update_restaurant_time_spent, 
    :if => Proc.new {|v| v.changed.include?("time_spent")}

  def :update_restaurant_time_spent
    difference = changes["time_spent"].last - changes["time_spent"].first
    Restaurant.update_counters(restaurant_id, :total_time_spent => difference)       
    restaurant.reload   
    avg = restaurant.visitors_count == 0 ? 
      0 : restaurant.total_time_spent / restaurant.visitors_count
    restaurant.update_attribute(:average_time_spent, avg)
  end     
end