Multiple where clauses in rails searchkick - ruby-on-rails

I have a very basic question about searchkick. What if you want you join multiple where statements in searchkick query using if statements. Much like query-builder
#product = Product.all
unless request.end_date.nil?
#product = #product.search, where('created_at <= ?', request.end_date)
end
unless request.max_price.nil?
#product = #product.search, where('price <= ?', request.max_price)
end
#product
The above code works fine if request has either end date or max_price. If it has both, it throws an error. Is there a way to construct or concatenate the two where statements. I cannot do
Product.search '*', where('created_at <= ?', request.end_date), where('price <= ?', request.max_price)
because if statement is important.

You should check docs of Searchkick, it has Or filter: https://github.com/ankane/searchkick
where: {
expires_at: {gt: Time.now}, # lt, gte, lte also available
or: [
[{in_stock: true}, {backordered: true}]
]
}
For your case, you can deal with it as below:
conditions[:or] = [[]]
unless request.end_date.nil?
conditions[:or][0] += [{created_at: {lt: request.end_date}}]
end
unless request.max_price.nil?
conditions[:or][0] += [{price: {lt: request.max_price}}]
end
Product.search '*', where: conditions

Related

Using where search clause after select

I'm new to rails and I'm adding some updates to already existing app. I need to add filtering by search word and I'm trying to use where with ILIKE to do that but due to the original code is written. the original code uses Model.select to get the records and when i try to chain where with the ILIKE clause to that it still returns the whole array not filtered.
date_format = get_js_date_format_by_id(#account.date_format_id)
sort_format = "#{params[:sort_item] || "date"} #{params[:sort_order] || "asc"}"
#expenses = Expense.select("expenses.*, to_char(expenses.date, '#{date_format}') as formatted_date,
to_char(
CASE
WHEN expense_categories.is_calculated = true then expenses.amount * expense_categories.unit_cost
ELSE expenses.amount
END, 'FM999999990.00') as total,
CASE
WHEN expense_categories.is_calculated = true then expense_categories.unit_name
ELSE null
END as unit_name,
'' as images_list,
users.name as user_name, expense_categories.name as category_name, projects.name as project_name, clients.name as client_name")
.joins(:user, :project, :expense_category, :client)
if params[:query]
#expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
#expenses.where(conditions)
.order(sort_format)
if params[:page]
#expenses = #expenses.page(params[:page]).per(params[:per_page])
end
#expenses
I'm adding the filtering part starting at if params[:query] the rest is the original code. any help would be appreciated to pin pointing the problem.
thanks!
The problem is that you don't modify the current #expenses scope with:
if params[:query]
#expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
This line returns the new scope you want, but you are not saving it anywhere. Following code should work:
if params[:query]
#expenses = #expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
Note: same thing applies for the sorting under the params[:query] condition.

Searchkick: Searching multiple specific fields in the same query

i'm trying to make an advanced search in my rails app but i'm having some problems with empty params
class Product< ActiveRecord::Base
searchkick
when i fill the search and size fields everyting works, but if i leave size field blank nothing show up in the results...
probably i'm doing something stupid
i made it work with a bunch of IFs:
def index
if params[:search].present?
if params[:size].present?
#products = Product.search params[:search], where: {size: params[:size]}
else
#products = Product.search params[:search]
end
else
if params[:size].present?
#products = Product.search "*", where: {size: params[:size]}
else
#products = Product.search "*"
end
end
but probably thats not the best approuch, having in mind that i will search in at least 5 other fields...
Search, Size, Brand, Color, Store.state, Price, Rating etc...
sorry for my english, i hope you guys understand my question and get able to help me..
My be smth:
...
search_condition = params[:search] || '*'
where_conditions = params.slice(:size, :brand, :color, ...)
#products = if where_conditions.any?
Product.search search_condition, where: where_conditions
else
Product.search search_condition
end
Found a solution where
Setting up Facets in Elasticsearch with Searchkick gem in Rails 4.1
query = params[:query].presence || "*"
conditions = {}
conditions[:state] = params[:state] if params[:state].present?
conditions[:city] = params[:city] if params[:city].present?
movies = Movie.search query, where: conditions

Rails simple filtering using LIKE

I'm looking for a simple way to build up a filter query using given column/value hash. All of the values should be used in LIKE queries. I can build up the query and the parameters in arrays and pass it to where, like this:
def self.filter(filters=nil)
if filters.nil? || filters.empty?
return all
end
query = []
value_array = []
filters.each do |key, value|
query << "#{key} LIKE ?"
value_array << "%#{value}%"
end
where([query.join(' AND ')] + value_array)
end
But I was wondering if there is a better way of doing this either built into Rails (using version 4) or if there is a super simple gem that can easily accept a has and turn into a LIKE filter?
a nice way to play nice with hashes in queries is to make use of the Hash#slice method and scopes:
...
filtering_params(params).each do |key, value|
#products = #products.public_send(key, value) if value.present?
end
def filtering_params(params)
params.slice(:status, :location, :starts_with)
end
class Product < ActiveRecord::Base
scope :status, -> (status) { where status: status }
scope :location, -> (location_id) { where location_id: location_id }
scope :starts_with, -> (name) { where("name like ?", "#{name}%")}
end
Taken from here. After all you might need to restrict you logic to some specific queries in your DSL.

Rails 3: Search method returns all models instead of specified

What I'm trying to do: I have a model "Recipe" in which I defined a method "search" that takes an array of strings from checkboxes (I call them tags), and a single string. The idea is to search the db for recipes that has anything in it's 'name' or 'instructions' that contains the string, AND also has any of the tags matching it's 'tags' property.
Problem: The search method return all the recipes in my db, and doesn't seem to work at all at finding by the specific parameters.
The action method in the controller:
def index
#recipes = Recipe.search(params[:search], params[:tag])
if !#recipes
#recipes = Recipe.all
end
respond_to do |format|
format.html
format.json { render json: #recipe }
end
end
The search method in my model:
def self.search(search, tags)
conditions = ""
search.present? do
# Condition 1: recipe.name OR instruction same as search?
conditions = "name LIKE ? OR instructions LIKE ?, '%#{search[0].strip}%', '%#{search[0].strip}%'"
# Condition 2: if tags included, any matching?
if !tags.empty?
tags.each do |tag|
conditions += "'AND tags LIKE ?', '%#{tag}%'"
end
end
end
# Hämtar och returnerar alla recipes där codition 1 och/eller 2 stämmer.
Recipe.find(:all, :conditions => [conditions]) unless conditions.length < 1
end
Any ideas why it return all records?
if you are using rails 3, then it is easy to chain find conditions
def self.search(string, tags)
klass = scoped
if string.present?
klass = klass.where('name LIKE ? OR instructions LIKE ?', "%#{string}%", "%#{string}%")
end
if tags.present?
tags.each do |tag|
klass = klass.where('tags LIKE ?', "%#{tag}%")
end
end
klass
end
When you do
search.present? do
...
end
The contents of that block are ignored - it's perfectly legal to pass a block to a function that doesn't expect one, however the block won't get called unless the functions decides to. As a result, none of your condition building code is executed. You probably meant
if search.present?
...
end
As jvnill points out, it is in general much nicer (and safer) to manipulate scopes than to build up SQL fragments by hand

Optional mix of filter parameters in a search the Rails way

I've got a simple list page with a couple of search filters status which is a simple enumeration and a test query which I want to compare against both the title and description field of my model.
In my controller, I want to do something like this:
def index
conditions = {}
conditions[:status] = params[:status] if params[:status] and !params[:status].empty?
conditions[???] = ["(descr = ? or title = ?)", params[:q], params[:q]] if params[:q] and !params[:q].empty?
#items = Item.find(:all, :conditions => conditions)
end
Unfortunately, it doesn't look like I can mix the two types of conditions (the hash and the paramatized version). Is there a "Rails Way" of doing this or do I simply have to do something awful like this:
has_status = params[:status] and !params[:status].empty?
has_text = params[:q] and !params[:q].empty?
if has_status and !has_text
# build paramatized condition with just the status
elsif has_text and !has_status
# build paramatized condition with just the text query
elsif has_text and has_status
# build paramatized condition with both
else
# build paramatized condition with neither
end
I'm migrating from Hibernate and Criteria so forgive me if I'm not thinking of this correctly...
Environment: Rails 2.3.4
You can mix hash and array conditions using scopes:
hash_conditions = {}
# build hash_conditions
items_scope = Item.scoped(:conditions => hash_conditions)
unless params[:q].blank?
items_scope = items_scope.scoped(:conditions => ["(descr = ? or title = ?)", params[:q], params[:q]])
end
...
items = items_scope.all
So you can mix and match any types of conditions, and the query will be executed only when you do items_scope.all
a=[],b=[]
unless params[:status].blank?
a << "status = ?"
b << params[:status]
end
unless params[:q].blank?
a << "(descr = ? or title = ?)"
b << params[:q] << params[:q]
end
#items = Item.all( :conditions => [a.join(" AND "), b] )
A better search on my part turned up something called "named scopes" which looks like is exactly what I'm looking for. I'm about to see if it will work with the will_paginate gem....
Reference:
http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/

Resources