Включая оптимизацию ассоциаций в Rails

Мне нужна помощь с оптимизацией Ruby в отношении загрузки ассоциаций по запросу.

Это упрощенный пример. У меня 3 модели: Post, Comment, User. Ссылки: Post содержит много комментариев, а Comment ссылается на User (:author). Теперь, когда я перехожу на страницу сообщения, я ожидаю увидеть тело сообщения + все комментарии (и имена их соответствующих авторов). Для этого требуются следующие 2 запроса:

select * from Post -- to get post data (1 row)
select * from Comment inner join User -- to get comment + usernames (N rows)

В коде у меня есть:

Post.find(params[:id], :include => { :comments => [:author] }

Но это не работает так, как ожидалось: как я вижу в бэкенде, по-прежнему есть N+1 совпадений (хотя некоторые из них кэшируются). Как я могу это оптимизировать?

UPD. После некоторого расследования оказалось, что код был правильным, но он не работает должным образом, если я указал в модели Comment имя contains_to. Как только я сменил :author на :user, все заработало, как и ожидалось.


person Vitaly    schedule 14.04.2010    source источник


Ответы (2)


В моем проекте у меня аналогичные отношения с вашими моделями Post, Comment и User. Я вижу только три реальных запроса sql.

Post.find(1, :include => { :comments => [:author] })

Из журнала отладки он показывает эти три запроса

SELECT * FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `comments`.* FROM `comments` WHERE (`comments`.`post_id` = 1)
SELECT * FROM `authors` WHERE (`authors`.`id` IN (4,8,15,16,23,42)) 
person Corey    schedule 14.04.2010
comment
Я обновился, он меняет способ подтягивания пользователей (авторов) в зависимости от того, что у меня есть в own_to. - person Vitaly; 14.04.2010
comment
Вы исследовали, как можно упорядочить комментарии по описанию даты создания? - person Vitaly; 15.04.2010
comment
так вы имеете в виду это?: Post.find(1, :include =› {:comments =› :author}, :order =› 'comments.created_at desc') - person Corey; 15.04.2010
comment
Не совсем так: он переводит этот запрос как внутреннее соединение вместо дополнительного запроса и не включает автора в комментарии. - person Vitaly; 16.04.2010

Если вас устраивает 2/3 запросов, вы можете попробовать:

@post = Post.find params[:id]
@comments = Comments.find_by_post_id(params[:id], :include => [:author])

or

@comments = @post.comments(:include => [:author])

Изменить: вы пробовали:

Post.find(params[:id], :include => { :comments => :author }

person Vlad Zloteanu    schedule 14.04.2010
comment
С последним вариантом я поступил правильно, и все же вижу, что он загружает авторов отдельно (попадание в базу данных более одного раза, по количеству комментариев). - person Vitaly; 14.04.2010
comment
guides.rubyonrails.org/, похоже, вы можете вкладывать - person Corey; 14.04.2010
comment
@Corey - да, я следил за этой статьей, как вы можете видеть в моих вопросах, но это не сработало, как ожидалось. - person Vitaly; 14.04.2010
comment
@vlad - ты исследовал, как можно упорядочить комментарии по дате создания? - person Vitaly; 15.04.2010
comment
@post.comments(:include =› [:author], :order =› 'created_at DESC') - person Vlad Zloteanu; 15.04.2010