【问题标题】:OR clause in Rails 5Rails 5 中的 OR 子句
【发布时间】:2016-05-28 13:29:44
【问题描述】:
如何在 Rails 5 ActiveRecord 中获取 (A || B) && ( C || D) 查询?我试过
Post.where(a).or(Post.where(b)).where(c).or(Post.where(d))
但它产生为:(A || B) && C || D。获得所需查询的正确代码是什么?
【问题讨论】:
标签:
ruby-on-rails
activerecord
ruby-on-rails-5
【解决方案1】:
Rails or() 方法没有这个复杂。首次提出时,关于该功能的讨论很长here, on Github
此方法旨在提供一种便捷的 Railsifying 查询方式,但有时事情并没有那么简单。 or() 方法本质上是从左到右读取。
在坚持“Railsy”约定时,您最好的选择是遵循以下方法之一:
Post.where("a OR b").where("c OR d") # Example 1 - SQL with additional where()
Post.where(a: [true, !b]).where(c: [true, !d]) # Example 2 - Array comparison
Post.where("(a OR b) AND (c OR d)") # Example 3 - SQL only
【解决方案2】:
您是否尝试过将每个or 查询下推到Post 模型中的各个命名范围?对我来说,将它们分开会产生不同的结果。试试下面的代码,看看你是否得到了预期的结果(我手边没有 Rails 应用程序,所以我把所有东西都放在一个 Ruby 文件中):
rails_where_or.rb
require 'active_record'
require 'rspec'
ActiveRecord::Base.establish_connection(
adapter: 'sqlite3', database: ':memory:'
)
ActiveRecord::Schema.define do
create_table :posts do |t|
t.boolean :a, default: false
t.boolean :b, default: false
t.boolean :c, default: false
t.boolean :d, default: false
end
end
class Post < ActiveRecord::Base
def self.a_or_b_and_c_or_d
a_or_b.c_or_d
end
def self.a_or_b_and_c_or_d_all_chained
where(a: true).or(where(b: true)).
where(c: true).or(where(d: true))
end
def self.a_or_b
where(a: true).or(where(b: true))
end
def self.c_or_d
where(c: true).or(where(d: true))
end
end
RSpec.describe Post do
let!(:only_a) do
described_class.create!(a: true)
end
let!(:only_b) do
described_class.create!(b: true)
end
let!(:only_c) do
described_class.create!(c: true)
end
let!(:only_d) do
described_class.create!(d: true)
end
let!(:a_and_b) do
described_class.create!(a: true, b: true)
end
let!(:a_and_c) do
described_class.create!(a: true, c: true)
end
let!(:a_and_d) do
described_class.create!(a: true, d: true)
end
let!(:b_and_c) do
described_class.create!(b: true, c: true)
end
let!(:b_and_d) do
described_class.create!(b: true, d: true)
end
let!(:c_and_d) do
described_class.create!(c: true, d: true)
end
describe '.a_or_b_and_c_or_d' do
let(:a_or_b_and_c_or_d) do
described_class.a_or_b_and_c_or_d
end
it 'returns only records that match (A || B) && ( C || D )' do
expect(a_or_b_and_c_or_d).not_to include(only_a)
expect(a_or_b_and_c_or_d).not_to include(only_b)
expect(a_or_b_and_c_or_d).not_to include(only_c)
expect(a_or_b_and_c_or_d).not_to include(only_d)
expect(a_or_b_and_c_or_d).not_to include(a_and_b)
expect(a_or_b_and_c_or_d).not_to include(c_and_d)
expect(a_or_b_and_c_or_d).to include(a_and_c)
expect(a_or_b_and_c_or_d).to include(a_and_d)
expect(a_or_b_and_c_or_d).to include(b_and_c)
expect(a_or_b_and_c_or_d).to include(b_and_d)
end
end
describe '.a_or_b_and_c_or_d_all_chained' do
let(:a_or_b_and_c_or_d_all_chained) do
described_class.a_or_b_and_c_or_d_all_chained
end
it 'returns records that match (A || B) && C || D ' do
expect(a_or_b_and_c_or_d_all_chained).not_to include(only_a)
expect(a_or_b_and_c_or_d_all_chained).not_to include(only_b)
expect(a_or_b_and_c_or_d_all_chained).not_to include(only_c)
expect(a_or_b_and_c_or_d_all_chained).not_to include(a_and_b)
expect(a_or_b_and_c_or_d_all_chained).to include(only_d)
expect(a_or_b_and_c_or_d_all_chained).to include(c_and_d)
expect(a_or_b_and_c_or_d_all_chained).to include(a_and_c)
expect(a_or_b_and_c_or_d_all_chained).to include(a_and_d)
expect(a_or_b_and_c_or_d_all_chained).to include(b_and_c)
expect(a_or_b_and_c_or_d_all_chained).to include(b_and_d)
end
end
end