我如何查询 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 对使用的这些附加函数一无所知,因此没有支持或内置方法来使用它们。