Rails search through `has_many :through` text fields - mysql

Let say I have a two models with association has_many :through between them.
class Category < ActiveRecord::Base
has_many :category_recipes
has_many :categories, through: :category_recipes
validates :name, presence: true
class Recipe < ActiveRecord::Base
has_many :category_recipes
has_many :categories, through: :category_recipes
validates :title, presence: true
I want to create search functionality using ActiveRecord for mySQL database, which allow users to implement text search on Recipe title and Category name.
Now I have just:
#recipes = Recipe.where('title LIKE ?', "%#{params[:query]}%")
How can I modify this query to search through both title of recipe and it's category names?

Recipe.includes(:categories).where('recipes.title LIKE :query or categories.name like :query', query: "%#{params[:query]}%").references(:categories)
See: Specifying Conditions on Eager Loaded Associations

Hey try in this way
#recipes = Recipe.includes(:categories).where('title LIKE ? or categories.name LIKE ?', "%#{params[:query]}%","%#{params[:query]}%")

You need to left join to the associated table and query the name on either table with OR:
Recipe.joins("LEFT OUTER JOIN category_recipes ON category_recipes.recipe_id = recipes.id")
.joins("LEFT OUTER JOIN categories ON categories.id = category_recipes.id")
.where("recipes.title LIKE :q OR categories.name LIKE :q", q: "%#{params[:query]}%").uniq

Related

Rails query user first name on join model

this situation that find the user on a join model is seems tricky than usual
class ConversationParticipant < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
attr_accessible :user_id
end
class User < ActiveRecord::Base
has_many :conversation_participants
end
I'm trying to find the conversation_participant with the fisrt_name with this query
user = User.where('conversation_participant like ? or first_name like ?', query, query)
but the query did not return the user_participant and the user first_name either.
someone can spare a hint please!
You have to include the % for the sql LIKE operator
query = params[:query] # or something ...
User.where('conversation_participant LIKE :query OR first_name LIKE :query', query: "%#{query}%")

Rails 5 - Activerecord association query

I have two models associated with each other as follows.
class Comment < ApplicationRecord
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments
end
Following record query
comments_list = Comment.where(:post_id => post_id, :is_delete => false).joins(:user).select('comments.*,users.*')
Generates the following mysql query in logger
SELECT comments.*,users.* FROM `comments` INNER JOIN `users` ON `users`.`id` = `comments`.`user_id` WHERE `comments`.`post_id` = '81' AND `comments`.`is_delete` = 0.
This seems generating very ligitimate query, but comments_list object contain columns only from comments table.
Thanks
It depends on what you want to do, if you want to display the username next to the comment, Mert B.'s answer is fine, all you have to do is include(:user) and the users from the comment list will be fetched along when you do something like this:
comments_list = Comment.where(:post_id => post_id, :is_delete => false).joins(:user).select('comments.*,users.*')
comments_list.each do |comment|
puts "#{comment.text} by #{comment.user.name}"
end
Or maybe if you want only users who have at least one comment, you can always select users from the user_ids on the comments table:
User.where(id: Comment.select(:user_id))

Write an ActiveRecord association statement for Inner Join query

There are three table namely attendances,students and subjects for which I am unsuccessfully trying to do build an inner join query using active record.
Desired sql query
The raw sql statement that fetches correct data is
SELECT * FROM attendances AS a
INNER JOIN subjects AS b ON a.subject_id = b.id
WHERE b.organization LIKE '%city%'
AND a.started_at BETWEEN '2016-07-11 02:59:00' AND '2016-07-11 09:00:00'
The above query fetches exactly the correct 30 rows. However the problem arises when I try to perform the same with active-record associations.
Active-record associations
ActiveRecord::Base.establish_connection ({ adapter: 'mysql2',host:'localhost',
username: 'testuser',password: '***',database: 'database'})
class Subject < ActiveRecord::Base
has_many :attendances
has_many :students, through: :attendances
end
class Student < ActiveRecord::Base
has_many :attendances
has_many :subjects, through: :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :subject
belongs_to :student
end
data = Attendance.
where(started_at: start_date..end_date).
joins(:subject).
where(subjects: {:organization => city}).
pluck(:subject_id,:name,:duration,:started_at,:student_id)
Using _to_sql_ and after removing the :pluck, the sql query generated by active record is
SELECT `attendances`.* FROM `attendances` INNER JOIN `subjects`
ON `subjects`.`id` = `attendances`.`subject_id`
WHERE (`attendances`.`started_at` BETWEEN '2016-07-11 02:59:00'
AND '2016-07-11 09:59:00')
AND `subjects`.`organization` = 'city'
Modifying the statement to the below one doesn't help either.
data = Attendance.joins(:subject).
where('started_at BETWEEN ? AND ?','2016-07-11 02:59:00','2016-07-11 09:59:00').
where(subjects: {:organization => 'city'}).
pluck(:subject_id,:name,:duration,:started_at,:student_id)
The sql query generated for the above statement after removing :pluck using _to_sql_ as suggested is
SELECT `attendances`.* FROM `attendances` INNER JOIN `subjects`
ON `subjects`.`id` = `attendances`.`subject_id`
WHERE (started_at BETWEEN '2016-07-11 02:59:00' AND '2016-07-11 09:59:00')
AND `subjects`.`organization` = 'city'
Both the above active-record statement results in additional irrelavent rows being fetched. I am unable generate the desired correct sql query as shown earlier.
Constructing a correct active record statement or associations would be really helpful.
environment:-
ruby 2.1.6
activerecord 4.2.4

Custom Search in Ruby on Rails

I'm trying to implement a custom search feature in my Rails app.
I have 3 tables User Company and CompanyUser
My relations declared are as follows
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :user
...
end
In Company.rb
has_many :company_users, dependent: :destroy
has_many :users, through: :company_users
In User.rb
has_many :company_users, dependent: :destroy
has_many :companies, through: :company_users
and in my view page I'm listing all companies using company_users table
Now i have a search field where I can type a name in it which filters this list of company users.
I'm calling a method from my CompanyUsers table to filter the search result like this
#company_users = CompanyUser.all
if params[:search_data].present?
#company_users = #company_users.filter_company_users(params[:search_data])
end
where params[:search_data] is passed to the same method when search data is entered in the search field.
this is the method that do the search filtering.
def self.filter_company_users(search_data)
where('company_id like (?)', "%#{search_data}%")
end
Right now I can get the result only if I type the correct id of CompanyUser table in that search field. What I'm trying to do is to search using fields from User table and Company Table. For eg email from User and name from Company.
class CompanyUser
def self.search(search_data)
search = self.joins(:company, :user)
search.where!('company_users.company_id LIKE :query OR users.email LIKE :query OR companies.name LIKE :query', query: "%#{search_data}%")
search
end
end
With the join statement you join the CompanyUser table with the company and the user, therefore you will be able to apply filters on that tables too.
The where condition reuses the same query string and applies the search in OR to all the columns you want to search for.
#company_users = CompanyUser.all
#copany_users.all(:conditions => ["company_id LIKE ? OR company.email like ? OR user.name LIKE ?","%#{params[:search_data]}%","%#{params[:search_data]}%", "%#{params[:search_data]}%" ])
or
def self.filter_company_users(search_data)
where("company_id LIKE ? OR company.email like ? OR user.name LIKE ?" ,"%#{search_data}%", "%#{search_data}%","%#{search_data}%")
end

Rails 2 - named_scope :include, :joins and selecting specific columns

I was wondering if it's possible to use :include in named_scope but to specify only specific columns to :include?
Currently I use the following:
class ProductOverwrite < ActiveRecord::Base
belongs_to :product
named_scope :with_name, :include => :product
def name
produt.name
end
end
But i'm wondering if I can select specific columns from the product table instead of selecting the entire set of columns which I obviously don't need.
This isn't something rails does out of the box.
You could 'piggy back' the attribute
named_scope :with_product_name, :joins => :product, :select => 'product_overwrites.*, products.name as piggy_backed_name'
def product_name
read_attribute(:piggy_backed_name) || product.name
end
If it is possible for a ProductOverwrite to have no product, then you'd need a left join rather than the default inner join.