2015-09-04 42 views
0

我正在做一個社交媒體網站,類似於Facebook和數量的結果,N + 1個查詢放緩我失望在以下情況 -導軌 - 消除N + 1個查詢存在的訪存結果

我的朋友表的模式 - 它有3列,user_id(發送請求的用戶),friend_id(接收請求的人)和pending(表示請求是否被接受的布爾值)如果用戶拒絕請求,那麼條目從表中刪除

用戶有一個朋友的方法,看起來像這樣 -

def friends 
    user_ids = Friend.where(
    '(user_id = :id OR friend_id = :id) AND pending = false', 
    id: self.id 
).pluck(:user_id, :friend_id) 
    .flatten 
    .uniq 
    .reject { |id| id == self.id } 

    User.where(id: user_ids) 
end 

我意識到這個方法並不好,因爲它執行2個SQL查詢。任何建議,以改善這是值得歡迎的,但我希望在這個問題上解決另一個問題。

對於我的看法,我希望得到一個用戶的所有朋友,也是朋友的數量,每個朋友都有。這將使我能夠實現列出朋友的朋友列表,以及朋友的朋友數量。

要這樣做是醜陋的方式 -

json.friends @user.friends do |friend| 
    json.extract! friend, :id, :name, :profile_pic 
    json.number_friends friend.friends.length 
end 

但很明顯,這不是有效的。

是否存在被我可以獲取用戶的朋友的一種方式,和好友的號碼是每個朋友都在一個單一的查詢?

或緩存每個用戶的朋友數量我唯一的解決方案?

編輯 - 一個朋友模型有幾個belongs_to的關聯,即belongs_to的=>用戶和belongs_to的=>朋友

belongs_to :user, 
    class_name: "User", 
    foreign_key: :user_id, 
    primary_key: :id 

    belongs_to :friend, 
    class_name: "User", 
    foreign_key: :friend_id, 
    primary_key: :id 

用戶模型得到他的朋友們使用上面的帖子寫的方法

+0

您有2模特'User'和'Friend'?如果是的話,他們之間的聯繫是什麼。 –

+0

SQL解決方案能爲你工作嗎? –

+0

@JuanCarlosOropeza我更喜歡ActiveRecord,但是如果SQL是唯一的解決方案,那麼肯定,我會採取 – satnam

回答

1

這就是我要做的。

SQL Fiddle Demo

這應該是在一個PostgreSQL函數接收參數的USER_ID。但是因爲在普通的sql中你不能使用變量,所以我在第一個CTE中使用了一個小問題來定義ID參數。

二CTE檢查所有的行,其中USER_ID是relatioship的一部分。並使用CASE來向朋友展示。

最後一個返回的USER_ID

WITH p as (
    SELECT 2 as ID 
), allFriend AS ( 
    SELECT CASE 
       WHEN f.user_id = p.id THEN f.friend_id 
       ELSE f.user_id 
      END myFriend 
    FROM friends f, p 
    WHERE f.user_id = p.ID or f.friend_id = p.ID 
    and f.pending = 0 
) 
SELECT myFriend, Count(*) 
FROM allfriend f 
inner join friends f1 
    on f.myFriend = f1.user_id 
    OR f.myFriend = f1.friend_id 
    WHERE f1.pending = 0 
GROUP BY myFriend 
1

另一種方式,每個朋友的朋友計數來構建這種友誼,而且有相當多的真,是在優化的代價更改讀取優化它通過在友誼表中使用兩行來表示每個友誼對。當添加友誼時,基本上(:user_id,:friend_id,:pending),您還添加(:friend_id,:user_id,:pending) - 同一對user_id,但是相反。

關於毀壞一個你毀壞另一個,並接受一個你接受另一個。這通過回調很簡單。

這樣的友誼總是被表示爲一個簡單的關聯關係:

class User 
    has_many :friendships 
    has_many :friends , :through => :friendships 
end 

class Friendship 
    belongs_to :user 
    belongs_to :friend 
end 

你的公會是渴望可裝載,並表示爲:

@user.friends 

所以你移動的複雜性和低效率成爲了優化非常頻繁的操作,較少 - 很少執行的操作。