2

我正在尝试有条件地建立一个模型列表。IE

@items = []

if some_condition
    @items << MyModel.where(...)
end

if another_condition
    @items << MyModel.where(...)
end

...

这是行不通的。它实际上会建立一个正确对象的数组,但是当我访问数组中项目的字段和关系时,它们“找不到”。我尝试了其他一些方法,例如@items = MyModel.noneand @items = {}.merge但没有任何效果。我似乎无法弄清楚这一点。

有条件地建立这样一个集合的最佳方法是什么?

更新我希望能够维护 ,Relation以便我可以继续使用.where.first其他Relation方法查询它。

4

3 回答 3

5

<<会将一个项目附加到数组中,因此查询将不会运行,而是作为 a 附加ActiveRecord::Relation,如果您使用all,您最终会得到一个数组数组。您应该使用concat附加整个集合(+=也可以,但如果您的查询返回大量记录,则会实例化影响性能的不必要的临时数组):

@items = []

if some_condition
    @items.concat(MyModel.where(...))
end

if another_condition
    @items.concat(MyModel.where(...))
end
于 2013-10-01T13:38:03.107 回答
3

您的连接方法将导致多个数据库查询,并且不能链接,这通常是分页、范围、分组和排序所需要的。

相反,我会收集您的条件并在最后合并。看起来您的条件实际上是 OR 类型查询,而不是更容易链接的 AND 类型查询。

因此,请执行以下操作:

@queries = []

if some_condition
  # Relation condition
  @queries << MyModel.where(...)
end

if another_condition
  # another Relation condition
  @queries << MyModel.where(...)
end

if and_another_condition
  # Hash equality and IN conditions
  @queries << { attr1: 'foo', attr2: [1,2,3] }
end

if yet_another_condition
  # string condition with argument
  @queries << ['attr LIKE ? ', arg]
end

@items = MyModel.any_of(*queries).order(...).page(...).per(...)

神奇之处在于一个漂亮的自定义 AR 扩展方法any_of?,用于使用 Arel 组合 OR 类型查询。它可以采用关系、字符串条件、哈希条件或数组来插入 where() 子句。

# put in config/initializers/ar_any_of.rb or in lib/xxxx
class ActiveRecord::Base
  def self.any_of(*queries)
    where(
      queries.map { |query|
        query = where(query) if [String, Hash].any? { |type| query.kind_of? type }
        query = where(*query) if query.kind_of? Array
        query.arel.constraints.reduce(:and)
      }.reduce(:or)
    )
  end
end

它可以与以下各种条件一起使用以生成单个 SQL:

Country.any_of(
  Country.where(alpha2: 'AU'),
  { alpha2: ['NZ', 'UK'] },
  ['alpha2 LIKE ?', 'U%']).to_sql

# => "SELECT \"countries\".* FROM \"countries\"  WHERE (((\"countries\".\"alpha2\" = 'AU' OR \"countries\".\"alpha2\" IN ('NZ', 'AD')) OR (alpha2 LIKE 'U%')))"
于 2013-10-02T13:38:09.350 回答
-1

我认为答案相当简单。

您可以将您的集合初始化为匿名范围

@items = MyModel.scoped

这是一个 ActiveRecord::Relation。

**注意,scoped在 RoR 4 上已弃用,但all做同样的事情http://blog.remarkablelabs.com/2012/12/what-s-new-in-active-record-rails-4-countdown-to-2013所以我们的例子是

@items = MyModel.all

之后,链接额外的条件(按条件)应该很容易:

@items = @items.where(owner: me) if me.present?

@items = @items.group(:attribute_1) if show_groups
于 2013-10-04T06:50:53.783 回答