Rails searching a belongs_to relationship - ruby-on-rails

In my application I have a Property and Customers model. Property has_one :customer and Customer belongs_to :property. Property has an address column of type string.
I am trying to allow users to search Customers by the address of the property it belongs to.
# customer.rb
class Customer < ActiveRecord::Base
belongs_to: property
def self.search(search, user)
if search
where('full_name LIKE ?', "%#{search}%").where(user: user)
else
where(user: user)
end
end
end
Doing this doesn't work:
def self.search(search, user)
if search
where('full_name LIKE ? OR property.address LIKE ?', "%#{search}%", "%#{search}%").where(user: user)
else
where(user: user)
end
end
What is the best way to accomplish this?

You need to use a "join."
def self.search(search, user)
if search
joins(:property).where('properties.address LIKE ?', "%#{search}%").where(user: user)
else
where(user: user)
end
end
In SQL terminology this is called an "inner join."
Here is the Rails Guide on joining tables.

Related

Caching association that was in where clause

Let me show an example:
I have 2 models:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
scope :created_in, ->(start_date, end_date) { where(created_at: start_date..end_date) }
end
What I want is to get users that created post during a specific period:
users = User.includes(:posts).joins(:posts).merge(Post.created_in(start_date, end_date))
Is it somehow possible to cache posts that are in the where clause? So after I do
users.first.posts
it will show me exactly those posts that match the condition without producing any additional queries.
No, I don't think this is possible. Depending on the context, what you can do is to do a lookup table which you memoize / cache. Something like
User.all.each do |user|
posts = posts_by_user_id[user.id]
end
def posts_by_user_id
#_posts_by_user_id ||= posts.group_by(&:user_id)
end
def posts
Post.created_in(start_date, end_date)
end

activerecord complex joins

I have a many to many relationship and I want to get all the users of all the cars of one user. What is the most efficient join for that?
Basically, I want to know how to do the following but with activerecord
complex SQL query, many to many
class User < ApplicationRecord
def people_who_use_my_car
self.cars.users
end
end
In your controller or view or wherever you want to use it do:
#user = User.first
#users = #user.people_who_use_my_car
A straightforward way would be
class User < ApplicationRecord
def people_who_use_my_car
self.cars.inject([]).do |users, car|
users += car.users
users
end
end
end
But this is not as efficient as using the database as #poet suggests,
class User < ApplicationRecord
def people_who_use_my_car
User.joins(:cars).where(cars: {user: self})
end
end

how to perform a search query of a foreign key field's attribute in rails?

I have two models Exam and Student.
exam.rb
class Exam < ApplicationRecord
belongs_to :year
belongs_to :student, class_name: "Student"
def self.search(search)
if search
self.where('student. LIKE ?', "%#{search}%")
else
self.all
end
end
end
student.rb
class Student < ApplicationRecord
belongs_to :year
has_many :exams
end
The student field in exam.rb refers to the Student object . However model Student has attribute name and I want to perform the search based on that attribute and find out the list of exams taken by that student . Is it possible to make such a query ? if so , what would be the correct way to implement it ?
Thanks
You can implement it using joins and where:
exams_list = Exams.joins(:student).where('students.name LIKE ?', "%#{search}%"))
Hope it helps.
if you want to search text irrespective or case(capitalisation) you can use ilike option for better searching:
exams_list = Exams.joins(:student).where('students.name ilike ?', "%#{search}%"))
Hope it helps.
You can try:
def self.search(search)
if search
self.joins(:student).where('students.name LIKE ?', "%#{search}%")
else
self.all
end
end
Hope it helps.

Is there a better way to find associations on a class level?

Here are the objects I am working with:
class Client < ActiveRecord::Base
has_many :servers
def self.active
where("updated_at > ?", 1.month.ago)
end
end
class Server
belongs_to :client
end
I would like to be able to get all the servers that belong to active clients like so:
Client.active.servers
The only way I can think to do this is:
def Client.servers
Server.where(id: all.collect(&:id))
end
There must be a more Rails-y to do this!
I believe I've found what you're looking for, and it's the Active Record method merge.
Server.joins(:client).merge(Client.active)
In testing, if you find a conflict with your updated_at column, be sure to disambiguate it in your active scope:
def self.active
where("clients.updated_at > ?", 1.month.ago)
end
You want to join to the client from server. Something like this should work:
Server.joins(:client).where('clients.updated_at > ?', 1.month.ago)

Where NOT IN () on a join?

I have the following scope in a Rails model.
class Suggestion < ActiveRecord::Base
has_many :favourites
def self.favoured_by(user)
joins(:favourites).where(favourites: { user_id: user.id })
end
end
That works perfectly. It will return all the suggestions which a particular user has favourited.
How can I retrieve all the suggestions which are either not favourited at all or which are favourited but not by this particular user?
def self.not_favoured_by(user)
# ...
end
My Favourite model looks like this:
class Favourite < ActiveRecord::Base
belongs_to :suggestion
belongs_to :user
end
favorited_suggestions_ids = joins(:favourites).where(favourites: { user_id: user.id }).map(&:id)
return scoped if favorited_suggestion_ids.empty?
where('id not in (?)',favorited_suggestions_ids)
How about this:
def self.not_favoured_by(user)
sql = <<-SQL
SELECT suggestions.* FROM suggestions
WHERE NOT EXISTS
(SELECT id FROM favourites WHERE user_id = #{user.id} AND favourites.suggestion_id = suggestions.id);
SQL
find_by_sql(sql)
end

Resources