Rails3: combine scope with OR
From Arel documentation
The
OR
operator is not yet supported. It will work like this:
users.where(users[:name].eq('bob').or(users[:age].lt(25)))
This RailsCast shows you how to use the .or
operator. However, it works with Arel objects while you have instances of ActiveRecord::Relation
.
You can convert a relation to Arel using Product.name_a.arel
, but now you have to figure out how to merge the conditions.
Combine multiple scope or where queries with OR
Since you're open to using a third party library, how about Ransack?
It has a very robust implementation allowing for all kinds of and and or condition combinations and works well with associated models as well.
For a use case like yours where there are a few predefined queries/scopes that I want the user to be able to select from and run the or
combination of them, I use ransack's out of the box implementation and then on the view level, I use javascript to insert hidden fields with values that will result in the structured params hash ransack is expecting in the controller.
All of your scopes are simple to define in a view using ransack helpers. Your code should look like:
Controller
def index
@q = Patient.search(params[:q])
@patients = @q.result(distinct: true)
end
View
<%= search_form_for @q do |f| %>
<%= f.label :older_than %>
<%= f.date_field :dob_lt %>
<%= f.label :with_gender %>
<%= f.text_field :gender_eq %>
<%= f.label :with_name_like %>
<%= f.text_field :name_cont %>
<%= f.label :in_arrears_exceeding %>
<%= f.text_field :accounts_total_due_gte %>
<%= f.submit %>
<% end %>
Also, if you want more control over the anding
and oring
take a look at the complex search form builder example using ransack.
How to combine two scopes with OR
Answering my own question. I think I figured out a way.
where("pages.id IN (?) OR pages.id IN (?)",
Page.where(
"user_id IN (?) OR user_id = ?",
user.watched_ids, user.id
),
Page
.joins(:attachments)
.where("attachments.tag_id IN (?)", user.tags)
.select("DISTINCT pages.*")
)
It seems to be working so far, hope this is it!
How to combine scopes in Ruby on Rails 3?
In SQL, you can search by multiple conditions. You have defined two scoped, and they can be chained together as follows:
Project.search_by_name('fooproject').search_by_client('misterx')
This creates the following SQL:
SELECT "projects".* FROM "projects" WHERE "projects".name LIKE '%fooproject%' AND "projects".client_id LIKE '%misterx'
The operator joining these two conditions is "AND" which means that the result would be those that meet both conditions, not either condition.
There are a few ways to retrieve projects having a certain name, or projects belonging to a client. The most simple way is to create a new scope which specifies the OR operator:
scope :search_by_name_or_client, lambda { |name, client| where('name LIKE ? OR client_id LIKE ?', "%#{name}%", "%#{client}%") }
You may also want to look at SQL UNIONs, which combines the result set of two or more select statements. ActiveRecord does not handle UNION functionality, but there are gems to extend the functionality to include this, such as https://github.com/tsmango/union
And example of writing this using the Union gem would look like this:
Project.union([{:conditions => ['name like ?', "%#{name}%"]}, {:conditions => ['client like ?', "%#{client}%"]}])
This would generate the following SQL:
SELECT "projects".* FROM "projects" WHERE "projects".name LIKE '%fooproject%'
UNION
SELECT "projects".* FROM "projects" WHERE "projects".client_id LIKE '%misterx'
More information on SQL UNIONs can be found here: http://www.w3schools.com/sql/sql_union.asp
Using OR with queries in a scope
You have Squeel
to your rescue. More details here.
Using that, you could define something like:
class Teacher
...
scope :active_teachers, joins{subjects}.where {(active == true) | (subjects.active == true)}
...
end
Rails 3 merging scopes with joins
Apparently, at this time you can only merge simple constructs that don't involve joins. Here is a possible workaround if you modify your models to look like this:
class SolarSystem < ActiveRecord::Base
has_many :planets
has_many :planet_types, :through => :planets
scope :has_earthlike_planet, joins(:planet_types).merge(PlanetType.like_earth)
end
class Planet < ActiveRecord::Base
belongs_to :solar_system
belongs_to :planet_type
scope :like_earth, joins(:planet_type).merge(PlanetType.like_earth)
end
class PlanetType < ActiveRecord::Base
has_many :planets
attr_accessible :gravity, :life
scope :like_earth, where(:life => true, :gravity => 9.8)
end
** UPDATE **
For the record, a bug was filed about this behavior - hopefully will be fixed soon...
Rails 3: How to merge queries or scopes for complex query?
Try using scopes:
class Event < AR::Base
scope :active, lambda { |date| where("start_date < ? AND end_date > ?", date) }
scope :future, lambda { |date| where("end_date < ?", date }
...
end
# Console
> @active_events = Event.active(Date.today)
> @future_events = Event.future(Date.today)
See http://guides.rubyonrails.org/active_record_querying.html
Rails 5 joining activerecord scopes with OR
A scope represents a narrowing of a database query, such as where(color: :red).select('shirts.*').includes(:washing_instructions) (https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope)
Joining scopes using OR condition is not possible. A scope narrows resultset with some filter. Chaining scopes together is equivalent to using scope filters at the same time, ie SQL's AND condition.
For your case when 2 scopes need to be joined with OR you could merge their resultsets:
Vehicle.unassigned.to_a | Vehicle.driverless.to_a
but it's better to write another scope, as you already did.
Related Topics
How to Make a Post Request with Open-Uri
Testing If a Hash Has Any of a Number of Keys
Before/After Suite When Using Ruby Minitest
Ruby Timeouts and System Commands
Rails 3 Install Error: "Invalid Value for @Cert_Chain"
Safe Navigation Operator (&.) for Nil
In Ruby, Can You Perform String Interpolation on Data Read from a File
Difference Between Integer(Value) and Value.To_I
Missing Host to Link To! Please Provide the :Host Parameter, for Rails 4
Ruby: Split String at Character, Counting from the Right Side