【发布时间】:2014-05-30 15:06:02
【问题描述】:
我如何以这样的方式获取 arel 组件,我可以执行以下操作:
queries = []
queries << MyModel.some_scope.get_the_arel_component
queries << MyModel.some_scope_with_param("Dave").get_the_arel_component
queries << MyModel.where(:something => 'blah').get_the_arel_component
queries << MyModel.some_scope_with_join_and_merge.get_arel_component
# etc ... (may be any number of queries)
# join each query with OR
combined_query = nil
queries.each do |query|
combined_query ||= query
combined_query = combined_query.or(q)
end
# run the query so it just works
MyModel.where(combined_query)
我在接受类似问题的答案时遇到了一些问题。
假设我有这样的课程:
class Patient
has_one :account
scope :older_than, ->(date) { where(arel_table[:dob].lt(date)) }
scope :with_gender, ->(gender) { where(:gender => gender) }
scope :with_name_like, ->(name) { where("lower(name) LIKE ?", name.downcase) }
scope :in_arrears, -> { joins(:account).merge( Account.in_arrears ) }
end
目标是将任何范围或 where 子句与 OR 结合起来。
One way 将是 Patient.with_name_like("Susan") | Patient.with_name_like("Dave")。这似乎单独运行每个单独的查询,而不是组合成一个查询。我已经排除了这个解决方案。
# this fails because `where_values` for the `with_name_like` scope returns a string
sues = Patient.with_name_like("Susan").where_values.reduce(:and)
daves = Patient.with_name_like("Dave").where_values.reduce(:and)
Patient.where(sues.or(daves))
# this works as `where_values` returns an `Arel::Nodes::Equality` object
ages = Patient.older_than(7.years.ago).where_values.reduce(:and)
males = Patients.with_gender('M').where_values.reduce(:and)
Patient.where(ages.or(males))
# this fails as `in_arrears` scope requires a joins
of_age = Patient.older_than(18.years.ago).where_values.reduce(:and)
arrears = Patients.in_arrears.where_values.reduce(:and)
Patient.where(of_age.or(arrears)) # doesn't work as no join on accounts
Patient.join(:account).where(of_age.or(arrears)) # does work as we have our join
总而言之,当where 被传递一个字符串或查询需要连接时,就会出现 ORing 查询的问题。
我很确定where 会将传递给它的任何内容转换为 arel 对象,这只是访问正确的部分并以正确的方式重新组合它们的问题。我只是还没弄好。
答案最好只使用 ActiveRecord 和 AREL,而不是第三方库。
【问题讨论】:
-
你似乎已经弄清楚了。 Or-ing 很难。我没有答案,只有一些想法。因为你总是需要去
where_values,所以你失去了所需的连接和like运算符。因此,一种解决方案是直接使用 arel 运算符,但这意味着:不对现有范围进行 or-ing。另外:你是如何点赞的。但是恕我直言,您清楚地记录了与 arel 进行或运算的局限性。就我个人而言,我只是避免使用“OR”,或者如果出于性能原因确实需要它,我会编写一个自定义查询。 -
@nathanvda,感谢 cmets 和建议。我不介意直接与 arel 运营商合作,但我无法对它们进行试验,也不知道它们是如何组合在一起的。只是为了了解更多背景知识,我正在尝试对模型进行高级搜索,用户可以在该模型中选择查找与所有 (AND) 或任何 (OR) 可用选项匹配的记录。
-
实际上,amazing website 的 Dan Shultz 建议使用 in 运算符。我想它会像
MyModel.where(:id => MyModel.select(:id).some_scope)。所以在我的第一个示例中,循环将变为queries.each {|q| combined_query.where(:id => q)}。我还没有尝试过,但它似乎可以工作。 -
嗯,实际上考虑了我的最后一条评论,这仍然等同于对每个范围进行 AND 运算。此外,即使在同一张桌子上,它也会对属性进行新的选择。也许我需要更多地思考如何正确应用他的建议。
标签: ruby-on-rails arel