我如何查询 ActiveRecord 以仅 return 每个对象的第一个实例,其中 enum = X, Y, Z?
How do I query ActiveRecord to return only the first instance of each object where enum = X, Y, Z?
我们的 CMS 有一个文章模型,它使用枚举来定义文章类型。
enum display_format: {
webpage: 0,
blog: 1,
interactive: 2,
advertisement: 3,
experience: 4,
product_questions: 5
}
在我们的主页上,我显示每个类别中的最新文章,每个当前都作为单独的查询。
Article.where(display_format: :webpage).order(:pub_date).first
Article.where(display_format: :blog).order(:pub_date).first
Article.where(display_format: :interactive).order(:pub_date).first
有没有办法在单个查询中 return 这三个记录?
我可以使用 Article.where(display_format: [:webpage, :blog, :interactive]).order(:pub_date)
然后过滤集合以获取每个类别的第一项,但我希望在 ActiveRecord 中有更有效的方法来执行此操作。
如果您按出版日期排序,您可以使用 .limit(n)
,其中 n 是您想要获取的最新文章的数量。
您还可以使用 .first(n)
或 .last(n)
,具体取决于您获得所有结果的方式,它们将 return n
许多文章。
scope :most_recent, ->(formats, num) {
from(
<<~SQL
(
SELECT articles.*,
row_number() OVER (
PARTITION BY articles.display_format
ORDER BY articles.updated_at DESC
) AS rn
FROM articles
) articles
SQL
).where(display_format: formats).where("articles.rn <= #{num}")
}
Article.most_recent([:x, :y, :z], 1) # get top 1 each display_format
Article.most_recent([:x, :y, :z], 10) # get top 10 each display_format
注意当你的数据库长大并且你关心性能时,你应该考虑table partition
有不同的方法可以实现这一点,这在很大程度上取决于您使用的 RDBMS,是否有索引来覆盖查询、版本等。但是如果您使用的是 Postgres,则可以使用 ROW_NUMBER
加上OVER (PARTITION BY ...)
:
Article
.from(
Article
.select(
:display_format,
:pub_date,
'ROW_NUMBER() OVER(PARTITION BY articles.display_format ORDER BY articles.pub_date DESC) AS idx'
)
.where(display_format: [0, 1, 2]),
:articles
)
.where(idx: 1)
在这种情况下,如果适当注意绑定您收到的任何值,使用“原始”SQL 字符串没有问题。如您所见,ActiveRecord 对使用的这些附加函数一无所知,因此没有支持或内置方法来使用它们。
我们的 CMS 有一个文章模型,它使用枚举来定义文章类型。
enum display_format: {
webpage: 0,
blog: 1,
interactive: 2,
advertisement: 3,
experience: 4,
product_questions: 5
}
在我们的主页上,我显示每个类别中的最新文章,每个当前都作为单独的查询。
Article.where(display_format: :webpage).order(:pub_date).first
Article.where(display_format: :blog).order(:pub_date).first
Article.where(display_format: :interactive).order(:pub_date).first
有没有办法在单个查询中 return 这三个记录?
我可以使用 Article.where(display_format: [:webpage, :blog, :interactive]).order(:pub_date)
然后过滤集合以获取每个类别的第一项,但我希望在 ActiveRecord 中有更有效的方法来执行此操作。
如果您按出版日期排序,您可以使用 .limit(n)
,其中 n 是您想要获取的最新文章的数量。
您还可以使用 .first(n)
或 .last(n)
,具体取决于您获得所有结果的方式,它们将 return n
许多文章。
scope :most_recent, ->(formats, num) {
from(
<<~SQL
(
SELECT articles.*,
row_number() OVER (
PARTITION BY articles.display_format
ORDER BY articles.updated_at DESC
) AS rn
FROM articles
) articles
SQL
).where(display_format: formats).where("articles.rn <= #{num}")
}
Article.most_recent([:x, :y, :z], 1) # get top 1 each display_format
Article.most_recent([:x, :y, :z], 10) # get top 10 each display_format
注意当你的数据库长大并且你关心性能时,你应该考虑table partition
有不同的方法可以实现这一点,这在很大程度上取决于您使用的 RDBMS,是否有索引来覆盖查询、版本等。但是如果您使用的是 Postgres,则可以使用 ROW_NUMBER
加上OVER (PARTITION BY ...)
:
Article
.from(
Article
.select(
:display_format,
:pub_date,
'ROW_NUMBER() OVER(PARTITION BY articles.display_format ORDER BY articles.pub_date DESC) AS idx'
)
.where(display_format: [0, 1, 2]),
:articles
)
.where(idx: 1)
在这种情况下,如果适当注意绑定您收到的任何值,使用“原始”SQL 字符串没有问题。如您所见,ActiveRecord 对使用的这些附加函数一无所知,因此没有支持或内置方法来使用它们。