find all :conditions id found in an array of values - ruby-on-rails

I have 2 models (player and team linked through the model lnkteamplayer)
Team has_many players through lnkteamplayer
Player has_many teams through lnkteamplayer
I need to retrieve all players not belonging to a specific team.
<% #players = Player.find(:all, :conditions => ["id != ?",#team.lnkteamplayers.player_id ]) %>
I am getting an error with above line of code. My question is how do i pass an array of values in the above condition.
Thanks for any suggestion provided.

You've got a couple of problems there:
1) the first part of conditions, "id != ?", is a fragment of sql, and in sql you do "not equals" as <> not !=. Eg "id <> ?"
2) To use an array, the sql syntax is id in (1,2,3) or id not in (1,2,3). In your conditions you can do this like :conditions => ["id not in (?)", array_of_ids]
So, you could get players not on a team like this:
#team = Team.find(params[:team_id])
#not_on_team = Player.find(:all, :conditions => ["id not in (?)", #team.player_ids])

Since you haven't provided an error message, I am kind of guessing here. However, I don't think != is a valid syntax in many SQL dialects. You are probably looking for something like NOT IN () instead.
Also, #team.lnkteamplayers.player_id probably doesn't work since the value returned from #team.lnkteamplayers likely doesn't have a player_id method; you might want the ids of the actual players instead.
That can be done using something like #team.lnkteamplayer_ids.
All in all, your line probably needs to look like
<% #players = Player.find(:all, :conditions => ["id NOT IN (?)", #team.lnkteamplayer_ids]) %>
but without more information we can't say for sure.

Related

Rails: Sorting Objects From Different Models

I've populated a hash with two different models. I then try to sort them like so:
#search_results = User.find(:all, :conditions => ['name LIKE ?', "%#{params[:query]}%"])
#search_results += Book.find(:all, :conditions => ['title LIKE ?', "%#{params[:query]}%"])
#search_results.sort! { |a,b| a.impressions_count <=> b.impressions_count }
This throws the following error:
comparison of User with Book failed
Both users and books have an integer-based impressions_count. Why can't I sort via this attribute? What other options do I have?
I faced a similar problem recently and ended up writing some custom sql because all other ways returned an array. Pretty sure its not a good idea to use the sort method since it will always be more efficient to sort in SQL than ruby, especially when the data set gets large
#combined_results = User.find_by_sql(["SELECT title, id, impressions_count, NULL as some_attribute_of_book
FROM user
WHERE title LIKE ?
UNION SELECT title, id, impressions_count, some_attribute_of_book FROM book
WHERE title LIKE ?
ORDER BY impressions_count", params[:query], params[:query]])
The above is completely untested code, more of an example than anything

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

Targeting every object in an array syntax

Newb question of the day:
I'm trying to select all the users with this condition, and then perform an action with each one :
User.find(:all).select { |u| u.organizations.count > 0} do |user|
Except, this isn't the right way to do this. Not entirely sure what the proper syntax is.
Any fellow rubyist offer a newb a hand?
To perform an action with each element of a collection use the each method, like this:
User.find(:all).select { |u| u.organizations.count > 0}.each do |user|
You'd probably be better folding the select into the query with:
User.find(:all, :conditions => "organization_id IS NOT NULL").each do |user|
This will only fetch the relevant results from the database so there should be less unnecessary data retrieved and thrown away.
EDIT:
As suggested in the comments, the following would be correct for a many-to-many relationship assuming a join model called memberships (where user has_many :organisations, :through => :membership)...
User.all(:joins => "inner join memberships on memberships.user_id = users.id")

Exists in nested collection cannot find without ID?

Hey guys another rails issue,
Currently have a collection that is line items for an invoicing system. I want to increment the count of the line items if I add in an item that already exists. At the moment I'm using an exists? query on the collection but it seems to return regardless of the key.
The foreign key I'm using is item_id, so I try to do invoice_items.exists?(:item_id => item.id)
This wasn't returning, so I changed it to invoice_items.find(:conditions => ["item_id == ?", item.id) and I get a return that I cannot search without invoiceItem ID.
Ideas?
conditions => ["item_id == ?", item.id
should be
conditions => ["item_id = ?", item.id]
So your query look like this
invoice_items.find(:all).conditions => ["item_id = ?", item.id]
you should just need to do either
invoice_items.all(:conditions => ["item_id == ?", item.id])
OR
invoice_items.first(:conditions => ["item_id == ?", item.id])
and you can use the syntax
invoice_items.all(:conditions => {:item_id => item.id})
if you are going to use the model.find command the first parameter needs to be :all, :first, :last or the primary key you are searching for. that is why I generally prefer to use Model.find only if I am searching for an id, otherwise I use Model.first, Model.last, Model.all. That way you know what you are going to get.

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.

Resources