Query always the same with Sunspot/Solr on rails - ruby-on-rails

I have a problem with sunspot. When i do a SOLR request i have always the same query.
SOLR Request (3.7ms) [ path=select parameters={fq: ["type:Tag"], start: 0, rows: 30, q: "*:*"} ]
No matter what I type in the search field , it's always the same query
Here my code :
tag.rb
class Tag < ActiveRecord::Base
belongs_to :pictogram
searchable do
text :name
end
end
searchs_controller.rb
class SearchsController < ApplicationController
def index
if params[:search]
pictograms = Array.new
#term = params[:query]
search = Tag.search do
fulltext #term
end
index = 0
search.results.each do |t|
if !pictograms.include? t.pictogram
pictograms[index] = t.pictogram
index = index + 1
end
end
#results = pictograms.group_by { |p| p.category }
#search_mode = true
end
end
end
index.html
<%= form_tag result_search_path do %>
<div class="input-group input-group-lg">
<%= search_field_tag :term,"#{#term}", class: 'form-control' %>
<span class="input-group-btn">
<%= submit_tag "Search", :class => 'btn btn-primary btn' %>
</span>
</div>
<% end %>

I think the params you are using for search are not correct.
<%= search_field_tag :term,"#{#term}", class: 'form-control' %>
means that the params that you are passing back to the controller is [:term],
so the code should be something like:
if params[:term].present?
pictograms = Array.new
#term = params[:term]
search = Tag.search do
fulltext params[:term]
end
and not params[:search] and params[:query] like you have tried.

Related

Search multiple fields rails 5

I have the following form:
<div class='panel' id='panel-advanced-search'>
<%= form_tag contacts_path, method: :get do %>
<div>
<div class='advanced_search'>
<div>
<%= label_tag "Contact Type" %>
<%= select_tag(:contact_type, options_for_select(Contact::TYPES, selected: params[:contact_type]), prompt: 'Any', class: 'customSelect') %>
</div>
<div>
<%= label_tag "Prospect Strength" %>
<%= select_tag(:prospect_strength, options_for_select(Contact::PROSPECT_STRENGTHS, selected: params[:prospect_strength]), prompt: 'Any', class: 'customSelect') %>
</div>
<div>
<%= label_tag "Customer" %>
<%= collection_select(:customer_account_id, :customer_account_id, Customer.order(:name), :id, :name, { prompt: '' }, class: 'customSelect select2') %>
</div>
<div>
<%= label_tag "Supplier" %>
<%= collection_select(:supplier_account_id, :supplier_account_id, Supplier.order(:name), :id, :name, { prompt: '' }, class: 'customSelect select2') %>
</div>
<div>
<%= label_tag "Company Name" %>
<%= text_field_tag :company_name %>
</div>
<div>
<%= label_tag "Name" %>
<%= text_field_tag :name %>
</div>
<div>
<%= label_tag "Job Title" %>
<%= text_field_tag :title %>
</div>
<div>
<%= label_tag "Postcode" %>
<%= text_field_tag :postcode %>
</div>
<div>
<%= label_tag :created_at, 'Created From' %>
<div class="date-picker">
<%= text_field_tag :created_at, nil, class: 'date-picker-select' %>
<span class="date-picker-btn">
<span class="icon-calendar" aria-hidden="true"></span>
</span>
</div>
</div>
<div>
<%= label_tag :updated_at, 'Created To' %>
<div class="date-picker">
<%= text_field_tag :updated_at, nil, class: 'date-picker-select' %>
<span class="date-picker-btn">
<span class="icon-calendar" aria-hidden="true"></span>
</span>
</div>
</div>
<div>
<%= label_tag "Tags" %>
<%= collection_select(:tag_list, :tag_list, #tags.order(:name), :name, :name, {}, { class: 'select2', multiple: true }) %>
</div>
<div>
<%= label_tag "Assignee" %>
<%= collection_select(:assigned_to, :assigned_to, User.all, :id, :name, { prompt: 'Any' }, class: 'customSelect select2') %>
</div>
<div>
<%= label_tag "Obsolete?" %>
<%= select_tag(:obsolete, options_for_select(['Obsolete', 'All'], selected: params[:obsolete]), prompt: 'Not obsolete?', class: 'customSelect') %>
</div>
<div>
<%= label_tag "Send Results To" %>
<%= select_tag(:subsequent_action, options_for_select([
['Report', 'report'],
['CSV Export', 'csv_export'],
['New Event', 'new_event']
]), prompt: 'None', class: 'customSelect') %>
</div>
</div>
<div class="advanced_search_btns">
<%= submit_tag submit_text %>
<%= link_to secondary_btn, contacts_path, class: 'btn-medium' %>
</div>
</div>
<% end %>
</div>
and the following method in the model
def self.advanced_search
Contact.where('
contact_type LIKE :search OR
prospect_strength LIKE :search OR
customer_account_id LIKE :search OR
supplier_account_id LIKE :search OR
company_name LIKE :search OR
name LIKE :search OR
title LIKE :search OR
postcode LIKE :search OR
created_at LIKE :search OR
updated_at LIKE :search OR
tag_list LIKE :search OR
assigned_to LIKE :search OR
obsolete LIKE :search
', search: "%#{search}%"
)
end
How do I go about using this method so the user can use this search form with multiple params? I already have the following in the index method for a basic search so I need to have both search forms
def index
#per_page = params[:per_page] || 20
#tags = ActsAsTaggableOn::Tag.all
if params[:search].present?
#contacts = Contact.search(params[:qs], params[:search]).order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: #per_page)
else
#contacts = Contact.all.order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: #per_page)
end
end
Edit Updated the form above to the complete form, ideally, I would like the forms functionality to be entirely in the model.
Edit #2
This is the basic search:
model
QUICK_SEARCH_FIELDS = {
name: {
column_names: 'contacts.name'
},
customers: {
joins_table: :customer,
column_names: 'contacts.name',
filters: { contact_type: 'customer' }
},
suppliers: {
joins_table: :supplier,
column_names: 'contacts.name',
filters: { contact_type: 'supplier' }
},
tags: {
tagged_with: true
}
}.with_indifferent_access
def self.search(field, query)
field = QUICK_SEARCH_FIELDS[field]
contact = all
contact = contact.joins(field[:joins_table]) if field[:joins_table]
contact = contact.where(field[:filters]) if field[:filters]
contact = contact.where("#{field[:column_names]} LIKE ?", "%#{query}%") if field[:column_names]
contact = contact.tagged_with(query) if field[:tagged_with]
contact
end
form
<div class='panel' id='panel-search'>
<%= form_tag contacts_path, method: :get do %>
<div class='l-inline-row-block'>
<div class='l-inline-col width_120px'>
<%= select_tag(:qs, options_for_select(Contact::QUICK_SEARCH_FIELDS.keys(), selected: params[:qs]), class: 'customSelect') %>
</div>
<div class='l-inline-col'>
<%= search_field_tag :search, params[:search] %>
</div>
<div class='l-inline-col' style='width: 100px;'>
<%= submit_tag submit_text %>
</div>
</div>
<% end %>
</div>
you may need to make view adjustments and you cannot just use the search term/query for all fields:
1.make sure the fields inside the form in your view are all the same names of the columns in the Contact model that need to be searched
2.add a hidden field inside each form so you can determine which search this is (there is another option but this one is less work)
3.change the collection_select where user can only select one field to select_tag in order to not mess up query building
every field that is filled will pass a value- you can build a hash including column/form-field entered and their value e.g { contact_type: "value1", prospect_strength: "value2" }
after that, you would need to build a query using that hash and query the DB
this would look like so:
In your view:
add this in the advanced search form
<%= hidden_field_tag :search_type, :advanced %>
and in your normal search add this
<%= hidden_field_tag :search_type, :normal %>
In your model:
def self.advanced_search(values_by_column_name) # values_by_column_name is a hash passed from the controller
tag_list = values_by_column_name.delete("tag_list")
sql_query = values_by_column_name.keys.map { |column| "#{column} LIKE :#{column}" }
sql_query = sql_query.join(" OR ")
values_by_column_name = values_by_column_name.transform_values { |value| "%#{value}%" }.symbolize_keys
relation = Contact.where(sql_query, values_by_column_name)
relation = relation.tagged_with(tag_list) if tag_list
relation
end
in your controller:
ADVANCED_SEARCH_FIELDS = [
:contact_type,
:prospect_strength,
:customer_account_id,
:supplier_account_id,
:company_name,
:name,
:title,
:postcode,
:created_at,
:updated_at,
{tag_list: []},
:assigned_to,
:obsolete,
]
def index
#per_page = params[:per_page] || 20
#tags = ActsAsTaggableOn::Tag.all
if advanced_search?
#contacts = Contact.advanced_search(advanced_search_params)
elsif normal_search?
#contacts = Contact.search(params[:qs], params[:search])
else
#contacts = Contact.all
end
#contacts = #contacts.order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: #per_page)
end
def advanced_search?
params[:search_type] == "advanced" && advanced_search_params.any?
end
def normal_search?
params[:search_type] == "normal" && params[:search].present?
end
def advanced_search_params
#_advanced_search_params ||= params.permit(ADVANCED_SEARCH_FIELDS).select { |_, v| v.present? }.to_h
end

Search method Ruby

I made this method for a search method in ruby with a form.
I think, that this code can be simplify. What do u think ?
And I have an issue. When a user does not enter anything in the form, the results display all the tips in the database.
But when a user combines two searches, for example a keywords and a country, the result is all the tips that have the keywords as well as all the tips that have the country concerned. Except what I'm looking for is for the two criteria to be combined.
Search.rb
require 'pry'
class Search < ApplicationRecord
def self.search(keywords, category_id,city,country)
table_of_ids_country = []
table_of_ids_city = []
table_of_ids_title = []
table_of_ids_cats = []
two_searches_ids = []
merged_table_of_tips_ids = []
#results = []
tips_by_country = Tip.where(["country like?",country]) if country.present?
tips_by_city = Tip.where(["city like?",city]) if city.present?
tips_by_keyword = Tip.where(["title LIKE?","%#{keywords}%"]) if keywords.present?
tips_by_cat = Category.where(["id = :id",{id:category_id}]) if category_id.present?
two_searches = Tip.where(["title LIKE?",keywords]) if keywords.present? && Category.where(["id = :id",{id:category_id}]) if category_id.present?
if tips_by_country != nil
tips_by_country.each do |tip|
tip.id
table_of_ids_country << tip.id
end
end
if tips_by_city != nil
tips_by_city.each do |tip|
tip.id
table_of_ids_city << tip.id
end
end
if tips_by_keyword != nil
tips_by_keyword.each do |tip|
tip.id
table_of_ids_title << tip.id
end
end
if two_searches != nil
two_searches.each do |tip|
tip.id
two_searches_ids << tip.id
end
end
if tips_by_cat != nil
Category.find(tips_by_cat[0].id).tips.each do |tip|
table_of_ids_cats << tip.id
end
end
merged_table_of_tips_ids = [table_of_ids_title, table_of_ids_cats,table_of_ids_city,table_of_ids_country,two_searches_ids].flatten
merged_table_of_uniq_tips_ids = merged_table_of_tips_ids.uniq
merged_table_of_uniq_tips_ids.each do |tip|
result = Tip.find(tip)
#results << result
binding.pry
end
return #results
end
end
searches_controller
require 'pry'
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
#search = Search.search(#search.keywords, #search.category_id,#search.city,#search.country)
#Permet d'envoyer les paramètres au model search et à les réutilisé dans la méthode self.search
end
private
def search_params
params.require(:search).permit(:keywords,:category_id,:id,:city,:country)
end
end
my form :
<%=form_for #search do |f| %>
<div class="form-group">
<%= f.label :keywords, "Mots-clés" %>
<%= f.text_field :keywords, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :city, "Mots-clés" %>
<%= f.text_field :city, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :country, "Mots-clés" %>
<%= f.text_field :country, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :category_id, "Catégories" %><br>
<%= f.collection_select :category_id, Category.all, :id, :name, :include_blank => true %>
</div>
<div class="form-group d-flex justify-content-center">
<%= f.submit "Rechercher", class: "btn btn-primary" %>
</div>
<%end%>
class Search < ApplicationRecord
def self.search(keywords, category_id,city,country)
ids = if keywords.present? && category_id.present?
Tip.keywords(keywords).pluck(:id) & Category.category_id(category_id).pluck(:id)
elsif [keywords, category_id, city, country].any?(&:present?)
Tip.country(country)
.city(city)
.keywords(keywords)
.pluck(:id)
else
[]
end
Tip.find(ids.uniq)
end
end
class Category < ApplicationRecofd
scope :category_id, ->(category_id) {
where(["id = :id",{id:category_id}])
}
end
class Tip < ApplicationRecord
scope :country, ->(country) {
where("country like?",country) if country.present?
}
scope :city, ->(city) {
where("city like?",city) if city.present?
}
scope :keyword, ->(keywords) {
tips = tips.where("title LIKE?","%#{keywords}%") if keywords.present?
}
end
also notice I'm doing a union on category ids, so keep in mind
2.5.1 :002 > [1,2] && [2,3]
=> [2, 3]
2.5.1 :003 > [1,2] & [2,3]
=> [2]

ElasticSearch Range Aggregation no output results with no error

Hello I've got this problem with the rails Elascticsearch range aggregations, it seems right as there's no error output but then again it also doesn't aggregate.
Heres my controller
def results
min_price = params[:min_price] if params[:min_price].present?
max_price = params[:max_price] if params[:max_price].present?
price_ranges = [{to: max_price}, {from: min_price, to: max_price}, {from: min_price}]
#results = Item.search(params[:q], aggs: {item_final_price: {ranges: price_ranges}}, page: params[:page], per_page: 10) if params[:q].present?
end
and my model
class Item < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
# Item.import
searchkick callbacks: :async, highlight: [:item_name]
def search_data
{
item_name: item_name,
item_details: item_details,
item_final_price: item_final_price,
item_av_rating: item_av_rating
}
end
end
and my views
<%= form_tag results_path, method: :get, enforce_utf8: false, id: "q_filter" do %>
<section class="widget widget-categories">
<%= hidden_field_tag :q, params[:q] %>
<h3 class="widget-title">Price Range</h3>
<div class="form-group">
<label>Price Between</label>
<%= number_field_tag :min_price, params[:min_price], class: "form-control form-control-sm", placeholder: "Min Price" %>
</div>
<div class="form-group">
<label>And</label>
<%= number_field_tag :max_price, params[:max_price], class: "form-control form-control-sm", placeholder: "Max Price" %>
</div>
<%= button_tag(type: "submit", name: nil, class: "btn btn-outline-primary btn-sm btn-block") do %>
Filter Search
<% end %>
</section>
<% #results.each do |item| %>
<%= item.item_name %>
<% end %>
Try to target this in your view
#results.response["aggregations"]["item_final_price"]["buckets"]
If you're running in development, can you throw a binding.pry and see what the result keys are? The data should be there if the query returns results.

Filtering Rails 5 model using multiple parameters

I have an application that has hundreds of products. I created a simple filter search bar and it works great for a single parameter, but I would like to add multiple parameters and return any product that contains one of the search parameters.
Here is my working code for a single parameter; I would show what I've tried to add multiple params, but I am a bit out of my depth.
products_controller.rb
def index
#products = Product.all.order('LOWER(name)')
if params[:q]
#products = Product.where('name ILIKE ?', "%#{params[:q]}%").all.order('LOWER(name)')
end
end
_searchbar.html.erb
<div class="form-inline">
<%= form_tag(products_path, :method => "get", id: 'search-form', :html => {class: 'form'}) do %>
<div class="form-group">
<%= text_field_tag :q, params[:q], placeholder: 'Product Name', class: 'form-control' %>
</div>
<%= submit_tag 'Search', class: 'btn btn-primary' %>
<% end %>
</div>
We should know the operation behind. The sql query must be like this:
select * from products where name ilike '%name1%' or name ilike '%name2%';
According you code:
select * from products where name ilike '%name1,name2%'
That's incorrect.
Try to the following:
# :query need to be an array
# Eg:
# "Product name 1, Product name 2, Product name 3" => params[:q].split(', ')
# => ["Product name 1", "Product name 3", "Product name 3"]
search_string = []
query.each { |q| search_string << "name ILIKE ?" }
search_string = search_string.join(' OR ')
Product.where(search_string, *query)
You can do that using following steps like if you need to use input field then form look like this
<div class="form-inline">
<%= form_tag(products_path, :method => "get", id: 'search-form', :html => {class: 'form'}) do %>
<div class="form-group">
<%= text_field_tag :x, params[:x], placeholder: 'Product Name1', class: 'form-control' %>
</div>
<div class="form-group">
<%= text_field_tag :y, params[:y], placeholder: 'Product Name2', class: 'form-control' %>
</div>
<div class="form-group">
<%= text_field_tag :z, params[:z], placeholder: 'Product Name3', class: 'form-control' %>
</div>
<%= submit_tag 'Search', class: 'btn btn-primary' %>
<% end %>
</div>
Or you need this filtering key is in the checkbox then form look like this
<%= form_tag(products_path, :method => "get", id: 'search-form', :html => {class: 'form'}) do %>
<%= check_box_tag("x[]", "x") %> Xname
<%= check_box_tag("y[]", "y") %> Yname
<%= check_box_tag("z[]", "z") %> Zname
<%= submit_tag 'Search', class: 'btn btn-primary' %>
<% end %>
Now update the controller look like this
if params[:x] || params[:y] || params[:z]
#products = Product.where('true')
#products = #products.where('name ILIKE ?', "%#{params[:x]}%") unless params[:x].blank?
#products = #products.where('name ILIKE ?', "%#{params[:y]}%") unless params[:y].blank?
#products = #products.where('name ILIKE ?', "%#{params[:z]}%") unless params[:z].blank?
#products = #products.order('LOWER(name)')
else
#products = Product.all.order('LOWER(name)')
end
Hope it helps

Validation in form?

I am trying to do validation but didnot find the right way while uploading the excel
Code for view
<%= form_for :dump, :url=> import_csv_index_path, :html => { :multipart => true } do |f| %>
<% if (-----).errors.any? %>
<div id="error_explanation">
<%= pluralize((----).errors.count, "error") %> prohibited this link from being saved:
<% (---).errors.full_messages.each do |msg| %>
<li><%= msg %></li>
</div>
<% end %>
<%= f.label :'Select an Excel File:' %>
<%= f.file_field :file %>
<i href="#" id="blob_1" class="icon-info-sign" rel="popover" style="margin-top: 300px"></i><br/>
<%= submit_tag 'Submit' ,:id => 'submit',:class =>"btn btn-primary"%><br/><br/>
<% end -%>
Code for model:
class Person < ActiveRecord::Base
attr_accessible :date, :email, :name, :avatar ,:template_id
validates_presence_of :email,:message => 'Email is compulsory'
end
What do i write at place of (-----)
Thank you
I have done with the other approach so discuss here,
Code for controller:
def import
#error = []
#error = Person.import(params[:dump][:file])
redirect_to people_path, notice: "Employees imported.",:flash => { :error => "Employees( Email : invalid/blank) are not saved : #{#error.join(" , ")}" }
end
In model:
def self.import(file)
spreadsheet = open_spreadsheet(file)
#err = []
#alt = []
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
#person = Person.find_by_id(row["id"]) ||Person.new
#person.name = row["Name"].to_s
#person.date = row["Date"]
#person.email = row["Email"]
if #person.save
else
#err << {:name => row["Name"].to_s,:error => #person.errors.messages}
end
end
#err.each do |t|
#alt.push(t[:name])
end
return #alt
end
and call that as flash message...and working fine now.
Thanks
I think somewhere in your controller you have to call .validate on a new instance of your Person model with your form's data. You can save that model in an instance variable that you can put in place of the (-----).

Resources