Rails Arel 选择不同的列
我在新的 scope
方法(Arel 0.4.0、Rails 3.0.0.rc)中遇到了一些障碍,
基本上我有:
一个 topics
模型,其中 has_many :comments
和 belongs_to :topics
的 comments
模型(带有 topic_id
列)。
我正在尝试获取“热门主题”的集合,即最近评论的主题。当前代码如下:
# models/comment.rb
scope :recent, order("comments.created_at DESC")
# models/topic.rb
scope :hot, joins(:comments) & Comment.recent & limit(5)
如果我执行 Topic.hot.to_sql
,则会触发以下查询:
SELECT "topics".* FROM "topics" INNER JOIN "comments"
ON "comments"."topic_id" = "topics"."id"
ORDER BY comments.created_at DESC LIMIT 5
这工作正常,但它可能返回重复的主题 - 如果主题 #3 最近被评论了多次,它会被退回几次。
我的问题
我将如何返回一组不同的主题,请记住我仍然需要访问 comments.created_at
字段,以显示最后一个主题是多久前帖子是?我会想象一些类似于 distinct
或 group_by
的东西,但我不太确定如何最好地去做。
非常感谢任何意见/建议 - 我已经添加了 100 代表赏金,希望很快能找到一个优雅的解决方案。
I've hit a slight block with the new scope
methods (Arel 0.4.0, Rails 3.0.0.rc)
Basically I have:
A topics
model, which has_many :comments
, and a comments
model (with a topic_id
column) which belongs_to :topics
.
I'm trying to fetch a collection of "Hot Topics", i.e. the topics that were most recently commented on. Current code is as follows:
# models/comment.rb
scope :recent, order("comments.created_at DESC")
# models/topic.rb
scope :hot, joins(:comments) & Comment.recent & limit(5)
If I execute Topic.hot.to_sql
, the following query is fired:
SELECT "topics".* FROM "topics" INNER JOIN "comments"
ON "comments"."topic_id" = "topics"."id"
ORDER BY comments.created_at DESC LIMIT 5
This works fine, but it potentially returns duplicate topics - If topic #3 was recently commented on several times, it would be returned several times.
My question
How would I go about returning a distinct set of topics, bearing in mind that I still need to access the comments.created_at
field, to display how long ago the last post was? I would imagine something along the lines of distinct
or group_by
, but I'm not too sure how best to go about it.
Any advice / suggestions are much appreciated - I've added a 100 rep bounty in hopes of coming to an elegant solution soon.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
解决方案 1
这不使用 Arel,而是使用 Rails 2.x 语法:
确保对
created_at
和topic_id
列建立索引>评论表。解决方案 2
在您的
Topic
模型中添加last_comment_id
列。创建评论后更新last_comment_id
。这种方法比使用复杂的 SQL 来确定最后一条注释要快得多。例如:
这比运行复杂的 SQL 查询来确定热点主题要高效得多。
Solution 1
This doesn't use Arel, but Rails 2.x syntax:
Make sure you index the
created_at
andtopic_id
column in thecomments
table.Solution 2
Add a
last_comment_id
column in yourTopic
model. Update thelast_comment_id
after creating a comment. This approach is much faster than using complex SQL to determine the last comment.E.g:
This is much efficient than running a complex SQL query to determine the hot topics.
这在大多数 SQL 实现中并不是那么优雅。一种方法是首先获取按 topic_id 分组的最近五个评论的列表。然后通过使用 IN 子句进行子选择来获取 comments.created_at 。
我对 Arel 很陌生,但是这样的东西可以工作
This is not that elegant in most SQL implementations. One way is to first get the list of the five most recent comments grouped by topic_id. Then get the comments.created_at by sub selecting with the IN clause.
I'm very new to Arel but something like this could work
为了实现这一点,您需要有一个带有
GROUP BY
的范围来获取每个主题的最新评论。然后,您可以按created_at
订购此范围,以获得有关主题的最新评论。以下内容对我来说使用 sqlite
我使用了以下 seeds.rb 来生成测试数据
以下是测试结果
为 sqlite(重新格式化)生成的 SQL 非常简单,我希望 Arel 能为其他引擎呈现不同的 SQL,因为这在许多数据库引擎中肯定会失败,因为主题中的列不在“分组依据列表”中。如果这确实出现了问题,那么您可以通过将所选列限制为只有 comments.topic_id 来克服它
In order to accomplish this you need to have a scope with a
GROUP BY
to get the latest comment for each topic. You can then order this scope bycreated_at
to get the most recent commented on topics.The following works for me using sqlite
I used the following seeds.rb to generate the test data
And the following are the test results
The SQL generated for sqlite(reformatted) is extremely simple and I hope Arel would render different SQL for other engines as this would certainly fail in many DB engines as the columns within Topic are not in the "Group by list". If this did present a problem then you could probably overcome it by limiting the selected columns to just comments.topic_id
由于问题是关于 Arel 的,我想我应该添加这个,因为 Rails 3.2.1 将
uniq
添加到 QueryMethods:如果将
.uniq
添加到 Arel 它将DISTINCT
添加到select
语句中。例如
Topic.hot.uniq
也适用于范围:
例如
scope :hot, joins(:comments).order("comments.created_at DESC").limit(5).uniq
所以我认为这
也应该可行。
请参阅http://apidock.com/rails/ActiveRecord/QueryMethods/uniq
Since the question was about Arel, I thought I'd add this in, since Rails 3.2.1 adds
uniq
to the QueryMethods:If you add
.uniq
to the Arel it addsDISTINCT
to theselect
statement.e.g.
Topic.hot.uniq
Also works in scope:
e.g.
scope :hot, joins(:comments).order("comments.created_at DESC").limit(5).uniq
So I would assume that
should also probably work.
See http://apidock.com/rails/ActiveRecord/QueryMethods/uniq