如何在同一模型上组合两个查询的结果?

Pre*_*her 7 ruby-on-rails ruby-on-rails-3

我需要返回十个记录才能在视图中使用.我有一个高度限制性的查询我想使用,但我想要一个限制较少的查询来填写结果,以防第一个查询没有产生十个结果.

只是玩了几分钟,这就是我想出来的,但它不起作用.我认为它不起作用,因为合并意味着在不同的模型上组合查询,但我可能是错的.

class Article < ActiveRecord::Base
...
  def self.listed_articles
    Article.published.order('created_at DESC').limit(25).where('listed = ?', true)
  end

  def self.rescue_articles
    Article.published.order('created_at DESC').where('listed != ?', true).limit(10)
  end

  def self.current
    Article.rescue_articles.merge(Article.listed_articles).limit(10)
  end
...
end
Run Code Online (Sandbox Code Playgroud)

查看控制台,这会强制查询rescue_articles中查询中的lists_articles限制,显示如下内容:

Article Load (0.2ms)  SELECT `articles`.* FROM `articles` WHERE (published = 1) AND (listed = 1) AND (listed != 1) ORDER BY created_at DESC LIMIT 4
Article Load (0.2ms)  SELECT `articles`.* FROM `articles` WHERE (published = 1) AND (listed = 1) AND (listed != 1) ORDER BY created_at DESC LIMIT 6 OFFSET 4
Run Code Online (Sandbox Code Playgroud)

我确信文档中缺少一些可笑的简单方法,但我还没有找到它.

编辑:我想做的是返回最近25篇文章中列出的所有文章.如果这不能得到我的十篇文章,我想在最近的文章中添加足够的文章,其中列出的不是真的,以获得我的十篇文章.

编辑#2:换句话说,合并方法似乎将查询串起来进行一次长查询而不是合并结果.我需要两个查询的前十个结果(列出所列文章的优先级),而不是一个长查询.

Yul*_*ule 11

使用您的初始代码:

您可以使用+加入两个数组,然后获得前10个结果:

  def self.current
    (Article.listed_articles  +  Article.rescue_articles)[0..9]
  end
Run Code Online (Sandbox Code Playgroud)

我想一个非常脏的做法是:

  def self.current
      oldest_accepted = Article.published.order('created_at DESC').limit(25).last
      Artcile.published.where(['created_at > ?', oldest_accepted.created_at]).order('listed DESC').limit(10)
  end
Run Code Online (Sandbox Code Playgroud)


Jul*_*res 7

如果你想要一个ActiveRecord::Relation对象而不是数组,你可以使用:

  • ActiveRecordUnion 宝石

    安装 gem:gem install active_record_union并使用:

    def self.current
      Article.rescue_articles.union(Article.listed_articles).limit(10)
    end
    
    Run Code Online (Sandbox Code Playgroud)
  • 联合范围模块

    创建模块 UnionScope (lib/active_record/union_scope.rb)。

    module ActiveRecord::UnionScope
      def self.included(base)
        base.send :extend, ClassMethods
      end
    
      module ClassMethods
        def union_scope(*scopes)
          id_column = "#{table_name}.id"
           if (sub_query = scopes.reject { |sc| sc.count == 0 }.map { |s|   "(#{s.select(id_column).to_sql})" }.join(" UNION ")).present?
            where "#{id_column} IN (#{sub_query})"
          else
            none
          end
        end
      end
    end
    
    Run Code Online (Sandbox Code Playgroud)

    然后在您的 Article 模型中调用它。

    class Article < ActiveRecord::Base
      include ActiveRecord::UnionScope
      ...
      def self.current
        union_scope(Article.rescue_articles, Article.listed_articles).limit(10)
      end
      ...
    end
    
    Run Code Online (Sandbox Code Playgroud)