Rails, finding object with multiple dates in has_many assocation - ruby-on-rails

I'm pretty new to Rails, and the problem I'm bumping into is described as followes:
First, there is a table with houses.
The houses have multiple dates associated with has_many :dates
Second there is the table with dates.
Dates have the association belongs_to :house
Now, in a search, a user can select a start and end-date.
And what I want to do, is search each house for it's dates, and check if these are between the start and end-date. If one or more of the dates is within the start/end date, it needs to be found.
These are my conditions to search a house for other criteria:
parameters.push( [ params[:house_category_id], "houses.house_category_id = ?" ] );
parameters.push( [ params[:province_id], "houses.province_id = ?" ] );
I've created a method to create valid conditions with these parameters.
Please tell me, if it's not clear enough, cause it's a little hard to explain.
I've tried searching for this, but it got me on all kind of other pages except the pages with an answer..
Thanks

Not sure if I understand your problem correctly, but according to what I understood, you can get the desired result using the following:
res = House.find(:all, :select => 'distinct houses.*', :joins => "INNER JOIN dates ON dates.house_id = houses.id", :conditions => "dates.date < '2011-01-12' AND dates.date > '2011-01-11'")
where '2011-01-12' & '2011-01-11' are the dates selected by the user.
Let me know if this is what you were looking for or post with more details.

Now another question pops in my mind, but I don't know if that is possible..
If you forget about the from-to for this situation, is there any possibility that a I can check this sort of associations like so:
A house has 2 dates attached.
A User selects 2 dates.
Now I only want to select the house if these 2 dates both correspond..
Like this:
#houses = House.find(:all, :select => 'distinct houses.*', :joins => "INNER JOIN dates ON dates.house_id = houses.id", :conditions => "dates.date = '2011-01-12' AND dates.date = '2011-01-11'")
I tried the code above and got an SQL like this:
SELECT distinct houses.* FROM `houses` INNER JOIN dates ON dates.house_id = houses.id WHERE (dates.date = '2011-01-15' AND dates.date = '2011-01-22')
But it doesn't seem to work, is this possible at all in a single query or single find( )?

Related

How to Average Multiple Columns in Rails

I have the following objects: Products, Ratings, and Users. I am trying to filter and select a number of Products that the User owns (through a has_many :through relationship with UserProducts) and average a certain column the Ratings table that matches their User ID and the correct Product ID.
So, my function is something along these lines:
def find_rating(criteria)
product = self.products.find(:all, :conditions => ["criteria = ?", criteria])
rating = self.ratings.where("product_id = ?", product).average(:overall)
end
I think that I'm going about this the wrong way, because I'm trying to find a product_id by passing an entire array of data consisting of multiple products. But, I think of using a more traditional loop and that seems convoluted. Can someone point me in the right direction for solving this problem? Thanks!
If product is a single entry, as it appears to be in your code, I would do this:
rating = self.products.find_by_criteria(criteria).ratings.average(:overall)
If it's an array of products, this method may help you: http://apidock.com/rails/ActiveRecord/Batches/ClassMethods/find_each

Is it possible to delete_all with inner join conditions?

I need to delete a lot of records at once and I need to do so based on a condition in another model that is related by a "belongs_to" relationship. I know I can loop through each checking for the condition, but this takes forever with my large record set because for each "belongs_to" it makes a separate query.
Here is an example. I have a "Product" model that "belongs_to" an "Artist" and lets say that artist has a property "is_disabled".
If I want to delete all products that belong to disabled artists, I would like to be able to do something like:
Product.delete_all(:joins => :artist, :conditions => ["artists.is_disabled = ?", true])
Is this possible? I have done this directly in SQL before, but not sure if it is possible to do through rails.
The problem is that delete_all discards all the join information (and rightly so). What you want to do is capture that as an inner select.
If you're using Rails 3 you can create a scope that will give you what you want:
class Product < ActiveRecord::Base
scope :with_disabled_artist, lambda {
where("product_id IN (#{select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})")
}
end
You query call then becomes
Product.with_disabled_artist.delete_all
You can also use the same query inline but that's not very elegant (or self-documenting):
Product.where("product_id IN (#{Product.select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})").delete_all
In Rails 4 (I tested on 4.2) you can almost do how OP originally wanted
Application.joins(:vacancy).where(vacancies: {status: 'draft'}).delete_all
will give
DELETE FROM `applications` WHERE `applications`.`id` IN (SELECT id FROM (SELECT `applications`.`id` FROM `applications` INNER JOIN `vacancies` ON `vacancies`.`id` = `applications`.`vacancy_id` WHERE `vacancies`.`status` = 'draft') __active_record_temp)
If you are using Rails 2 you can't do the above. An alternative is to use a joins clause in a find method and call delete on each item.
TellerLocationWidget.find(:all, :joins => [:widget, :teller_location],
:conditions => {:widgets => {:alt_id => params['alt_id']},
:retailer_locations => {:id => #teller_location.id}}).each do |loc|
loc.delete
end

Ruby on Rails: how do I sort with two columns using ActiveRecord?

I want to sort by two columns, one is a DateTime (updated_at), and the other is a Decimal (Price)
I would like to be able to sort first by updated_at, then, if multiple items occur on the same day, sort by Price.
In Rails 4 you can do something similar to:
Model.order(foo: :asc, bar: :desc)
foo and bar are columns in the db.
Assuming you're using MySQL,
Model.all(:order => 'DATE(updated_at), price')
Note the distinction from the other answers. The updated_at column will be a full timestamp, so if you want to sort based on the day it was updated, you need to use a function to get just the date part from the timestamp. In MySQL, that is DATE().
Thing.find(:all, :order => "updated_at desc, price asc")
will do the trick.
Update:
Thing.all.order("updated_at DESC, price ASC")
is the Rails 3 way to go. (Thanks #cpursley)
Active Record Query Interface lets you specify as many attributes as you want to order your query:
models = Model.order(:date, :hour, price: :desc)
or if you want to get more specific (thanks #zw963 ):
models = Model.order(price: :desc, date: :desc, price: :asc)
Bonus: After the first query, you can chain other queries:
models = models.where('date >= :date', date: Time.current.to_date)
Actually there are many ways to do it using Active Record. One that has not been mentioned above would be (in various formats, all valid):
Model.order(foo: :asc).order(:bar => :desc).order(:etc)
Maybe it's more verbose, but personally I find it easier to manage.
SQL gets produced in one step only:
SELECT "models".* FROM "models" ORDER BY "models"."etc" ASC, "models"."bar" DESC, "models"."foo" ASC
Thusly, for the original question:
Model.order(:updated_at).order(:price)
You need not declare data type, ActiveRecord does this smoothly, and so does your DB Engine
Model.all(:order => 'updated_at, price')
None of these worked for me!
After exactly 2 days of looking top and bottom over the internet, I found a solution!!
lets say you have many columns in the products table including: special_price and msrp. These are the two columns we are trying to sort with.
Okay, First in your Model
add this line:
named_scope :sorted_by_special_price_asc_msrp_asc, { :order => 'special_price asc,msrp asc' }
Second, in the Product Controller, add where you need to perform the search:
#search = Product.sorted_by_special_price_asc_msrp_asc.search(search_params)

Rails - Find results from two join tables

I have have 3 Tables of data and 2 Join Tables connecting everything. I'm trying to figure out a way to query the results based on the condition that the join table data is the same.
To explain, I have User, Interest, and Event Tables. These tables are linked through an HABTM relationship (which is fine for my needs since I dont need to store any other fields) and joined through two join tables. So i also have a UsersInterests table with (user_id, interest_id) and a EventsInterests table with (event_id, interest_id).
The problem comes when trying to query all the Events where the users interests match the events interests.
I thought it would look something like this...
#events= Event.find(:all, :conditions => [#user.interests = #event.interests])
but I get the error
"undefined method `interests' for nil:NilClass", Is there something wrong with my syntax or my logic?
You're problem is that either #user or #event is undefined. Even if you define them, before executing this statement, the conditions option supplied is invalid, [#user.interests = #event.interests].
This named scope on events should do the trick
class Event < ActiveRecord::Base
...
named_scope :shares_interest_with_user, lambda {|user|
{ :joins => "LEFT JOIN events_interests ei ON ei.event_id = events.id " +
"LEFT JOIN users_intersets ui ON ui.interest_id = ei.interest_id",
:conditions => ["ui.user_id = ?", user], :group_by => "events.id"
}
end
#events = Event.shares_interest_with_user(#user)
Given Event <-> Interest <-> User query all the Events where the users interests match the events interests (so the following will find all such Events that this event's interest are also interests of at least one user).
First try, the simplest thing that could work:
#events = []
Interest.all.each do |i|
i.events.each do |e|
#events << e if i.users.any?
end
end
#events.uniq!
Highly inefficient, very resource hungry and cpu intensive. Generates lots of sql queries. But gets the job done.
Second try should incorporate some complicated join, but the more I think about it the more I see how vague your problem is. Be more precise.
Not sure I completely follow what you are trying to do. If you have one user and you want all events that that user also has interest in then something like:
Event.find(:all, :include => [:events_interests], :conditions => ['events_interests.interest_id in (?)', #user.interests.collect(&:id)])
should probably work.

Find all objects with no associated has_many objects

In my online store, an order is ready to ship if it in the "authorized" state and doesn't already have any associated shipments. Right now I'm doing this:
class Order < ActiveRecord::Base
has_many :shipments, :dependent => :destroy
def self.ready_to_ship
unshipped_orders = Array.new
Order.all(:conditions => 'state = "authorized"', :include => :shipments).each do |o|
unshipped_orders << o if o.shipments.empty?
end
unshipped_orders
end
end
Is there a better way?
In Rails 3 using AREL
Order.includes('shipments').where(['orders.state = ?', 'authorized']).where('shipments.id IS NULL')
You can also query on the association using the normal find syntax:
Order.find(:all, :include => "shipments", :conditions => ["orders.state = ? AND shipments.id IS NULL", "authorized"])
One option is to put a shipment_count on Order, where it will be automatically updated with the number of shipments you attach to it. Then you just
Order.all(:conditions => [:state => "authorized", :shipment_count => 0])
Alternatively, you can get your hands dirty with some SQL:
Order.find_by_sql("SELECT * FROM
(SELECT orders.*, count(shipments) AS shipment_count FROM orders
LEFT JOIN shipments ON orders.id = shipments.order_id
WHERE orders.status = 'authorized' GROUP BY orders.id)
AS order WHERE shipment_count = 0")
Test that prior to using it, as SQL isn't exactly my bag, but I think it's close to right. I got it to work for similar arrangements of objects on my production DB, which is MySQL.
Note that if you don't have an index on orders.status I'd strongly advise it!
What the query does: the subquery grabs all the order counts for all orders which are in authorized status. The outer query filters that list down to only the ones which have shipment counts equal to zero.
There's probably another way you could do it, a little counterintuitively:
"SELECT DISTINCT orders.* FROM orders
LEFT JOIN shipments ON orders.id = shipments.order_id
WHERE orders.status = 'authorized' AND shipments.id IS NULL"
Grab all orders which are authorized and don't have an entry in the shipments table ;)
This is going to work just fine if you're using Rails 6.1 or newer:
Order.where(state: 'authorized').where.missing(:shipments)

Resources