Searching for parameter value in a postgres column - ruby-on-rails

I'm attempting to implement an advanced search on my Rails 5 site. The user passes in a parameter "provider_type", and I would like to return all records that contain that value. The value is chosen from a dropdown list using simple-form. My new.html.erb looks like this:
<%= simple_form_for Search.new, :html => {:class => 'form-horizontal' } do |f| %>
<%= f.input :provider_type, collection: ['Mental Health', 'Medical'] %>
<%= f.button :submit %>
<% end %>
My Search model looks like this:
class Search < ApplicationRecord
def search_providers
providers = Provider.all
providers = providers.where("provider_type LIKE ?", ['Mental Health', 'Medical']) if provider_type.present?
providers
end
end
And my Searches controller:
def SearchesController < ApplicationController
def new
#types = Provider.uniq.pluck(:provider_type)
end
private
def search_params
params.require(:search).permit(:provider_type)
end
end
end
When I try to search for 'Mental Health' in the search form, I get this error: PG::UndefinedFunction: ERROR: operator does not exist: character varying[] ~~ unknown
EDIT
When I reword it as
providers.where(provider_type: provider_type) if provider_type.present?
This produces the error "PG::InvalidTextRepresentation: ERROR: malformed array literal: "%Mental Health%" DETAIL: Array value mut start with "{" or dimension information.

Probably you need not LIKE operator but IN. IN (or ANY) checks if fields match to one of element of array:
providers.where(provider_type: provider_type) if provider_type.present?
Rails 3 / Ruby: ActiveRecord Find method IN condition Array to Parameters single quote issue

Related

How to query the activerecord based on the enum status?

I am trying implement a search/filter action on a model Production based on a column status. The column status is of integer type. Later for the purpose of readability I used enum datatype on status column as follows.
class Production < ApplicationRecord
enum status:{
Preproduction:1,
Postproduction: 2,
Completed:3
}
end
Then I started to work on a search/filter functionality to fetch the record based on the status given by the user.
productions_controller
def filter
if params[:filter]
#productions = Production.where('productions.status like ?', "%#{params[:filter]}%")
else
#productions = Production.all
end
end
view
<%= form_tag [:filter, :productions], :method => 'get' do %>
<p>
<%= text_field_tag :filter, params[:filter] %>
<%= submit_tag "Filter", :status => nil %>
</p>
<% end %>
Now I am able to query the record properly only if I enter the integer values like 1 2 or 3 in the text field. When I enter the status like Preproduction like I assigned, I am not getting the result. I am getting a blank page. How can I fix this ? How can I make it to accept the string and query successfully ?
You can do this...
#productions = Production.where('productions.status like ?', "%#{Production.statuses[params[:filter]]}%")
Enums have a pluralized class method, so enum status in Production has a hash
Production.statuses which looks like your status hash but with the symbols changed into strings.

Search/ filter method for all attributes in my index table

I'm trying to write a row for my index table that filters my objects regarding a specific value of a specific column. What I have until now is this:
pimps_controller.rb:
def index
#pimps = Pimp.search(params[:search])
end
pimp.rb:
def self.search( search)
if search
where('title LIKE ?', "%#{search}%")
else
scoped
end
end
A part of view:
<%= text_field_tag :search, params[:search] %>
That filters after the objects title only so I tried to alter it to make it functional for different search fields that can filter after different attributes. I want to pass a second parameter value if someone fires the search function to make sure it triggers for the right attributes. That's what I've tried:
pimps_controller.rb
#pimps = Pimp.search(params[:search_column],params[:search])
pimp.rb:
def self.search(search_column, search)
if search
col = "%#{search_column}"
s = "%#{search}%"
where(col 'LIKE ?', s)
else
scoped
end
end
The view:
<%= text_field_tag :search, params[:search], params[:search_column => title] %>
But it's not working. I get an error message for passing the both parameters in one search field I guess. How would you do it?
Here's a simple tutorial on how to do it:
https://we.riseup.net/rails/simple-search-tutorial
In the model, you will have to add the fields with or condition to the query.
def self.search(search)
search_condition = "%" + search + "%"
find(:all, :conditions => ['title LIKE ? OR description LIKE ?', search_condition, search_condition])
end
If you want to define the field to search in the params you can use string interpolation with simple quotes:
%q(text contains "#{search.query}")
You need 2 text fields, one for the column, one for the value:
# view
<%= text_field_tag :search_value, params[:search_value] %>
<%= text_field_tag :search_column, params[:search_column] %>
# controller
#pimps = Pimp.search(params[:search_column], params[:search_value])
# Pimp model
def self.search(search_column, search_value)
if search_value.present? && search_column.present?
column = self.column_names.include?(search_column.to_s) ? search_column : 'title'
value = "%#{search_value}%"
where("#{self.table_name}.#{column} LIKE ?", value)
else
scoped
end
end
The problem about this method is that if you don't type the exact name of the column, it will search the value in the column title. I think you should use a select_tag, listing all searchable columns of the model:
# view
<%= select_tag :search_column, options_for_select(Pimp.column_names.map { |col| [col, col] }, params[:search_column]) %>
This view code will display a select tag with the available columns of the Pimp model. You can easily limit the searchable columns by defining a class method on Pimp:
# Pimp model
def searchable_columns
self.column_names - ['id', 'created_at', 'updated_at']
end
# view
<%= select_tag :search_column, options_for_select(Pimp.searchable_columns.map { |col| [col, col] }, params[:search_column]) %>

is there a way to convert an array in Ruby to a float by removing the brackets?

I have a little problem that seems rather easy to solve but I can't seem to find a solution. I'm using the Geocoder gem for Rails and from this method of:
Geocoder.coordinates(params[:search])
I'm getting an array that contains floats like this: [43.653226, -79.3831843]
My question is, is there a way to remove the brackets from the array or convert the value to a float so the output is 43.653226, -79.3831843?
My closest solution so far has been to do this:
a = Geocoder.coordinates(params[:search])
and then to obtain each float individually by doing this in the method call:
a[0] --> 43.653226
a[-1] --> -79.3831843
This doesn't seem to work as I get an error with the particular method that I am trying to use.
I'm using the 'sunspot-solr' and 'sunspot-rails' gem for this and what I am trying to do is allow a user to enter a city to find users and then display users near that city within a 100, 200 km radius
This is what I have in my user.rb file:
class User < ActiveRecord::Base
searchable do
text :city
latlon(:location) { Sunspot::Util::Coordinates.new(latitude, longitude) }
end
end
This is what's in my users_controller.rb
def index
if params[:search]
#search = User.search do
fulltext params[:search]
with(:location).near(*Geocoder.coordinates(params[:search]),:precision => 6)
end
#users = #search.results
end
end
This is the line that seems to be causing the problem: with(:location).near(*Geocoder.coordinates(params[:search]),:precision => 6)
And finally in my view, this is the search form:
<%= form_tag users_path, class: "form-signin", role: "form", method: :get do %>
<%= text_field_tag :search, params[:search], class: "form-control", placeholder:"Type in a location" %>
<div><%= submit_tag "Search users", class: "btn btn-lg btn-primary btn-block", id: "home-search" %></div>
<% end %>
Now when I'm trying to add the geospatial feature I'm getting a 400 error that looks like this:
RSolr::Error::Http - 400 Bad Request Error: {'responseHeader'=>{'status'=>400,'QTime'=>2},'error'=>{'msg'=>'com.spatial4j.core.exception.InvalidShapeException: incompatible dimension (2) and values (dpz83dffmxps). Only 0 values specified','code'=>400}}
If anyone can help that'd me great!
The values are floats already, if you want to use the content of the array as 2 arguments to a method call then you can use the splat operator (*) e.g
method_call(*[43.653226, -79.3831843]) is same as method_call(43.653226, -79.3831843)
or destructure the array and pass the new variables as argument e.g:
lat, lng = [43.653226, -79.3831843]
method_call(lat,lng)
You could use join:
eg.
a = Geocoder.coordinates(params[:search])
a.join(", ");
Would return the array - comma separated.

How to extract info from input field in ruby

I'm a frontend + PHP dev, trying to fix [] in a project built in Rails.
[] = Fetch color, show a slightly darker color.
This row:
<%= f.text_field attribute %>
creates an input field with a value that can be translated into a color. I'm at loss as to where to look for how it adds that value. I'm trying to use the value that this input field generates.
this is code from the file select_a_color_input.html.erb inside the app/views/shared folder. Any ideas on where to continue my treasure hunt? :)
update: I found this!
def app_text_field(attribute, args = {})
render_field 'text_field', field_locals(attribute, args)
end
Does that help? ^__^
update:
The form builder
class AppFormBuilder < ActionView::Helpers::FormBuilder
def form_fields(partial = nil , options = {})
partial ||= 'form'
fields = ''
unless options.delete(:without_error_messages)
fields << #template.render('shared/error_messages', :target => Array(#object).last)
end
fields << #template.render(partial, options.merge(:f => self))
end
def app_text_field(attribute, args = {})
render_field 'text_field', field_locals(attribute, args)
end
def app_file_field(attribute, args = {})
render_field 'file_field', field_locals(attribute, args)
end
private
def render_field(name, locals)
#template.render field_path(name), locals
end
def field_locals(attribute, args = {})
help_options = args[:help_options] || {}
field_options = args[:field_options] || {}
html_options = args[:html_options] || {}
{ :f => self, :attribute => attribute, :help_options => help_options, :field_options => field_options, :html_options => html_options, :object => object }
end
def field_path(value)
"shared/app_form/#{value}"
end
end
update:
When I tried to add
<%= content_tag(:p, attribute) %>
It does not give me the values, but instead the id/name of the item, not the colour.
<%= f.text_field attribute %>
This by itself is not very useful to help us gather context. What's the surrounding markup look like? attribute is a ruby variable in this instance. If it were f.text_field :attribute, then :attribute is now a symbol instead of a variable and this would indicate that it maps to the attribute method on X model. This all depends on what your form_for looks like though. I'll give an example:
<%= form_for #user do |f| %>
<%= f.text_field :attribute %>
In this case, we have a form for the User model, and our text_field maps to #user.attribute. The field itself looks something like this:
<input type='text' name='user[attribute]'>
And in the controller's #update or #create action (depending on if this is a new record or an existing record you're editing) the value would be accessible in this fashion:
params[:user][:attribute]
However, it's impossible to say what exactly the params will look like in your particular case. What action is being run? What's the name of the file that this is being loaded from? "app/views/users/new" would indicate the #new action handles this page, and the #create action will handle the form submission.
Things we need to know to fully solve your problem:
Name and relevant code of the controller that's handling this action.
Full view path that this is being rendered from
The rest of the markup starting at form_for and ending at this field attribute
What value does attribute hold? It's a variable, so it must be holding a symbol value or something that will indicate which field is being mapped to this input.

Ransack search not working if there is 'space' in search term

I am using ransack for search in my rails 3.2 application using postgres as database.
I have a Invoice model and every invoice belongs_to a buyer. Below is my search form in index page.
views/invoices/index.html.erb
<%= search_form_for #search do |f| %>
<%= f.text_field :buyer_name_cont %>
<%= f.submit "Search"%>
<% end %>
And here is my controller code.
controllers/invoices_controller.rb
def index
#search = Invoice.search(params[:q])
#invoices=#search.result(:distinct => true).paginate(:page => params[:page], :per_page => GlobalConstants::PER_PAGE )
respond_to do |format|
format.html # index.html.erb
format.json { render json: #invoices }
end
end
Let's say a invoice is there of a buyer having name "Bat Man".
If I search "Bat", I get the invoice in results.
Again if I search "Man", I get the invoice in results.
But if I search "Bat Man", I don't get the invoice in results.
I know it might be something trivial but I am not able to resolve.
Update
When I tried the sql query formed directly in database using pgAdmin, I realized that in database there were multiple spaces in the buyer name, something like "Bat.space.space.space.Man".
Can something be done so that "Bat.space.Man" search also finds "Bat.space.space.space.Man" in results?
You could sanitize your data. For instance with regexp_replace(). Run in the database once:
UPDATE invoice
SET buyer = regexp_replace(buyer, '\s\s+', ' ', 'g')
WHERE buyer <> regexp_replace(buyer, '\s\s+', ' ', 'g');
And sanitize new inserts & updates likewise.
\s .. class shorthand for "white space" (including tab or weird spaces).
The 4th parameter 'g' is for "globally", needed to replace all instances, not just the first.
Ransack not support cont search for multi terms, I solved the requirement my customized way. the details as following:
Add scope to your model:
scope :like_search, ->(column, value) {
keywords = value.to_s.split.map{ |k| "%#{k}%" }
where(Array.new(keywords.size, "#{column} ILIKE ?").join(' AND '), *keywords)
}
in your view. instead of using f.text_field :buyer_name_cont provided by ransack, use normal field helper text_field_tag :buyer_name, params[:buyer_name]
then restrict your ransack in scope:
scope = Invoice.like_search(:name , params[:buyer_name])
#q = scope.ransack(params[:q])

Resources