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
Related
I need run a mysql query from the controller but I do not know how to connect to db.
this is my method:
def set_dcs
sql="select employees.nombre, employees.apellido_paterno, employees.apellido_materno from employees, activities, field_tests
where activities.field_test_id=field_tests.id and employees.id=activities.dcs_id and field_tests.id=id"
end
How do I get the result of the query?
How could I do the same query but with the ActiveRecord?
You can execute raw SQL through ActiveRecord::Base.connection but I rarely recommend it and certainly not in this case, but for edification purposes
def set_dcs
sql= <<SQL
select employees.nombre, employees.apellido_paterno, employees.apellido_materno
from employees, activities, field_tests
where activities.field_test_id=field_tests.id and employees.id=activities.dcs_id and field_tests.id=id
SQL
ActiveRecord::Base.connection.exec_query(sql)
end
I was not sure what the trailing id is in reference to and it will raise a SQL error due to it's ambiguity. I am going to assume it is a parameter and the primary search condition where as the rest are table joins.
That being said since you are using rails these can be true associations and joins resulting in far more readable controller code e.g. Model Definitions
class Employee < ActiveRecord::Base
has_many :activities, foreign_key: :dcs_id
has_many :field_tests, through: :activities
end
class FieldTest < ActiveRecord::Base
has_many :activities
end
class Activity < ActiveRecord::Base
belongs_to :employee, foreign_key: :dcs_id
belongs_to :field_test
end
Then the controller is simply
Employee.
select("employees.nombre, employees.apellido_paterno, employees.apellido_materno").
joins(:field_tests).where(field_tests: {id: SOME_ID})
the resulting SQL will be similar to
SELECT
employees.nombre,
employees.apellido_paterno,
employees.apellido_materno
FROM
employees
INNER JOIN activities ON employees.id = activities.dcs_id
INNER JOIN field_tests ON activities.field_test_id = field_tests.id
WHERE
field_tests.id = SOME_ID
And this will return a collection of Employee Objects rather than an ActiveRecord::Result (returned from ActiveRecord::Base.connection.exec_query) which is more similar to an Array than anything else.
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}%")
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))
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
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.