Rails/AR find where habtm does not include - sql

I have a Rails app with Users, and each user HABTM Roles.
I want to select Users without a specific role. I have searchlogic at my disposal, and I'm lost. I've tried using a combination of conditions and joins and includes and what not, but I can't seem to nail it. This works:
User.find(:all, :conditions => ['role_id != ?', Role[:admin].id], :joins => :roles)
To find users that are not admins, but doesn't not find users with no roles (which I want to find as well).
What simple thing am I missing in my tired state?

Use a sub-query and the NOT IN operator
User.find(:all,:conditions => ["id NOT IN (select user_id from roles_users where role_id = ?)", Role[:admin].id)

How about this:
User.find :all, :conditions => [ 'roles.id is ? or roles.id != ?', nil, Role[:admin].id ], :include => :roles
This works for has_many :through, seems like it should be the same for HABTM.

I can do
User.all - User.find(:all, :conditions => ['role_id = ?', Role[:admin].id], :joins => :roles)
Which accomplishes what I want in two queries, which is probably fine for this project, but if I can get it to a single query it would be nice.

Related

Query an association with an alias

I have a users table and a tasks table with a model with the following association:
class Task < ActiveRecord::Base
belongs_to :owner, class_name: User
end
I'm trying to build a query that will search the task's description and its owner's name as such:
includes(:owner).where("LOWER(tasks.description) LIKE ? OR LOWER(owner.name) LIKE ?", "%#{q.downcase}%", "%#{q.downcase}%")
...but I don't know how to properly query the users table for the task owners. In place of owner.name, I've tried users.name, tasks.users.name, and probably a few other things, all to no avail. How can I do this?
Note: I do not want to add a gem for this. I'm looking for a solution that is native to rails.
EDIT:
The foreign key as it exists in my schema.rb
add_foreign_key "tasks", "users", :name => "tasks_owner_id_fk", :column => "owner_id", :dependent => :nullify
SECOND EDIT:
I also need a solution that will return an AREL. I can get this to work by returning an array of objects, but I need to add other query methods to the result, so it has to be AREL.
You need to include a foreign_key of owner_id if you want to call owner.name in your view or query.
Second, you should be able to still call user, but since it's a belongs_to, it would be user.name, not users.name
If you set up the foreign_key you can call owner.name like you did before and it will work.
This should help you set it up:
http://guides.rubyonrails.org/active_record_migrations.html#foreign-keys
After digging around all day, this post helped me out, and my solution is the following:
joins("LEFT OUTER JOIN users ON users.id = tasks.owner_id").where("LOWER(users.name) LIKE ? OR
LOWER(tasks.description) LIKE ?",
"%#{q.downcase}%", "%#{q.downcase}%")

Rails sort collection by association count

I'm working in Rails 4, and have two relevant models:
Account Model
has_many :agent_recalls, primary_key: "id", :foreign_key => "pickup_agent_id", class_name: "Booking"
Hence, queries like Account.find(10).agent_recalls would work.
What I want to do is sort the entire Account collection by this agent_recalls association.
Ideally it'd look something like (but obviously not):
#agents = Account.where(agent: true).order(:agent_recalls)
Question: What's the correct query to output an ordered list, by this agent_recall count?
Well to accomplish what you are looking for you have 2 options:
first, only a query, but it will implied a join, so there will be lost the Accounts that doesn't have any agent_recalls, so i will discard this option
second, i think this one is more appropriate for what you are trying to do
Account.find(:all, :conditions => { :agent => true }, :include => :agent_recalls).sort_by {|a| a. agent_recalls.size}
As you can see is a mix between a query and ruby, hope it helps :)

Making this ActiveRecord lookups more efficient

I want to get an array of all email addresses for users of certain service types.
Using a string of ActiveRecord has_many relations, I can get these like this:
affected_services = Service.where(service_type: 'black')
affected_accounts = affected_services.map {|s| s.account}
affected_emails = affected_accounts.map {|a| a.contact.email}
I know it would be a fairly simple SQL query, but I'd prefer to see if ActiveRecord can do it to keep database abstraction.
Is there a good ActiveRecord way to retrieve those results?
You could use :include to include children in the query.
Account.find(:all, :include => :contact, :conditions => {:service_id => Service.where(:service_type => 'black').map{|account| account.contact.email } })

Problems using an id from a model inside a custom sql query in Rails

I want to do a model class which associates to itself on Rails. Basically, a user has friends, which are also users. I typed the following inside a User model class:
has_many :friends,
:class_name => "User",
:foreign_key => :user_id,
:finder_sql => %{SELECT users.*
FROM
users INNER JOIN friends
ON (users.id = friends.user_id OR users.id = friends.friend_id)
WHERE users.id <> #{id}}
But the funny fact is that it seems that this finder_sql is called twice whenever I type User.first.friends on irb. Why?
Drop the :finder_sql and refer to this:
http://guides.rubyonrails.org/association_basics.html#self-joins
I just read this post:
http://railsblaster.wordpress.com/2007/08/27/has_many-finder_sql/
I should have used single quotes to embrace the finder_sql, instead of %{}

rails active record nuances and protecting against injection attacks

When I make a query...
is there any meaningful difference between using a find_by helper or not?
Are there any reasons I'm overlooking for opting for shorter lines of code when doing things like this?
Booking.find_all_by_user_id(1, :joins => :confirmation)
Booking.find(:all, :joins => :confirmation, :conditions => [ 'bookings.user_id = ?', 1] )
No, regarding injection attacks.
The find_by method should be safe. However the only killer mistake is to use user input directly inside your conditions param when using find method, like doing:
Booking.find(:all, :joins => :confirmation, :conditions => [ 'bookings.user_id = #{params[user_id]]}'] )
Of course the right one is the way you did it and find method will filter things up.
Booking.find(:all, :joins => :confirmation, :conditions => [ 'bookings.user_id = ?', params[user_id]] )
What you're looking for is in here:
http://guides.rubyonrails.org/security.html#sql-injection
AND
http://guides.rubyonrails.org/security.html#mass-assignment
Be sure to read both carefully.