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

时间:2010-09-10 11:41:49

标签: ruby-on-rails ruby-on-rails-3 activerecord rails-activerecord

我正在使用Rails3,ActiveRecord

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

e.g。

Person.where(:name => "John").where(:lastname => "Smith")

通常会返回:

name = 'John' AND lastname = 'Smith'

但我想:

`name = 'John' OR lastname = 'Smith'

19 个答案:

答案 0 :(得分:95)

你会做

Person.where('name=? OR lastname=?', 'John', 'Smith')

目前,新的AR3语法没有任何其他OR支持(即没有使用某些第三方gem)。

答案 1 :(得分:45)

使用ARel

t = Person.arel_table

results = Person.where(
  t[:name].eq("John").
  or(t[:lastname].eq("Smith"))
)

答案 2 :(得分:43)

根据this pull request,Rails 5现在支持链接查询的以下语法:

Post.where(id: 1).or(Post.where(id: 2))

通过this gem.

还可以将功能反向移植到Rails 4.2中

答案 3 :(得分: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) }

旧答案

不需要第三方宝石

Person.where(
    Person.where(:name => "John").where(:lastname => "Smith")
      .where_values.reduce(:or)
)

答案 4 :(得分:36)

只需发布相同列或查询的数组语法,以帮助窥视。

Person.where(name: ["John", "Steve"])

答案 5 :(得分: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

更有价值的是能够使用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) }

然后你可以找到所有名字或姓氏为

的人
Person.first_or_last_name('John Smith').or.parent_last_name('Smith')

不是使用此功能的最佳示例,而只是尝试将其与问题相符合。

答案 6 :(得分:21)

你也可以使用MetaWhere gem来混淆你的代码与SQL的东西:

Person.where((:name => "John") | (:lastname => "Smith"))

答案 7 :(得分:9)

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

{ where("name = ? or name = ?", a, b) }

答案 8 :(得分:8)

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

Person.where{(name == "John") | (lastname == "Smith")}

您可以混合使用AND / OR以及其他许多awesome things

答案 9 :(得分:8)

Rails / ActiveRecord的更新版本可能原生支持此语法。它看起来类似于:

Foo.where(foo: 'bar').or.where(bar: 'bar')

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

现在,只需坚持以下作品:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

答案 10 :(得分: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

我尝试使用.or链接命名范围并且没有运气,但这适用于查找设置了这些布尔值的任何内容。像

一样生成SQL
SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))

答案 11 :(得分:4)

Rails 4

scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }

答案 12 :(得分:3)

如果你不能手动写出where子句来包含“或”语句(即你想要组合两个范围),你可以使用union:

Model.find_by_sql("#{Model.scope1.to_sql} UNION #{Model.scope2.to_sql}")

(来源:ActiveRecord Query Union

这将返回与任一查询匹配的所有记录。但是,这会返回一个数组,而不是一个arel。如果你真的想要返回一个arel,你可以查看这个要点:https://gist.github.com/j-mcnally/250eaaceef234dd8971b

只要你不介意猴子修补铁轨,这将完成这项工作。

答案 13 :(得分:2)

另请参阅以下相关问题:hereherehere

对于rails 4,基于this articlethis original answer

Person
  .unscoped # See the caution note below. Maybe you want default scope here, in which case just remove this line.
  .where( # Begin a where clause
    where(:name => "John").where(:lastname => "Smith")  # join the scopes to be OR'd
    .where_values  # get an array of arel where clause conditions based on the chain thus far
    .inject(:or)  # inject the OR operator into the arels 
    # ^^ Inject may not work in Rails3. But this should work instead:
    .joins(" OR ")
    # ^^ Remember to only use .inject or .joins, not both
  )  # Resurface the arels inside the overarching query

注意最后文章的注意事项:

  

Rails 4.1 +

     

Rails 4.1将default_scope视为常规范围。默认   范围(如果有的话)包含在where_values结果中   inject(:或)将在默认范围和您的范围之间添加或声明   哪里。那很糟糕。

     

要解决这个问题,您只需要取消查询。

答案 14 :(得分:1)

如果您想提供一个范围(而不是显式地处理整个数据集),那么这就是Rails 5应该做的事情:

public class ServiceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.drawable.logob_icon_prestigesalon)
                .setContentTitle("ABC")
                .setContentText("time to go")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        Toast.makeText(context,"service",Toast.LENGTH_LONG).show();

        NotificationManager notificationmanager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationmanager.notify(0, builder.build());
    }
}

或者:

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

  <receiver android:name=".ServiceReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

        <!-- Will not be called unless the application explicitly enables it -->
        <receiver android:name=".DeviceBootReceiver"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

答案 15 :(得分:0)

squeel gem提供了一种非常简单的方法来实现这一目标(在此之前我使用了类似@ coloradoblue的方法):

names = ["Kroger", "Walmart", "Target", "Aldi"]
matching_stores = Grocery.where{name.like_any(names)}

答案 16 :(得分:0)

原来问题的答案是,您可以将范围加入&#39;或&#39;而不是&#39;而不是&#39;似乎是#34;不,你不能&#34;。但是您可以手动编写完全不同的作用域或查询,或使用ActiveRecord中的其他框架,例如MetaWhere或Squeel。在我的案例中没用;

我是pg_search生成的范围,它比select更多,它包含了ASC的顺序,这使得一个干净的联合变得混乱。我想&#39;或&#39;它有一个手工制作的范围,可以完成我在pg_search中无法做到的事情。所以我必须这样做。

Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")

即。将范围转换为sql,在每个范围内放置括号,将它们组合在一起,然后使用生成的sql查找find_by_sql。它有点垃圾,但确实有效。

不,不要告诉我,我可以使用&#34;反对:[:name,:code]&#34;在pg_search中,我喜欢这样做,但是&#39; name&#39; field是一个hstore,pg_search还不能处理。因此,名称范围必须手工制作,然后与pg_search范围联合。

答案 17 :(得分:0)

这是一种非常方便的方法,并且在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")
  )

答案 18 :(得分:-2)

names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)