Apply where condition if it exists in rails - ruby-on-rails

is there a way in rails activerecord to apply the where condition based on a condition ?
For example:
Say I want to apply the filter if it is present in the input parameters.
Assume #name is a mandatory field and #salary is an optional field.
#name = "test_name"
#salary = 2000
I want to do something like below:
Employee.where(name: #name)
.where(salary: #salary) if #salary.present?
I know to make it in multiple db calls, but I'm looking for an option to make this happen in a single db call.

You can assign the result of the 1st where to a variable and invoke the 2nd where conditionally:
#employees = Employee.where(name: #name)
#employees = #employees.where(salary: #salary) if #salary.present?

You can query only by parameters that are present:
args = { name: #name, salary: #salary.presence }
Employee.where(args.compact)

You can just add all possible arguments into one hash and remove the ones that are nil with Hash#compact:
Employee.where({ name: #name, salary: #salary }.compact)

Related

prompt option not working with select_tag

If there is value in marital_status then prompt should not be displayed but in my case it's displaying. My code is mentioned below. Please help.
= select_tag( 'request[marital_status]',
options_for_select(marital_status_options,
#employee.marital_status.try(:upcase)), prompt: "Select Marital Status", id: 'employee_marital_status', disabled: #request.submitted?)
In employee_helper.rb
def marital_status_options
Employee::MaritalStatus::ALL.zip(Employee::MaritalStatus::ALL)
end
In employee model
module MaritalStatus
MARRIED = 'MARRIED'
SINGLE = 'SINGLE'
DIVORCED = 'DIVORCED'
ALL = [MARRIED, SINGLE, DIVORCED]
end
You're very close. The problem here likely stems from your marital_status_options method: this will simply return DIVORCED as it evaluates to the last line due to your assignment.
Therefore, you might find the value is selected if your instance contains 'DIVORCED', though not either of the other values; your instance's value needs to match one of these for it to be selected instead of the prompt.
You likely want to change this:
def marital_status_options
MARRIED = 'MARRIED' # this will be evaluated first
SINGLE = 'SINGLE' # then this
DIVORCED = 'DIVORCED' # finally, this will be evaluated and returned as 'DIVORCED'
end
To an array, either:
def marital_status_options
['MARRIED', 'SINGLE', 'DIVORCED']
end
Or, to present the options as lowercase but keep uppercase values in the db:
def marital_status_options
[['Married', 'MARRIED'], ['Single', 'SINGLE'], ['Divorced', 'DIVORCED']]
end
Take a look at the docs on options_for_select and you'll see who they can be setup.
Further down the line, you might want to consider switching to enums - these are very handy for managing selections such as these, and auto generate methods such as Employee.married, employee.divorced?, and so forth.
As someone else has mentioned, it's best practice to store data such as this in the relevant model, though I'd argue these should be stored as a constant as they won't be changing. So one of the following:
# employee.rb
MARITAL_STATUSES = ['MARRIED', 'SINGLE', 'DIVORCED'].freeze
# or
MARITAL_STATUSES = [['Married', 'MARRIED'], ['Single', 'SINGLE'], ['Divorced', 'DIVORCED']].freeze
= select_tag('request[marital_status]',
options_for_select(Employee::MARITAL_STATUSES,
#employee.marital_status.try(:upcase)),
prompt: "Select Marital Status",
id: 'employee_marital_status',
disabled: #request.submitted?)
Hope that helps - let me know if you've any questions or need anything else.
The format and usage is correct. Kindly verify if #employee.marital_status.try(:upcase) exactly matches one of the marital_status_options provided here.
That looks like a probable case for such a behaviour.
Also, the first argument expected in select_tag needs to in an appropriate format, in this case, an array of strings.
Hence, your method marital_status_options should return an array of options to be used for dropdown.
def marital_status_options
['MARRIED', 'SINGLE', 'DIVORCED']
end
= select_tag "request[marital_status]", options_for_select(Employee.marital_status_options,
#employee.marital_status.try(:upcase)), :include_blank => '--Select Marital Status--', id: id: 'employee_marital_status', disabled: #request.submitted?
It's a good practice to define marital_status_options(Business logic) inside model: -
Assuming that it's Employee model
def self.marital_status_options
[
["MARRIED","MARRIED"],
["SINGLE","SINGLE"],
["DIVORCED", "DIVORCED"]
]
end
Reason that it's not selecting your default marital_status is because if #employee.marital_status.try(:upcase) will not match any of the marital_status_options, it will show your prompt option, so check it carefully that if #employee.marital_status.try(:upcase) matches any of the given options of select tag's option.

Multiple word search in one field

I have search functionality with this code in the 'search.rb' file:
votsphonebooks = votsphonebooks.where("address like ?", "%#{address}%") if address.present?
There are multiple fields, this is just one of them.
How can I successfully change this line into something like a map to include multiple words.
Eg. If they type in '123 Fake St' - it will look for exactly that, but I want it to search for '123', 'Fake', 'St'.
First thing you should do is split the address by spaces:
addresses = params[:address].split(" ")
Then what you need is a OR query, you could do it by using ARel.
t = VotsPhoneBook.arel_table # The class name is my guess
arel_query = addresses.reduce(nil) do |q, address|
q.nil? ? t[:address].matches("%#{address}%") : q.or(t[:address].matches("%#{address}%"))
end
results = Post.where(arel_query)
Try using REGEXP instead of LIKE:
address_arr = address.split(" ")
votsphonebooks = votsphonebooks.where('address REGEXP ?',address_arr.join('|')) unless address_arr.blank?

Chaining active record queries together

I have a search form which contains parameters such as city,building_type,min_price,max_price.
What is the best way to chain all those queries together. If the user doesn't set a parameter, I want it to just return all records.
Something like
city=params[:city] || "all"
building_type=params[:building_type] || "all"
min_price=params[:min_price] || "all"
#results= Property.where(city: city,building_type: building_type,min_price: min_price)
Is it possible to do something like this? Seeing that there is no "all" keyword.
You can chain active record queries by assigning the results of all the queries you want to do in a single variable
properties = Property.all
properties = properties.where(city: params[:city]) if params[:city].present?
properties = properties.where(building_type: params[:building_type]) if params[:building_type].present?
Moreover, if the parameter keys are the same as the columns in your database, you can just place all of them in an array and loop through it like
properties = Property.all
%i[city building_type min_price].each do |column_name|
next if params[column_name].blank?
properties = properties.where(column_name => params[column_name])
end
NOTE
The first line, properties = Property.all assumes that it returns an ActiveRecord::Relation object which is the default in Rails 4 (not sure). If you are using Rails 3, just use Property.

Init query (.where / .order) with empty query

I'm trying to do something like:
if filter_1
#field = #field.where()
else
#field = #field.where()
end
if filter_2
#field = #field.order()
end
etc.
But how do I init #field with an empty query? I tried #field = Field.all but that gives an array so not allowing chaining.
Try scopedon Model class e.g.
#fields = Field.scoped
#fields = #fields.where("your conditions") if filter_1
#fields = #fiels.order("your conditions") if filter_2
The first time you are initializing the #field instance variable, Please try referring to the class Field, i.e.
Filter1: #field = Field.where(...)
Afterwards if you need to keep adding further filters you can refer to your variable field as many times as you want to.
Filter2 onward: #field = #field.where(...)
As Filter1 would return an active Record relation, you can nest more condition clauses onto it. Also do not worry about performance issues as the SQL will only be generated and processed once it is actually needed.(lazy loading)
If you to #field.to_sql at the end of your filters, you'll be able to see that all of your where clauses have conveniently been nested together into one SQL statement.
Also, I'd recommend you to read Active Record Query Interface
EDIT
Create a method get_field. And use that to add filter results.
def get_field(field)
#field.is_a?(ActiveRecord::Relation) ? Field : field
end
get_field(#field).where(....)
get_field(#field).where(....)

Rails - Conditional Query, with ActiveRecord?

Given a query like:
current_user.conversations.where("params[:projectid] = ?", projectid).limit(10).find(:all)
params[:projectid] is being sent from jQuery ajax. Sometimes that is an integer and the above works fine. But if the use selects "All Projects, that's a value of '' which rails turns into 0. which yields an invalid query
How with rails do you say search params[:projectid] = ? if defined?
Thanks
I think you may have mistyped the query a bit. "params[:projectid] = ?" shouldn't be a valid query condition under any circumstances.
In any case, you could do some sort of conditional statement:
if params[:project_id].blank?
#conversations = current_user.conversations.limit(10)
else
#conversations = current_user.conversations.where("project_id = ?", params[:project_id]).limit(10)
end
Although, I'd probably prefer something like this:
#conversations = current_user.conversations.limit(10)
#converstaions.where("project_id = ?", params[:project_id]) unless params[:project_id].blank?
Sidenotes:
You don't have to use .find(:all). Rails will automatically execute the query when the resultset is required (such as when you do #conversations.each).
Wherever possible, try to adhere to Rails' snakecasing naming scheme (eg. project_id as opposed to projectid). You'll save yourself and collaborators a lot of headaches in the long run.
Thanks but if the where query has lets say 3 params, project_id, project_status, ... for example, then the unless idea won't work. I'm shocked that Rails doesn't have a better way to handle conditional query params
EDIT: If you have multiple params that could be a part of the query, consider the fact that where takes a hash as its argument. With that, you can easily build a parameter hash dynamically, and pass it to where. Something like this, maybe:
conditions = [:project_id, :project_status, :something_else].inject({}) do |hsh, field|
hsh[field] = params[field] unless params[field].blank?
hsh
end
#conversations = current_user.conversations.where(conditions).limit(10)
In the above case, you'd loop over all fields in the array, and add each one of them to the resulting hash unless it's blank. Then, you pass the hash to the where function, and everything's fine and dandy.
I didn't understand why you put:
where("params[:projectid] = ?", projectid)
if you receive params[:project] from the ajax request, the query string shouldn't be:
where("projectid = ?", params[:projectid])
intead?
And if you are receiving an empty string ('') as the parameter you can always test for:
unless params[:projectid].blank?
I don't think i undestood your question, but i hope this helps.

Resources