我有一个Book
has_many Reviews
,我已经为Book
添加了一个类方法,以显示最近审核过的'我的主页上有书。我试过这个:
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 :(得分: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行本书以及所有不同的评论。当您选择不同的列表时,您最终会得到本书的一行。这可以是连接表中的任何一个。然后根据此评论的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
答案 1 :(得分:0)
这实际上是一个比ActiveRecord更简单的DB查询,因为它需要一个子查询。以下是我将如何使用查询完成此操作:
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个书籍的列表Book.recently_reviewed.limit(n)
答案 2 :(得分:0)
你试过这个吗?
def self.recently_reviewed
Review.preload(:book).order(created_at: :desc).limit(5).map(&:book)
end