2

我有三個型號,以下關聯:多態ActiveRecord的查詢問題

User has_many :owns, has_many :owned_books, :through => :owns, source: :book 
Book has_many :owns 
Own belongs_to :user, :counter_cache => true, belongs_to :book 

我也有跟蹤頂級用戶通過下面的查詢擁有一個頁面:

User.all.order('owns_count desc').limit(25) 

我會現在想添加一個新的頁面,可以像上面那樣追蹤頂級用戶,但是有一個條件:

Book.where(:publisher => "Publisher #1") 

最有效的方法是什麼?

+1

這看起來更像是'has_and_belongs_to_many:books'寫在'has_many:books,through:...'方式。也就是說,如果引入where子句(存儲值計算所有書籍,而不是與該發佈者匹配的書籍),則不能使用'counter_cache'。我認爲你應該改變你的問題,專注於通過計數匹配條件的對象進行排序。 – Leito

+0

我對counter_cache的想法很多,實際上我確定,我認爲下面的解決方案實際上是可行的。 – user3141095

回答

1

我很有趣,如果有這種情況下特別的東西,但我的鏡頭將是以下。

首先,我沒有看到這裏可以應用多態關聯。你只有一本書可以屬於的對象(用戶)。據我所知,多態是用於連接書籍的幾個dif。對象(例如,用戶,庫,架等)(編輯 - 問題的初始文字提到多態關聯,現在它不)

第二,我不相信有一種方法來緩存計數器在這裏,只要「發佈者#1」是變化的輸入參數,而不是一組預定義和已知的發佈者(少數常量)。

第三,我假設單個發佈者的書籍數量相對有限。因此,即使您的桌子上有數百萬本書,每個發佈商的書籍數量也應該達到數百個。

然後,您可以先查詢所有發佈商的書籍ID,例如,

book_ids = Book.where(:publisher => "Publisher #1").pluck(:id) 

,然後查詢中擁有頂級用戶的ID表:

Owns.select("user_id, book_id, count(book_id) as total_owns").where(book_id: book_ids).group(:user_id).order(total_owns: :desc).limit(25) 

免責聲明 - 我沒有嘗試聲明中軌控制檯,因爲我沒有定義你的對象。我基於ActiveRecord中的羣組電話docs

編輯。爲了使事情更有效率,您可以嘗試以下操作:

0)爲防萬一,請確保您在兩個外鍵的Owns表上都有索引。

1)使用pluck進行第二個查詢,也不要創建Own對象,儘管由於限制(25)不應該有很大的差異。像這樣:

users_ids = Owns.where(book_id: book_ids).group(:user_id).order("count(*) DESC").limit(25).pluck("user_id") 

請參閱this question作爲參考。

2)裝載在一個後續查詢中的所有結果的用戶,而不是N次查詢爲每個用戶

top_users = User.where(:id => users_ids) 

3)嘗試在第一階接合用戶表:

owns_res = Owns.includes(:user).select("user_id, book_id, count(book_id) as total_owns").where(book_id: book_ids).group(:user_id).order("total_owns DESC").limit(25) 

然後用owns_res 。第一。用戶

+0

謝謝,這實際上工作,雖然我不得不改變順序(total_owns::desc)(「total_owns DESC」),使其工作。這會導致頁面上的查詢變慢,因爲我現在必須這樣做: <%@ users.each do | u | %> <%= User.find(u.user_id).name%> <% end %> 要獲取用戶名,但我會繼續玩一下。 – user3141095

+0

查看上面編輯部分的一些增加內容,以提高效率 –