2014-02-27 86 views
1

我有三種模式。用戶,網頁和網絡。在軌道中交叉列表的最快方法是什麼?

  • 用戶have_many網絡
  • 網絡belong_to用戶和頁面
  • 頁面have_many網絡。

我在寫一個Page#visible_to?(user)方法,並想知道是否比我現在做的更快/更高效/更好的方法。如果任何用戶的網絡也鏈接到頁面,該方法應該返回true。

當前實現:

def visible_to?(user) 
    return true if networks.empty 
    user_network_ids = user.networks.pluck(:id) 
    !networks.detect { |network| user_network_ids.include?(network.id) }.nil? 
end 

其他選項將包括利用&操作者確定的交點,或者將這一方法上的用戶。思考?

+1

SQL上的數據是?然後使用SQL來交叉,而不是首先將所有東西加載到ruby中。 – Reactormonk

+0

網頁在哪裏發揮作用? 'networks'是否包含與頁面相關的網絡? –

+0

'visible_to?'方法在Page上,所以是的。 – kddeisz

回答

1

你應該考慮使用Enumerable#any?

可以簡化

!networks.detect { |network| user_network_ids.include?(network.id) }.nil? 

newtworks.any? { |network| user_network_ids.include? network.id } 

雖然在這種情況下,更漂亮(我深奧的意見)只使用設置十字路口的所有重物:

def visible_to?(user) 
    networks.empty? || (networks.map(&:id) & user.networks.pluck(:id)).any? 
end 

如果有重疊或者網絡爲空,這將返回true。

+0

檢測速度會不會加快,因爲它會在發現第一個時發現?而不是做完整的相交? – kddeisz

+1

如果您有N個網絡和U用戶網絡ID,您的檢測解決方案是O(NU)最差的情況,並且交集的情況是O(N + U)最差的情況。 –

+0

非常好。很好的論據。 – kddeisz

2
def visible_to?(user) 
    return true if networks.empty? 
    (user.networks & networks).size > 0 
end 
+0

顯然它應該在'networks'爲空時產生'true',在這種情況下,你的答案應該包含一個'networks.empty?或......開始。 –

+0

@DaniëlKnippers:是的,修改了我的回答 – usha

+0

檢測速度會不會加快,因爲它會在發現第一個時發現?而不是做完整的相交? – kddeisz

0

只需使用數據庫,你可以這樣做:

Page.joins(networks: :user).where(pages: {id: page_id}, users: {id: user_id}).any? 

它生成此查詢:

SELECT COUNT(*) FROM "pages" 
    INNER JOIN "networks" ON "networks"."page_id" = "pages"."id" 
    INNER JOIN "users" ON "users"."id" = "networks"."user_id" 
WHERE "pages"."id" = <<page_id>> AND "users"."id" = <<user_id>> 

什麼是最快真的取決於你的具體情況和您的數據的分佈。例如,如果用戶的網絡和頁面的網絡已經被選中,因爲您在其他地方也需要它們,並且沒有太多要比較的元素,那麼執行(user.networks & page.networks).any?可能會更快。試試一下,找出最適合你的東西。

相關問題