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
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
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
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.
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)
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