Rails model with aggregation data (no table support)

Id like to create a model in rails that does not correlate with a table in the database. Instead, the model should dynamically retrieve aggregate data from other models.

Example:

I have a restaurant model stored in a restaurant table in the database. I like to have a RestaurantStats model where I can run RestaurantStats.find_total_visitors, or RestaurantStats.find_time_spent, etc ... on it, and it returns a set of RestaurantStats models, each of which:

[: restaurant_id ,: stat_value]

Obviously, in each method find ... stat_value will mean something else (for find_time_spent it will take a few seconds, for find_total_visitors there will be the number of visitors). The idea would be to return the top 100 restaurants by time or total number of visitors.

While I'm creating a model (not inherited from ActiveRecord)

class RestaurantStats 
   attr_reader :restaurant_id
   attr_reader :stat_value

   def self.find_total_visitors ... 
   def self.find_time_spent ...
end

The question is how to define the functions find_total_visitors, find_time_spent in rails y so that they fill the fields restaurant_id, stat_value?

+3
source share
3 answers

Are you sure you do not want these methods in the restaurant?

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
+2
source

Set values ​​using self. (field name), and then save them (after starting a search or assembly).

+1

, , .

. total time_spent , . , .

, . , .

:

class AddCounterCaches < ActiveRecord::Miration
  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 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 

N.B. , .

, , , .

+1
source

Source: https://habr.com/ru/post/1724131/


All Articles