简体   繁体   中英

Rails: “Caching” the average of an associated model's values

In Rails, I have some models that look like this:

class Product
  has_many :listings
end

class Listing
  belongs_to :product
  # quantity_in_kg
  # total_price
  # price_per_kg = total_price / quantity_in_kg
end

I'd like to be able to compare the listings for a product based on the price per kilogram, compared to the price per kilogram for the product. For example, this listing is only $2 per kilogram, whereas the product's average is $3.

Eventually, I'd like to be able to run a query that says "give me all of the listings which are below the average price of their product".

What's an effective way of doing this? I was thinking of something custom with ActiveRecord callbacks, and caching the per-kilo average in the products table, and the per-kilo price for each listing in the listings table. There's probably a lot of scope for getting that wrong, so I was wondering if there was another way.

I'm using Postgres 9.6 and Rails 5.1.0.

(Bonus points: listings can also be active/inactive, and I'd only like to compare the average of all active listings).

My suggestion is to start with a simple after_save callback and see where it takes you. You can add some updating criteria like "only recalculate on create/destroy or if active has been updated", add some transactions if you're feeling extra careful, etc..

If gets too slow, add a background worker to update it regularly (for example).

class Listing
  after_save do
    product.update(
      avg_price_per_kg: product.listings.where(active: true).average(:price_per_kg)
    )
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM