To perform a range query we follow something akin to the syntax below -:
oms[:order_items].where(:internal_sla => 3..5) results in this query
=> #<Sequel::Mysql2::Dataset: "SELECT * FROM `order_items` WHERE ((`internal_sla` >= 3) AND (`internal_sla` <= 5))">
But how can I change the active record query to give me something like this => select internal_sla from order_items where (internal_sla<=3 and internal_sla>=0) OR (internal_sla<=15 and internal_sla>=10)
.where("(internal_sla >= ? AND internal_sla <= ? OR
internal_sla >= ? AND internal_sla <= ? )", 0, 3, 10, 15).pluck(:internal_sla)
UPDATE after comment:
If internal_sla is integer, you can:
.where(:internal_sla => (0..3).to_a + (10..15).to_a).pluck(:internal_sla)
edit: fixed typo
This works
oms[:order_items].where(:internal_sla => [0..3, 10..15])
Related
I need to get the following output in rails how do i do it?
SELECT "booking_rooms".* FROM "booking_rooms" INNER JOIN "bookings" ON "bookings"."id" = "booking_rooms"."booking_id" WHERE "booking_rooms"."room_id" IN (22, 27, 21) AND ("bookings"."start_date" <= '2016-10-16' AND "bookings"."end_date" > '2016-10-12')
I tried:
BookingRoom.joins(:booking).where(room_id: self.booking_rooms.map(&:room_id), bookings: { start_date:self.end_date,end_date:self.start_date})
For using conditions in a where statement please refer to the "Conditions" documentation.
Try this:
BookingRoom.joins(:booking)
.where(room_id:self.booking_rooms.map(&:room_id)
.where(bookings: { "start_date <= :start_date AND end_date > :end_date" },start_date: params[:start_date], end_date: params[:end_date])
Using ActiveRecord chaining and Array-Conditions you can do something similar to:
BookingRoom.joins(:booking).
where(room_id: self.booking_rooms.map(&:room_id)).
where(bookings: { "start_date <= ? AND end_date > ?" }, self.start_date, self.end_date)
Initially when I was trying to build a histogram of all Items that have an Order start between a given set of dates based on exactly what the item was (:name_id) and the frequency of that :name_id, I was using the following code:
dates = ["May 27, 2016", "May 30, 2016"]
items = Item.joins(:order).where("orders.start >= ?", dates.first).where("orders.start <= ?", dates.last)
histogram = {}
items.pluck(:name_id).uniq.each do |name_id|
histogram[name_id] = items.where(name_id:name_id).count
end
This code worked FINE.
Now, however, I'm trying to build a histogram that's more expansive. I still want to capture frequency of :name_id over a period of time, but now I want to bound that time by Order start and end. I'm having trouble however, combining the ActiveRecord Relations that follow the queries. Specifically, if my queries are as follows:
items_a = Item.joins(:order).where("orders.start >= ?", dates.first).where("orders.start <= ?", dates.last)
items_b = Item.joins(:order).where("orders.end >= ?", dates.first).where("orders.end <= ?", dates.last)
How do I join the 2 queries so that my code below that acts on query objects still works?
items.pluck(:name_id).each do |name_id|
histogram[name_id] = items.where(name_id:name_id).count
end
What I've tried:
+, but of course that doesn't work because it turns the result into an Array where methods like pluck don't work:
(items_a + items_b).pluck(:name_id)
=> error
merge, this is what all the SO answers seem to say... but it doesn't work for me because, as the docs say, merge figures out the intersection, so my result is like this:
items_a.count
=> 100
items_b.count
=> 30
items_a.merge(items_b)
=> 15
FYI currently, I've monkey-patched this with the below, but it's not very ideal. Thanks for the help!
name_ids = (items_a.pluck(:name_id) + items_b.pluck(:name_id)).uniq
name_ids.each do |name_id|
# from each query object, return the ids of the item objects that meet the name_id criterion
item_object_ids = items_a.where(name_id:name_id).pluck(:id) + items_b.where(name_id:name_id).pluck(:id) + items_c.where(name_id:name_id).pluck(:id)
# then check the item objects for duplicates and then count up. btw I realize that with the uniq here I'm SOMEWHAT doing an intersection of the objects, but it's nowhere near as severe... the above example where merge yielded a count of 15 is not that off from the truth, when the count should be maybe -5 from the addition of the 2 queries
histogram[name_id] = item_object_ids.uniq.count
end
You can combine your two queries into one:
items = Item.joins(:order).where(
"(orders.start >= ? AND orders.start <= ?) OR (orders.end >= ? AND orders.end <= ?)",
dates.first, dates.last, dates.first, dates.last
)
This might be a little more readable:
items = Item.joins(:order).where(
"(orders.start >= :first AND orders.start <= :last) OR (orders.end >= :first AND orders.end <= :last)",
{ first: dates.first, last: dates.last }
)
Rails 5 will support an or method that might make this a little nicer:
items_a = Item.joins(:order).where(
"orders.start >= :first AND orders.start <= :last",
{ first: dates.first, last: dates.last }
).or(
"orders.end >= :first AND orders.end <= :last",
{ first: dates.first, last: dates.last }
)
Or maybe not any nicer in this case
Maybe this will be a bit cleaner:
date_range = "May 27, 2016".to_date.."May 30, 2016".to_date
items = Item.joins(:order).where('orders.start' => date_range).or('orders.end' => date_range)
I have an array of conditions i'm passing to where(), with the conditions being added one at a time such as
conditions[:key] = values[:key]
...
search = ModelName.where(conditions)
which works fine for all those that i want to compare with '=', however I want to add a '<=' condition to the array instead of '=' such as
conditions[:key <=] = values[:key]
which of course won't work. Is there a way to make this work so it i can combine '=' clauses with '<=' clauses in the same condition array?
One way of doing it:
You could use <= in a where clause like this:
User.where('`users`.`age` <= ?', 20)
This will generate the following SQL:
SELECT `users`.* FROM `users` WHERE (`users`.`age` <= 20)
Update_1:
For multiple conditions, you could do this:
User.where('`users`.`age` <= ?', 20).where('`users`.`name` = ?', 'Rakib')
Update_2:
Here is another way for multiple conditions in where clause:
User.where('(id >= ?) AND (name= ?)', 1, 'Rakib')
You can add any amount of AND OR conditions like this in your ActiveRecord where clause. I just showed with 2 to keep it simple.
See Ruby on Rails Official Documentation for Array Conditions for more information.
Update_3:
Another slight variation of how to use Array Conditions in where clause:
conditions_array = ["(id >= ?) AND (name = ?)", 1, "Rakib"]
User.where(conditions_array)
I think, this one will fit your exact requirement.
You could use arel.
conditions = {x: [:eq, 1], y: [:gt, 2]}
model_names = ModelName.where(nil)
conditions.each do |field, options|
condition = ModelName.arel_table[field].send(*options)
model_names = model_names.where(condition)
end
model_names.to_sql --> 'SELECT * FROM model_names WHERE x = 1 and y > 2'
controller code that doesn't work:
#mailings = Mailing.find(:all, :conditions => ["created_at.month = ?", m])
Error msg:
Mysql::Error: Unknown column 'created_at.month' in 'where clause': SELECT * FROM `mailings` WHERE (created_at.month = 10)
thanks!
The :conditions argument of find is interpolated directly into the resulting SQL. Thus, you can use SQL commands.
You could try the following if you're using MySQL:
#mailings = Mailing.find(:all, :conditions => ["MONTH(created_at) = ?", m])
Or this, for SQLite:
#mailings = Mailing.find(:all, :conditions => ["strftime('%m', created_at) = ?", m])
Try:
#mailings = Mailing.find(:all, :conditions => ["MONTH(created_at) = ?", m])
Assuming you're using MySQL you can find more in the Docs: MySQL Date and Time Functions
I usually like to build a conditions hash like this below:
conditions = {}
conditions[:color] = "black"
conditions[:doors] = 4
conditions[:type] = "sedan"
Cars.find(:all, :conditions=>conditions)
But how would I add a date range into this for something like:
year >= '2011-01-01' and year < '2011-02-01'
I am assuming you are using Rails 2.3.x and year is a date column.
conditions = {}
conditions[:color] = "black"
conditions[:doors] = 4
conditions[:type] = "sedan"
# create a date range
conditions[:year] = (Date.parse("2011-01-01")...Date.parse("2011-02-01"))
Car.all(:conditions => conditions)
If you want to do even more complex queries in 2.3.x use the AR Extensions gem.
Read this article for more details.
If you're on Rails 3, why not use AREL?
Cars.where(:color => "black").
where(:doors => 4).
where(:type => "sedan").
where("year >= '2011-01-01'").
where("year < '2011-02-01'")
Btw, don't use :type as a field name. Rails uses this for STI.
On Rails 2.3, I'd just build up conditions as a String instead.
You can build a up query through relations. The query will not be executed until it needs to be evaluated. This is nice for searches where some parameters are optional.
#cars = Cars.where(:color => "black")
#cars = #cars.where(:doors => 4)
#cars = #cars.where("year >= '2011-01-01'")
#cars = #cars.where("year <= '2011-02-01'")
Or you could just merge all that together into one:
Cars.where(["color=? AND doors=? AND year >= ? AND year <= ?", "black", 4, "2011-01-01", "2011-02-01"]
UPDATE:
For Rails < 3
#cars = Cars.scoped(:conditions => {:color => "black"})
#cars = #cars.scoped(:conditions => {:doors => 4})
#cars = #cars.scoped(:conditions => "year >= '2011-01-01'")
#cars = #cars.scoped(:conditions => "year <= '2011-02-01'")
OR
Cars.all(:conditions => ["color=? AND doors=? AND year >= ? AND year <= ?", "black", 4, "2011-01-01", "2011-02-01"]