2014-12-10 22 views
3

使用Rails 4.1.6和Ruby 2.1.2并给予下述的三个对象:导轨当使用4保持连接物体起的顺序包括:在通过的has_many关联

class FilmCollection 
    has_many :film_collection_films, -> { order(:position) } 
    has_many :films, through: :film_collection_films 
end 

class FilmCollectionFilm 
    belongs_to :film_collection_film 
    belongs_to :film 
end 

class Film 
    has_many :film_collection_films 
end 

FilmCollections是膜的集合,其成员通过连接对象FilmCollectionFilm表示。 FilmCollectionFilm有一个位置列,以便集合中的给定成员可以重新排序(像电影队列一样),因此我们为什么需要连接对象而不是has_and_belongs_to_many。

当我试图一次加载多个集合的电影时,出现了我的问题。

调用FilmCollection.first.films会得到我的查询,看起来像:

SELECT `film_collections`.* FROM `film_collections` 
    ORDER BY `film_collections`.`id` ASC LIMIT 1 
SELECT `films`.* FROM `films` 
    INNER JOIN `film_collection_films` ON `films`.`id` = `film_collection_films`.`film_id` 
    WHERE `film_collection_films`.`extensional_film_collection_id` = 1 
    ORDER BY `film_collection_films`.`position` ASC 

哪些订单的基础上正确的加入对象位置的电影。

但不是调用FilmCollection.includes(:电影).first.films会让我下面的查询:

SELECT `film_collections`.* FROM `film_collections` 
    ORDER BY `film_collections`.`id` ASC LIMIT 1 
SELECT `film_collection_films`.* FROM `film_collection_films` 
    WHERE `film_collection_films`.`film_collection_id` IN (1) 
    ORDER BY `film_collection_films`.`position` ASC 
SELECT `films`.* FROM `films` WHERE `films`.`id` IN (1, 16, 53, 185) 

采集到的正确的电影,但无视在查询的第二部分的顺序。如何在使用.includes()进行加载时仍然保持对连接对象的排序,但仍然没有n + 1查询?

+0

尝试使用'joins'来代替:'FilmCollection.joins(:films).first.films'? – Surya 2014-12-11 07:48:27

+0

我确实找到了即将发布的解决方案。这可能会起作用,但我认为加入总体上比总体速度要慢,特别是对于大型表格。虽然我可能是错的。 – BitPuncher 2014-12-12 01:08:38

+0

注意:在我看来,这是一个只能在Rails 5中修复的bug:/ https://github.com/rails/rails/pull/18766 – 2017-04-04 16:11:39

回答

2

事实证明,您可以通过明确包含连接对象以及通过它的关联来解决此问题。下面一行将正确选择所有内容,并保留连接对象上的所有条件/顺序。

FilmCollection.includes(film_collection_films: :film) 

如果你做到这FilmCollection.includes(:films)那么它会选择FilmCollectionFilms无论如何,然后就折腾他们。所以,上面的代码会做相同数量的工作,但正确地构建查询。

+1

也适用于rails 3.2。我还想指出,'FilmCollection.includes(film_collection_films :: film)'和'FilmCollection.includes(:films)'的sql查询本身是**完全相同的**(使用.to_sql进行测试)。因此,我的教育猜测是如何保存顺序(而不是挖掘activerecord代码)是在执行查询和构建数组之后,通过ruby的** sort **方法。这可能会导致性能下降,具体取决于记录总数。尽管我的需求,这是完美的! – mkralla11 2015-01-20 20:46:35