Rails:如何使用OR而不是AND链接范围查询?

use*_*715 131 activerecord ruby-on-rails ruby-on-rails-3 rails-activerecord

我正在使用Rails3,ActiveRecord

只是想知道如何用OR语句而不是AND来链接范围.

例如

Person.where(:name => "John").where(:lastname => "Smith")
Run Code Online (Sandbox Code Playgroud)

这通常会返回name ='John'和lastname ='Smith',但我想:

name ='John'或lastname ='Smith'

Pet*_*ros 100

你会的

Person.where('name=? OR lastname=?', 'John', 'Smith')
Run Code Online (Sandbox Code Playgroud)

现在,新的AR3语法没有任何其他OR支持(没有使用某些第三方gem).

  • 为了安全起见,值得表达为Person.where("name =?OR lastname =?","John','Smith') (21认同)
  • Rails 4没有变化,但是Rails 5 [将内置`.or`](https://github.com/rails/rails/commit/9e42cf019f2417473e7dcbfcb885709fa2709f89). (11认同)
  • 这仍然适用于Rails 4?Rails 4中没有更好的结果? (6认同)
  • 这不是范围,只是2 where子句,或非常具体的范围情况.如果我想链接更复杂的范围,例如使用连接,该怎么办? (3认同)
  • 使用Rails 5,您还可以执行类似Person.where("name ='John'").或(Person.where("lastname ='Smith'"))的操作 (2认同)

Rya*_*eaf 51

根据这个拉取请求,Rails 5现在支持链接查询的以下语法:

Post.where(id: 1).or(Post.where(id: 2))
Run Code Online (Sandbox Code Playgroud)

通过这个gem还可以将功能反向移植到Rails 4.2中.


Dan*_*vin 48

使用ARel

t = Person.arel_table

results = Person.where(
  t[:name].eq("John").
  or(t[:lastname].eq("Smith"))
)
Run Code Online (Sandbox Code Playgroud)

  • @OlivierLacan Arel是一个公共API,或者更准确地说,它暴露了一个.Active Record只是提供了便于使用它的方法,在它自己使用它是完全有效的.它遵循语义版本控制,因此除非您要更改主要版本(3.xx => 4.xx),否则无需担心中断更改. (7认同)
  • 我正在使用Postgres,这似乎工作得很好. (2认同)

kis*_*ber 38

Rails4的更新

不需要第三方宝石

a = Person.where(name: "John") # or any scope 
b = Person.where(lastname: "Smith") # or any scope 
Person.where([a, b].map{|s| s.arel.constraints.reduce(:and) }.reduce(:or))\
  .tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }
Run Code Online (Sandbox Code Playgroud)

老答案

不需要第三方宝石

Person.where(
    Person.where(:name => "John").where(:lastname => "Smith")
      .where_values.reduce(:or)
)
Run Code Online (Sandbox Code Playgroud)

  • 这是一篇不错的文章解释它https://coderwall.com/p/dgv7ag/or-queries-with-arrays-as-arguments-in-rails-4 (6认同)
  • 在资源和方法方面,这确实是OP问题的唯一纯粹答案.另一个答案是(a)要求第三方api或(b)要求在文本块内插入`或`或其他运算符,这两者都不会反映在OP的代码中. (5认同)
  • 在我的例子中使用了`... where_values.join('OR')` (4认同)
  • 你可以省略`.tap {| sc | sc.bind_values = [A,B] .MAP(:bind_values)}`如果你的范围不包含需要绑定任何变量的一部分. (2认同)

dai*_*no3 36

只需发布相同列OR查询的Array语法,以帮助窥视.

Person.where(name: ["John", "Steve"])
Run Code Online (Sandbox Code Playgroud)

  • @steven_noble - 这就是为什么我用或查询加粗"同一列".我可以完全删除评论,但认为这对于阅读"或"查询SO问题的人会有所帮助. (11认同)
  • 问题检查约翰反对名字和史密斯反对**姓氏** (2认同)

Sim*_*tsa 22

您还可以使用MetaWhere gem来将您的代码与SQL内容混淆:

Person.where((:name => "John") | (:lastname => "Smith"))
Run Code Online (Sandbox Code Playgroud)

  • +1.MetaWhere的继任者是同一作者的[squeel](https://github.com/ernie/squeel). (3认同)

cod*_*mev 22

如果有人正在寻找这个更新的答案,看起来有一个现有的拉取请求,以进入Rails:https://github.com/rails/rails/pull/9052.

感谢@ j-mcnally的ActiveRecord猴子补丁(https://gist.github.com/j-mcnally/250eaaceef234dd8971b),您可以执行以下操作:

Person.where(name: 'John').or.where(last_name: 'Smith').all
Run Code Online (Sandbox Code Playgroud)

更有价值的是能够将范围链接到OR:

scope :first_or_last_name, ->(name) { where(name: name.split(' ').first).or.where(last_name: name.split(' ').last) }
scope :parent_last_name, ->(name) { includes(:parents).where(last_name: name) }
Run Code Online (Sandbox Code Playgroud)

然后,您可以找到所有具有姓或姓的人或具有姓氏的父母

Person.first_or_last_name('John Smith').or.parent_last_name('Smith')
Run Code Online (Sandbox Code Playgroud)

这不是使用它的最佳示例,而只是尝试将其与问题相符.

  • 可以在这里找到最新的讨论和将其转换为Rails核心的pull请求:https://github.com/rails/rails/pull/16052 Rails 5.0针对官方发布的`.or`链功能.我能够使用我在Rails上提到的猴子补丁> = 3.2; 但是您可能希望等待Rails 5对代码进行任何长期更改. (3认同)
  • 您需要哪个版本或Rails? (2认同)

Fre*_*ore 11

我现在正在 Rails 6 中工作,看来现在这是可能的。使用OP的查询:

# in the Person model:
scope :john, -> { where(name: "John") }
scope :smith, -> { where(lastname: "Smith") }
scope :john_or_smith, -> { john.or(self.smith) }
Run Code Online (Sandbox Code Playgroud)


小智 9

对我来说(Rails 4.2.5)它只能这样工作:

{ where("name = ? or name = ?", a, b) }
Run Code Online (Sandbox Code Playgroud)


thi*_*ign 9

如果您希望提供一个范围(而不是明确地处理整个数据集),那么您应该使用 Rails 5 执行以下操作:

scope :john_or_smith, -> { where(name: "John").or(where(lastname: "Smith")) }
Run Code Online (Sandbox Code Playgroud)

或者:

def self.john_or_smith
  where(name: "John").or(where(lastname: "Smith"))
end
Run Code Online (Sandbox Code Playgroud)


dhu*_*han 8

如果您使用Rails 3.0+,这将是MetaWhere的一个很好的候选者,但它不适用于Rails 3.1.你可能想尝试一下口号.它是由同一作者制作的.以下是您如何执行基于OR的链:

Person.where{(name == "John") | (lastname == "Smith")}
Run Code Online (Sandbox Code Playgroud)

你可以混合和匹配AND/OR,以及许多其他很棒的东西.


Chr*_*ini 8

Rails/ActiveRecord的更新版本可以本机支持此语法.它看起来类似于:

Foo.where(foo: 'bar').or.where(bar: 'bar')
Run Code Online (Sandbox Code Playgroud)

如此拉动请求中所述https://github.com/rails/rails/pull/9052

现在,只需坚持下面的工作:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')
Run Code Online (Sandbox Code Playgroud)

  • Rails 4.2.5不支持 (5认同)

gen*_*abs 6

Rails 4 + Scope + Arel

class Creature < ActiveRecord::Base
    scope :is_good_pet, -> {
        where(
            arel_table[:is_cat].eq(true)
            .or(arel_table[:is_dog].eq(true))
            .or(arel_table[:eats_children].eq(false))
        )
    }
end
Run Code Online (Sandbox Code Playgroud)

我尝试用.or链接命名的范围并且没有运气,但这适用于找到那些布尔值设置的任何东西.像生成SQL一样

SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))
Run Code Online (Sandbox Code Playgroud)


Jig*_*att 5

这是一种非常方便的方法,并且在 Rails 5 中运行良好:

Transaction
  .where(transaction_type: ["Create", "Correspond"])
  .or(
    Transaction.where(
      transaction_type: "Status",
      field: "Status",
      newvalue: ["resolved", "deleted"]
    )
  )
  .or(
    Transaction.where(transaction_type: "Set", field: "Queue")
  )
Run Code Online (Sandbox Code Playgroud)