2017-10-07 18 views
0

我有一个Book has_many Reviews,我已经添加了一个类方法Book在我的主页上显示'最近审查'的书籍。我已经试过这样:Rails:订购连接表后删除重复项

def self.recently_reviewed 
    Book.joins(:reviews).order('reviews.created_at DESC').limit(5) 
end 

将会产生很多重复的记录,所以我尝试使用distinct像这样:

Book.joins(:reviews).order('reviews.created_at DESC').distinct.limit(5) 

我也尝试过,后.order,这

ActiveRecord::StatementInvalid: PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list 

我对如何解决这个问题有点困惑,我应该下降到.select有更多的灵活性?

+0

如果您的关联设置正确,为什么不直接找到最后一个'n''评论'的'Book.title'? – Andy

回答

1

Book.joins(:reviews).order('reviews.created_at DESC').distinct

你想选择的书籍和评论连接表不同的预订,然后根据reviews.created_at时间顺序不同的预订名单。 SQL会是这样的:

SELECT DISTINCT "books"."id" FROM "books" INNE JOIN "reviews" ON "reviews"."book_id" = "books"."id" ORDER BY reviews.created_at 

有一个很好的理由,这是不允许的。因为结果是不确定的。想象一下,你对一本书有100条评论。在连接表中,您将有100行本书与所有不同的评论。当你选择一个不同的列表时,你最终会看到这本书的一行。这可以是连接表中100个中的任何一个。然后,您可以根据此评论的created_at来订购。由于审查可以是100中的任何一个,所以每次订单都可能不同。

这将是完美的罚款:

Book.joins(:reviews).order('books.id DESC').distinct

因为它没关系是第100行它都会对那本书的books.id是一样的。

回到你的问题。看起来你正试图获得最新评论的5本书。我没有看到一个简单的方法来做到这一点,但这里是我的解决方案:

res = Review.group("book_id").maximum("created_at") # {book_id => create_at}, each book with its most recent review time 
arr = res.to_a.sort { |a,b| b[1]<=>a[1] } #array sorted by created_at in desc order 
arr.map{ |n| n[0] }.take(5) #top 5 books' ids with most recent reviews 
0

这实际上是一个比较复杂的DB查询比ActiveRecord的可以处理它最简单的形式,因为它需要一个子查询。下面是我会怎么做这完全与查询:

SELECT book.* 
FROM  book 
     INNER JOIN books on reviews.book_id = books.id 
WHERE reviews.created_on = (SELECT MAX(reviews.created_at) 
           FROM reviews 
           WHERE reviews.book_id = books.id) 
GROUP BY books.id 

转换成ActiveRecord的这个我会做到以下几点:

class Book 
    scope :recently_reviewed, joins(:reviews) 
    .where('reviews.created_on = (SELECT MAX(books.created_at) FROM reviews WHERE reviews.book_id = books.id)') 
    .group('books.id') 
end 

然后,您可以得到的是有一个最后审查的所有书籍的列表通过执行以下操作:

Book.recently_reviewed 

您可以通过

然后得到的书籍n个列表
0

你试过这个吗?

def self.recently_reviewed 
    Review.preload(:book).order(created_at: :desc).limit(5).map(&:book) 
end