Dynamic conditions query in RoR - mysql

I'm trying to make query in RoR, but my conditions are coming from params and changing dynamically.
I've tried this:
#userlist = User.find(:all, conditions:{(name:params[:name] if params[:name] != nil), (lastname:params[:lastname] if params[:lastname] != nil)})
It's just my imagination, but shows what I mean.
thanx

If you're on Rails 3:
#userlist = User.scoped
#userlist = #userlist.where(name: params[:name]) if params[:name].present?
#userlist = #userlist.where(lastname: params[:lastname]) if params[:lastname].present?
etc..

you could do something like:
BlahController < ActiveController::Base
before_filter :find_params, :only => :index
def index
#user_list = User.where(#filtered_params).all
end
def find_params
#filtered_params = params[:name].present? ? "'name = ?', #{params[:name]}" : "'last_name = ?', #{params[:last_name]}"
end
end
So I assume you only have two conditions, a params[:name] or a params[:last_name]. If there are more, simply change the ternary to a if elsif statement.

Related

Rails 4.1, Ruby 2.1, Devise, Why 'syntax error, unexpected tConstant' in my .where() with multiple conditions?

I see from similar posts that it's usually a case of some closing character missing from a previous line, but I don't see it here. The Courses resource is nested inside Schools and I can print out both the user_id and the school.id.
The error appears to be in the Course.where clause with multiple conditions (user_id: current_user.id AND school_id: #school.id)
The one-condition where clause above it seems to work just fine.
courses_controller.rb
class CoursesController < ApplicationController
before_action :set_school
helper_method :calculated_grade_stub
# GET /courses
# GET /courses.json
def index
if #school.nil?
#courses = Course.where(user_id: current_user.id)
else
#courses = Course.where(user_id: current_user.id AND school_id: #school.id)
end
end
The operator is && or and, not AND.
However, that's not the root cause of the problem: it appears you are confused about the general syntax of Hash literals. The entries in Hash literals are separated by comma, not by an operator:
#courses = Course.where(user_id: current_user.id, school_id: #school.id)
Note also that if is an expression and thus returns a value, so you can easily refactor that method to
def index
#courses = if #school.nil?
Course.where(user_id: current_user.id)
else
Course.where(user_id: current_user.id, school_id: #school.id)
end
end
You can try this:
#courses = Course.where('user_id = ? AND school_id = ?',current_user.id , #school.id)

How do I extract ID's from this array without making an unnecessary DB call?

In my controller I have extracted all the files that are in trashcan like this:
#myfiles = Myfile.all.where("user_id = ? AND trashcan = ?", current_user.id, 1).order('created_at DESC')
How do I fetch all the ID's of the myfiles that have been collected?
Use pluck:
#myfiles = Myfile.all.where("user_id = ? AND trashcan = ?", current_user.id, 1).order('created_at DESC')
myfiles_ids = #myfiles.pluck(:id)

Rails custom query based on params

I have zero or many filter params being sent from a json request.
the params may contain:
params[:category_ids"]
params[:npo_ids"]
etc.
I am trying to retreive all Projects from my database with the selected ids. Here is what I have currently:
def index
if params[:category_ids].present? || params[:npo_ids].present?
#conditions = []
#ids = []
if params["category_ids"].present?
#conditions << '"category_id => ?"'
#ids << params["category_ids"].collect{|x| x.to_i}
end
if params["npo_ids"].present?
#conditions << '"npo_id => ?"'
#ids << params["npo_ids"].collect{|x| x.to_i}
end
#conditions = #ids.unshift(#conditions.join(" AND "))
#projects = Project.find(:all, :conditions => #conditions)
else ...
This really isn't working, but hopefully it gives you an idea of what I'm trying to do.
How do I filter down my activerecord query based on params that I'm unsure will be there.
Maybe I can do multiple queries and then join them... Or maybe I should put a filter_by_params method in the Model...?
What do you think is a good way to do this?
In rails 3 and above you build queries using ActiveRelation objects, no sql is executed until you try to access the results, i.e.
query = Project.where(is_active: true)
# no sql has been executed
query.each { |project| puts project.id }
# sql executed when the first item is accessed
The syntax you are using looks like rails 2 style; hopefully you are using 3 or above and if so you should be able to do something like
query = Project.order(:name)
query = query.where("category_id IN (?)", params[:category_ids]) if params[:category_ids].present?
query = query.where("npo_ids IN (?)", params[:npo_ids]) if params[:npo_ids].present?
#projects = query
I solved this. here's my code
def index
if params[:category_ids].present? || params[:npo_ids].present?
#conditions = {}
if params["category_ids"].present?
#conditions["categories"] = {:id => params["category_ids"].collect{|x| x.to_i}}
end
if params["npo_ids"].present?
#conditions["npo_id"] = params["npo_ids"].collect{|x| x.to_i}
end
#projects = Project.joins(:categories).where(#conditions)
else
basically it stored the .where conditions in #conditions, which looks something like this when there's both categories and npos:
{:categories => {:id => [1,2,3]}, :npo_id => [1,2,3]}
Then inserting this into
Project.joins(:categories).where(#conditions)
seems to work.
If you're filtering on a has_many relationship, you have to join. Then after joining, make sure to call the specific table you're referring to by doing something like this:
:categories => {:id => [1,2,3]}

rails 2.3 convert hash into mysql query

I'm trying to find out how rails converts a hash such as (This is an example please do not take this literally I threw something together to get the concept by I know this query is the same as User.find(1)):
{
:select => "users.*",
:conditions => "users.id = 1",
:order => "username"
}
Into:
SELECT users.* FROM users where users.id = 1 ORDER BY username
The closest thing I can find is ActiveRecord::Base#find_every
def find_every(options)
begin
case from = options[:from]
when Symbol
instantiate_collection(get(from, options[:params]))
when String
path = "#{from}#{query_string(options[:params])}"
instantiate_collection(format.decode(connection.get(path, headers).body) || [])
else
prefix_options, query_options = split_options(options[:params])
path = collection_path(prefix_options, query_options)
instantiate_collection( (format.decode(connection.get(path, headers).body) || []), prefix_options )
end
rescue ActiveResource::ResourceNotFound
# Swallowing ResourceNotFound exceptions and return nil - as per
# ActiveRecord.
nil
end
end
I'm unsure as to how to modify this to just return what the raw mysql statement would be.
So after a few hours of digging I came up with an answer although its not great.
class ActiveRecord::Base
def self._get_finder_options options
_get_construct_finder_sql(options)
end
private
def self._get_construct_finder_sql(options)
return (construct_finder_sql(options).inspect)
end
end
adding this as an extension gives you a publicly accessible method _get_finder_options which returns the raw sql statement.
In my case this is for a complex query to be wrapped as so
SELECT COUNT(*) as count FROM (INSERT_QUERY) as count_table
So that I could still use this with the will_paginate gem. This has only been tested in my current project so if you are trying to replicate please keep that in mind.

How should I write case statement depend of parameters key?

For example I have index action:
def index
if params[:query]
#pharmaceutics = Pharmaceutic.where("name LIKE ?", params[:query])
elsif params[:code]
#pharmaceutics = Pharmaceutic.where("barcode LIKE ?", params[:code])
else
#pharmaceutics = Pharmaceutic.all
end
end
And when I send two params: code and query I would like to filter my Pharmaceutics using both of them. I have MySQL database.
I would probably use scoped method, like this:
def index
scope = Pharmaceutic.scoped # Pharmaceutic.all if you use Rails 4
scope = scope.where('name LIKE ?', params[:query]) if params[:query].present?
scope = scope.where('barcode LIKE ?', params[:code]) if params[:code].present?
#pharmaceutics = scope
end
You can also write your custom scopes and replace where(...) with them to make the code clearer.