2015-09-04 2 views
1

У меня есть несколько n + 1 запросов, которые делают время загрузки для моего сайта очень медленным. Это веб-сайт в социальных сетях, как и Facebook, а источник n + 1 запросов - друзья. Подробная информация -Rails - исключение n + 1 запросов без объединения таблиц

Таблица друзей содержит 3 столбца - User_id (пользователь, который отправил запрос), friend_id (пользователь, который получил запрос), и в ожидании, что является логическим, чтобы указать, была ли принята дружба или нет.

запросы Дружить реализованы в моей модели пользователя с помощью

def friend_requests 
    friend_ids = Friendship.where('friend_id = ? AND pending = true', self.id).all 
end 

Я специально нужна дружба идентификатор, потому что нужно обновить ожидающий булево, если пользователь хочет, чтобы принять или отклонить запрос.

Модель Дружбы имеет ассоциацию belongs_to => friend.

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

Источник n + 1. Для представления, когда я хочу получить запросы друга, полученные пользователем, я также хочу указать имя и профиль пользователя, отправившего запрос. Это выглядит некоторые вещи, как это -

json.friend_requests @user.friend_requests.includes(friend: :name) do |friendship| 
    json.extract! friendship, :id, :user_id 
    json.name User.find(friendship.user_id).name # n + 1 
    json.profile_pic User.find(friendship.user_id).profile_piC# n + 1 
end 

я первоначально были некоторые сомнения по поводу синтаксиса включает в себя (друг:: название), но я попробовал все перестановки, упомянутые в this thread, но это дает мне

Ассоциация с именем 'name' не была найдена на пользователе; возможно, вы с ошибкой это?

Это верно, так как имя является атрибутом Пользователя, а не ассоциацией.

Единственный способ решить эту проблему так, что я могу думать, это мой меняю стол friend_requests к find_by_sql запроса, который выглядит следующим образом -

SELECT f.id, f.user_id, f.friend_id, f.pending, users.name, users.profile_pic 
FROM friendship AS f 
JOIN users ON users.id = friendship.friend_id 
WHERE f.friend_id = ? AND pending = true 

Это даст мне имя и profile_pic из пользователь, но я не хочу этого делать, поскольку он выглядит грязным, но, что более важно, я хочу знать, есть ли лучший и разумный способ сделать это.

+0

Semi связанных http://stackoverflow.com/a/26251892/525478 –

ответ

2

Из документации API на #includes:

Укажите отношения, которые будут включены в набор результатов.

ключевого слова отношения: вы указываете в целом отношения должны быть включены, а не поле.

Вы должны попробовать это:

@user.friend_requests.includes(:friend) do |friendship| 

    # whatever you need before 

    # access the friend's name through the friend relationship 
    # this should not require another query as the friend object was 
    # preloaded into friendship using #includes 
    friendship.friend.name 

    # whatever you need after 

end 

Edit: на самом деле, это вызовет 2 запросов SQL, первое, чтобы получить пользователя friend_requests, затем второй, чтобы получить все friend отношения сразу. Второй запрос существо формы

SELECT "users".* FROM "users" WHERE "users"."id" IN (/*LIST OF IDs*/) 

используя все идентификаторы, найденные в первом friend_requests запросе.

Если вы хотите только один запрос будет уволен, вы должны попробовать JOIN, наверное, так:

@user.friend_requests.joins(:friend).includes(:friend)