[英]Rails: complex search on 3 models, return only newest - how to do this?
我正在嘗試向我的應用添加高級搜索選項,用戶可以在其中基於3種不同模型的屬性搜索某些鏈接。
我的應用程序設置為使User has_many :websites
, Website has_many :links
和Link has_many :stats
我知道如何在Rails中使用連接或包含等創建SQL查詢, 但是我陷入了困境,因為我只想獲取每個鏈接的最新統計信息,而不是所有鏈接的最新統計信息-我不知道執行此操作的最有效方法。
舉例來說,假設一個用戶有2個網站,每個網站有10個鏈接,每個鏈接有100個統計信息,總計2,022個對象,但我只想搜索42個對象(每個鏈接僅1個統計信息)。
一旦在數據庫查詢中僅獲得了這42個對象,就可以添加.where("attribute like ?", user_input)
並返回正確的鏈接。
我嘗試將以下內容添加到我的Link模型中:
has_many :stats, dependent: :destroy
has_many :one_stat, class_name: "Stat", order: "id ASC", limit: 1
但這似乎不起作用,例如如果我這樣做:
@links = Link.includes(:one_stat).all
@links.each do |l|
puts l.one_stat.size
end
我沒有得到1,1,1 1, 1, 1...
的所有統計數字: 125, 40, 76...
我可以使用limit選項來獲得所需的結果嗎?
我已經根據Erez的建議更新了代碼,但仍然無法正常工作:
has_one :latest_stat, class_name: "Stat", order: "id ASC"
@links = Link.includes(:latest_stat)
@links.each do |l|
puts l.latest_stat.indexed
end
=> true
=> true
=> true
=> false
=> true
=> true
=> true
Link.includes(:latest_stat).where("stats.indexed = ?", false).count
=> 6
Link.includes(:latest_stat).where("stats.indexed = ?", true).count
=> 7
它應該返回1和6,但它仍在檢查所有統計信息,而不僅僅是最新的統計信息。
有時,您需要突破AR抽象並開始使用SQL。 只是一點點。
假設您具有非常簡單的關系: Website
has_many :links
, Link
belongs_to :website
和has_many :stats
,以及Stat
belongs_to :link
。 任何地方都沒有非規范化。 現在,您要構建一個查詢,以查找其所有鏈接以及每個鏈接的最新統計信息,但僅針對具有某些屬性的統計信息(或者可以是具有某些屬性的網站或具有某些屬性的鏈接)。
未經測試,但類似:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where("stats.id = (select max(stats2.id)
from stats stats2 where stats2.link_id = links.id)")
最后一位子選擇每個鏈接的統計信息,並找到最大ID。 然后,它會過濾掉與該最大ID不匹配的統計信息(從頂部的聯接中)。 該查詢返回網站,每個網站都有一些鏈接,並且每個鏈接在其stats
集合中僅包含一個stat。
一些額外的信息
我最初以窗口函數的形式編寫了這個答案,結果被認為是過大了,但是我認為無論如何我都應該在這里進行介紹,因為很好玩。 您會注意到,我們上面使用的聚合函數技巧僅能起作用,因為我們正在根據ID確定要使用哪個統計信息,正是我們需要從聯接中過濾統計信息的屬性。 但是,假設您只希望按ID以外的其他條件(例如number_of_clicks
對第一個統計信息進行排名。 該技巧將不再起作用,因為聚合會丟失對ID的跟蹤。 那就是窗口功能進來的地方。
再次,完全未經測試:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where(
"(stats.id, 1) in (
select id, row_number()
over (partition by stats2.id order by stats2.number_of_clicks DESC)
from stat stats2 where stats2.link_id = links.id
)"
)
最后where
選擇與每個鏈接匹配的統計信息並按number_of_clicks
升序對其進行排序的部分,然后in
部分將其與聯接中的統計信息進行匹配。 請注意,窗口查詢不能移植到其他數據庫平台。 您還可以使用此技術解決您提出的原始問題(只需將stats2.id
為stats2.number_of_clicks
); 可以想象,它的性能可能更好,並且在本博客文章中得到了提倡。
我會嘗試這樣的:
has_one :latest_stat, class_name: "Stat", order: "id ASC"
@links = Link.includes(:latest_stat)
@links.each do |l|
puts l.latest_stat
end
請注意,由於它是stat對象本身,而不是關系,因此您無法打印latest_stat.size
。
這是您要找的東西嗎?
@user.websites.map { |site| site.links.map { |link| link.stats.last } }.flatten
對於給定的用戶,這將返回一個數組,其中包含該用戶網站上鏈接的最新統計信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.