繁体   English   中英

如何查询 ActiveRecord 以仅返回每个 object 的第一个实例,其中 enum = X、Y、Z?

[英]How do I query ActiveRecord to return only the first instance of each object where enum = X, Y, Z?

我们的 CMS 有一篇文章 model,它使用枚举来定义文章类型。

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

有没有办法在一个查询中返回这三个记录?

我可以使用Article.where(display_format: [:webpage, :blog, :interactive]).order(:pub_date)然后过滤集合以获取每个类别的第一项,但我希望有一种更有效的方法在 ActiveRecord 中执行此操作。

如果您已按发布日期顺序对其进行排序,则可以使用.limit(n)其中 n 是您想要获得的最新文章的数量。

您还可以使用 .first( .first(n).last(n) ,具体取决于您获得所有结果的方式,它们将返回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 对使用的这些附加功能一无所知,因此没有支持或内置方法来使用它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM