[英]Best practice: Which information should I store in my database?
目前我正在開發一個小書評級應用程序,用戶可以對書籍進行評分和評論。
我當然有書模型:
class Book < ActiveRecord::Base
has_many :ratings
end
和評級模型:
class Rating < ActiveRecord::Base
belongs_to :book
end
評級對象的“總評級值”通過不同的評級類別(例如,可讀性,......)來計算。 此外,一本書的總評分應按所有給定的評級計算。
現在我問自己的問題是:我是否應該計算/查詢每本書的每個人訪問我的頁面的整體評分,還是應該在我的圖書模型中添加一個字段,其中(定期)計算和保存總體評級?
編輯:我將在這種情況下使用的“計算”是一個簡單的平均確定。
示例:一本書有大約200個評級。 每個評級都是10個類別評級的組合。 所以我想確定一個評級的平均值,並在所有200個評級中結束。
如果這些評級的平均值在計算上並不昂貴(即不需要很長時間),那么只需在運行中進行計算即可。 這符合不過早使用的想法(參見http://c2.com/cgi/wiki?PrematureOptimization )。
但是,如果您確實想要優化此計算,那么將其存儲在書籍模型上並更新評級寫入的計算是可行的方法。 這被稱為“緩存”結果。 以下是一些緩存數據庫中平均評級的代碼。 (還有其他緩存方式)。
class Book < ActiveRecord::Base
has_many :ratings, after_add :update_average_rating
def update_average_rating
update_attribute(:average_rating, average_rating)
end
def average_rating
rating_sum / ratings.count
end
def rating_sum
ratings.reduce(0) {|sum, rating|
sum + rating.value # assuming rating model has a value attribute
}
end
end
class Rating < ActiveRecord::Base
belongs_to :book
end
注意:上面的代碼假定數據庫中book表上存在average_rating
列。 請記住通過遷移添加此列。
D B
最有效(盡管不是常規)方法是使用數據庫級別的ALIAS
列,允許您在每次book
計算評級的AVG
或SUM
:
#app/models/book.rb
class Book < ActiveRecord::Base
def reviews_avg category
cat = category ? "AND `category` = \"#{category}\"" : ""
sql = "SELECT AVG(`rating`) FROM `reviews` WHERE `book_id` = #{self.id} #{cat})
results = ActiveRecord::Base.connection.execute(sql)
results.first.first.to_f
end
end
這將允許:
@book = Book.find x
@book.reviews_avg # -> 3.5
@book.reviews_avg "readability" # -> 5
這是最有效的,因為它完全由DB處理:
軌道
您應該使用Rails的average
功能:
#app/models/book.rb
class Book < ActiveRecord::Base
has_many :ratings do
def average category
if category
where(category: category).average(:rating)
else
average(:rating)
end
end
end
end
以上將使您能夠調用@book
的實例 ,並評估其評級的average
或total
:
@book = Book.find x
@book.reviews.average #-> 3.5
@book.reviews.average "readability" #-> 5
-
您還可以在Review
上使用class method
/ scope
:
#app/models.review.rb
class Review < ActiveRecord::Base
scope :avg, (category) -> { where(category: category).average(:rating) }
end
這將允許您致電:
@book = Book.find x
@book.reviews.avg #-> 3.5
@book.reviews.avg "readability" #-> 5
協會擴展
另一種方法(未測試)是在ActiveRecord Association Extension
使用proxy_association.target
對象。
雖然不如DB級查詢那么高效,但它可以讓您在內存中執行活動:
#app/models/book.rb
class Book < ActiveRecord::Base
has_many :reviews do
def avg category
associative_array = proxy_association.target
associative_array = associative_array.select{|key, hash| hash["category"] == category } if category
ratings = associative_array.map { |a| a["rating"] }
ratings.inject(:+) / associative_array.size #-> 35/5 = 7
end
end
end
這將允許您致電:
@book = Book.find x
@book.reviews.avg # -> 3.5
@book.reviews.avg "readability" # -> 5
根本不需要重新計算每次頁面訪問的平均總體評級,因為它只會在某人實際評價該書時發生變化。 所以只需使用字段AVG_RATING或類似的東西,並更新每個給定評級的值。
您是否考慮使用評級的緩存版本。
rating = Rails.cache.fetch("book_#{id}_rating", expires_in: 5.minutes) do
do the actual rating calculation here
end
在大多數情況下,只需查詢數據庫即可獲得平均值:
average = book.reviews.average(:rating)
在大多數情況下,根據請求查詢將不會是一個非常昂貴的問題,而且正如Neil Atkinson所指出的那樣,預先成熟的優化可能會浪費時間和資源。
然而,當計算成本成為問題時,有幾種方法需要考慮,這取決於計算數據的性質。
如果計算出的數據是值得的資源,那么您可以將其保存在數據庫中。 例如,定期生成的報告(每日,每月,每年)以及需要查詢的報告。
否則,如果計算的數據具有較高的“流失率”(每天創建許多評論),您將使用緩存來盡可能避免昂貴的查詢,但將數據填充到數據庫中可能會導致過多的緩慢UPDATE查詢並占用您的網絡或工作流程。
有許多緩存方法相互補充:
etags
利用客戶端緩存 - 如果響應沒有改變,不要重新渲染。 請參閱使用Rails緩存:概述以獲取更多詳細信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.