簡體   English   中英

Rails:在3個模型上進行復雜的搜索,僅返回最新的-怎么做?

[英]Rails: complex search on 3 models, return only newest - how to do this?

我正在嘗試向我的應用添加高級搜索選項,用戶可以在其中基於3種不同模型的屬性搜索某些鏈接。

我的應用程序設置為使User has_many :websitesWebsite has_many :linksLink 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 :linksLink belongs_to :websitehas_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.idstats2.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM