Rails: Search by custom instance method's value using tire gem & elasticsearch - ruby-on-rails-3

For example, I have Article model like
class Article < ActiveRecord::Base
#Columns: id, title, status_number...etc
STATUSES = {1 => "SUCCESS", 2 => "REJECTED"}
include Tire::Model::Search
include Tire::Model::Callbacks
def display_status
STATUSES[status_number]
end
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 2) do
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
end
end
end
end
how to include display_status as "SUCCESS" by default in search method?
I tried
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
must { term :display_status , "SUCCESS" }
end
end
But couldn't get result.
Please help to solve this problem. Thanks

Related

rails 3 validations uniqueness on attributes of an association

i have a model called Fund and a model called Company .. where fund belongs_to a company.
i have this validation in my Fund table:
validates :name, presence: true, uniqueness: true
This works both on server side and client side using client_side_validations. But i want my fund names to be unique across both fund.name values and fund.company.name values. And i want to do it in a way it would work with client_side_validations too.
Suggestions?
Ended up creating a very specific validator and adding it to client-side-validation. Here'z the breakdown
In models/fund.rb
validates_fund_name_not_company_name :name
new file in config/initializers/validators .. called fund_name_not_company_name_validator.rb
class FundNameNotCompanyNameValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
if ::Company.exists?(name: value)
record.errors.add(attr_name, :fund_name_not_company_name, options.merge(:value => value))
end
end
end
# This allows us to assign the validator in the model
module ActiveModel::Validations::HelperMethods
def validates_fund_name_not_company_name(*attr_names)
validates_with FundNameNotCompanyNameValidator, _merge_attributes(attr_names)
end
end
module ClientSideValidations::Middleware
class FundNameNotCompanyName < ClientSideValidations::Middleware::Base
def response
if ::Company.exists?(name: request.params[:name])
self.status = 404
else
self.status = 200
end
super
end
end
end
then in app/assets/javascripts/rails.validations.custom.js
clientSideValidations.validators.remote['fund_name_not_company_name'] = function(element, options) {
if ($.ajax({
url: '/validators/fund_name_not_company_name',
data: { name: element.val() },
// async must be false
async: false
}).status == 404) { return options.message; }
}
This helped a great deal

Ruby on Rails - search in database based on a query

I have a simple form, where I set up a query that I want to browse, for example panasonic viera.
This is on how I search the term in database:
Product.where("name ilike ?", "%#{params[:q]}%").order('price')
The query looks like %panasonic viera%, but I would need to search the query this way: %panasonic%viera% - I need to find all products, where is in the title the word panasonic or viera... but how to make this query?
One solution would be to break up your query into individual terms and build a set of database queries connected by OR.
terms = params[:q].split
query = terms.map { |term| "name like '%#{term}%'" }.join(" OR ")
Product.where(query).order('price')
If you're using PostgreSQL, you can use pg_search gem. It's support full text search, with option any_word:
Setting this attribute to true will perform a search which will return all models containing any word in the search terms.
Example from pg_search:
class Number < ActiveRecord::Base
include PgSearch
pg_search_scope :search_any_word,
:against => :text,
:using => {
:tsearch => {:any_word => true}
}
pg_search_scope :search_all_words,
:against => :text
end
one = Number.create! :text => 'one'
two = Number.create! :text => 'two'
three = Number.create! :text => 'three'
Number.search_any_word('one two three') # => [one, two, three]
Number.search_all_words('one two three') # => []
How about via ARel
def self.search(query)
words = query.split(/\s+/)
table = self.arel_table
predicates = []
words.each do |word|
predicates << table[:name].matches("%#{word}%")
end
if predicates.size > 1
first = predicates.shift
conditions = Arel::Nodes::Grouping.new(predicates.inject(first) {|memo, expr| Arel::Nodes::Or.new(memo, expr)})
else
conditions = predicates.first
end
where(conditions).to_a
end
This isn't working?
WHERE name LIKE "panasonic" OR name LIKE "viera"

Rails: accessing the values of columns selected with column_names

Any ideas what it takes to get this to work? I can't for the life of me figure it out.
def get_prices(c)
#print_prices = {}
Billing.where(:name => c).column_names.each do |d|
if d.match(/^print_/)
#print_prices[d] = d.value
end
end
return #print_prices
end
I've got no idea what to substitute d.value for.
Cheers for any help.
The following code will perform the query, returned in the form of a relation, and reject all items in the attribute key-value hash which do not match the given regex, which, in this case, is /^print_/.
def get_prices(c)
Billing.where(:name => c).first.attributes.reject{ |i| !i.match(/^print_/) }
end
Alternatively, it can also be written as:
def get_prices(c)
Billing.where(:name => c).first.attributes.select{ |i| i.match(/^print_/) }
end

OR query with Tire / Elastic

This is probably very simple, but I can't work it out. I have this tire/elasicsearch query on Project class:
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 8) do
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
must { term :status, "posted" }
must { term :budget, params[:budget] } if params[:budget].present?
end
end
end
end
I need the Status term to be searching on 'posted' OR 'closed' and can't work this out. I tried term :status, ["posted", "closed"] but this doesn't return results so probably doing an AND.
Be grateful for the right tire syntax here. (NB not looking for the jason/elasticsearch string here)
Many thanks!
What you need is a terms query, which does the OR between terms passed as an array:
must { terms :status, ["posted"] }
or something like this for dynamic values:
# params[:status] can be single value or array
must { terms :status, Array(params[:status]) }
See this part of Tire documentation for more info and context.

Rails assigning names to variables

I'm building a user ranking system, and am trying to assign user.rank values with a name.
I wanted to define something like this in my User model and then be able to reference it when displaying each user's rank, but this probably isn't the best way:
class User < ActiveRecord::Base
RANK_NAMES = {
'Peasant' => (0..75),
'Craftsman' => (76..250),
'Vassal' => (251..750),
'Noble' => (750..1500),
'Monarch' => (1501..999999)
}
Perhaps it would be better to define a method in a controller or helper like:
if user.rank == 0..75
rank_name = "Peasant"
elsif...
But not sure how to do that. Anyone have any thoughts? I'm not even sure what to call what it is I'm trying to do, thus making it difficult to research on my own.
It could be something even as simple as this, assuming user.rank exists.
class User < ActiveRecord::Base
...
def rank_name
case self.rank
when 0..75
'Peasant'
when 76..250
'Craftsman'
when 251..750
'Vassal'
when 750..1500
'Noble'
when 1501..999999
'Monarch'
end
end
...
end
If rank_name is specific to the User, I'd make it a method of User.
You could try something like below. It might give you some ideas.
class User
RANKS = [
{:name => 'Peasant', :min => 0, :max => 75},
{:name => 'Craftsman', :min => 76, :max => 250}
# ...
]
attr_accessor :rank
def rank_name
# TODO what happens if rank is out of range of all ranks or rank is nil
# or not an integer
User::RANKS[rank_index][:name]
end
private
def rank_index
User::RANKS.index { |r| (r[:min]..r[:max]).include? #rank }
end
end
user = User.new
user.rank = 76
puts user.rank_name # -> Craftsman