我正在使用Rails3,ActiveRecord
只是想知道如何使用OR语句而不是AND来链接范围。
e.g。
Person.where(:name => "John").where(:lastname => "Smith")
通常会返回:
name = 'John' AND lastname = 'Smith'
但我想:
`name = 'John' OR lastname = 'Smith'
答案 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))
还可以将功能反向移植到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链接命名范围并且没有运气,但这适用于查找设置了这些布尔值的任何内容。像
一样生成SQLSELECT '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}")
这将返回与任一查询匹配的所有记录。但是,这会返回一个数组,而不是一个arel。如果你真的想要返回一个arel,你可以查看这个要点:https://gist.github.com/j-mcnally/250eaaceef234dd8971b。
只要你不介意猴子修补铁轨,这将完成这项工作。
答案 13 :(得分:2)
对于rails 4,基于this article和this 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)