how to include integer fields in searchkick search? - ruby-on-rails

I use the following code to search using SearchKick:
Book.search(q,
misspellings: { below: 5 },
fields: [:name, :author, :pages],
order: { name: 'asc' },
page: params[:page],
per_page: 20)
When I search name and author it works fine.
However, when I search 130 for page it doesn't search the field.
name and author are string fields of a Book model, and page is an integer field.
My guess is the 130 is coming through as a string, and it is incorrectly trying to match the integer.
How can I make it so that I can search based on page numbers?

Have you tried calling to_i on params[:page]?
Book.search(q,
misspellings: { below: 5 },
fields: [:name, :author, :pages],
order: { name: 'asc' },
page: params[:page]&.to_i,
per_page: 20)
If you're right and it is comparing integer to string, this would fix it.

Related

How to search integer fields using searchkick?

How to make searchkick search integer fields?
Let's say i have a Book model with three properties namely name:string, author:string and pages:integer.
I want to search according to pages field. Right now if i use a query like below it works for string fields i.e name and author but it doesnt work for pages field which is of integer type.
Book.search(q,
misspellings: { below: 5 },
fields: [:name, :author, :pages],
order: { name: 'asc' },
page: params[:page],
per_page: 20)
I go to console and just searched Book.search(120, fields: [:pages]) and it returns empty result even though there are records with pages 120. Why is searchkick not searching for integer fields? I appreciate any help to this dilemma i am facing. Thanks!
I fixed it with this
In Book model
def search_data
{
name: name,
author: author,
pages: pages.to_s
}
end

Searchkick without stemming

I am indexing some of my data with searchkick (https://github.com/ankane/searchkick) as an array and it works almost fine :)
def search_data
{isbn: isbn,
title: title,
abstract_long: abstract_long,
authors: authors.map(&:name)}
end
The field I'm interested in is authors.
What I'd like to accomplish is to search for "Marias" and find all the authors that actually have that exact string in their surname like (Javier Marias) and not all the Maria/Mario/Marais that Searchkick returns, and have them with a much bigger priority.
This is how I search right now
Book.search(#search_key,
fields: [:authors, :title, {isbn: :exact}],
boost_by: {authors: 10, title: 5, isbn: 1})
Is this possible at all? TIA
In Elasticsearch it has a regular match to deal with this case, but abandoned by Searchkick.
You could choose a walk around way for this case:
def search_data
{
isbn: isbn,
title: title,
abstract_long: abstract_long,
author_ids: authors.map(&:id)
}
end
For search:
author_ids = Author.where(surname: 'Marias').map(&:id)
Book.search(
#search_key,
where: {author_ids: {in: author_ids}},
fields: [:title, {isbn: :exact}],
boost_by: {title: 5, isbn: 1}
)

Searchkick boost exact matches

I have 15,000 courses and I would like to boost the title of each class so exact matches of a class are boosted above everything else.
When I do Course.seach_kick('theory of interest' , 1)
The correct search is returned with the course 'theory of interest' as the first result.
However, when I do Course.search_kick('theory of interest 3618', 1)
3618 being the catalog_number, no results are returned. I expected the theory of interest course to be returned, and returned first. It seems the search is looking for the complete string 'theory of interest 3618' be included in the title of the course.
I understand 'and' is the default operator, Is it a requirement that I have to use the 'or' operator? I am hesitant to use the 'or' operator because of the unexpected results.
Thanks, I really enjoy using the gem.
search method:
def self.search_kick(query, page)
search(query,
fields: ["title^10", "description", "crse_id", "subject", "catalog_number" ],
facets: [:subject],
misspellings: false,
page: page,
per_page: 20
)
end
def search_data
{
title: title,
description: description,
crse_id: crse_id,
subject: subject,
catalog_number: catalog_nbr
}
end
Why not filter catalog_number in where clause:
search(query,
fields: ["title^10", "description", "crse_id", "subject" ],
facets: [:subject],
misspellings: false,
where: {catalog_number: 3618},
page: page,
per_page: 20
)
In most cases, where clause comes from an IF:
conditions = {}
if params[:catalog_number].present?
conditions[:catalog_number] = params[:catalog_number].to_i
end
search(query,
fields: ["title^10", "description", "crse_id", "subject" ],
facets: [:subject],
misspellings: false,
where: conditions,
page: page,
per_page: 20
)
You can insert as many as possible filters into where clause, just the same as ActiveRecord.where()
docs ref: https://github.com/ankane/searchkick#queries

How to make fields on my model not searchable but they should still be available in the _source?

I am using the tire gem for ElasticSearch in Rails.
Ok so I have been battling with this the whole day and this is how far I have got. I would like to make fields on my model not searchable but they should still be available in the _source so I can use them for sorting on the search result.
My mappings:
mapping do
indexes :created_at, :type => 'date', :index => :not_analyzed
indexes :vote_score, :type => 'integer', :index => :not_analyzed
indexes :title
indexes :description
indexes :tags
indexes :answers do
indexes :description
end
end
My to_indexed_json method:
def to_indexed_json
{
vote_score: vote_score,
created_at: created_at,
title: title,
description: description,
tags: tags,
answers: answers.map{|answer| answer.description}
}.to_json
end
My Search query:
def self.search(term='', order_by, page: 1)
tire.search(page: page, per_page: PAGE_SIZE, load: true) do
query { term.present? ? string(term) : all }
sort {
by case order_by
when LAST_POSTED then {created_at: 'desc'}
else {vote_score: 'desc', created_at: 'desc'}
end
}
end
end
The only issue I am battling with now is how do I make vote_score and created_at field not searchable but still manage to use them for sorting when I'm searching.
I tried indexes :created_at, :type => 'date', :index => :no but that did not work.
If I understand you, you are not specifying a field when you send your search query to elasticsearch. This means it will be executed agains the _all field. This is a "special" field that makes elasticsearch a little easier to get using quickly. By default all fields are indexed twice, once in their own field, and once in the _all field. (You can even have different mappings/analyzers applied to these two indexings.)
I think setting the field's mappings to "include_in_all": "false" should work for you (remove the "index": "no" part). Now the field will be tokenized (and you can search with it) under it's fieldname, but when directing a search at the _all field it won't affect results (as none of it's tokens are stored in the _all field).
Have a read of the es docs on mappings, scroll down to the parameters for each type
Good luck!
I ended up going with the approach of only matching on the fields I want and that worked. This matches on multiple fields.
tire.search(page: page, per_page: PAGE_SIZE, load: true) do
query { term.present? ? (match [:title, :description, :tags, :answers], term) : all }
sort {
by case order_by
when LAST_POSTED then {created_at: 'desc'}
else {vote_score: 'desc', created_at: 'desc'}
end
}
end

ElasticSearch and tire/Rails: use two fields for a single facet

Using Elasticsearch with Rails 3 and tire gem.
I have got facets to work on a couple of fields, but I now have a special requirement and not sure it is possible.
I have two fields on my model Project that both store the same values: Country1 and Country2
The user is allowed to store up to two countries for a project. The drop down menus on both are the same. Neither field is required.
What I would like is a single facet that 'merges' the values from Country1 and Country2 and would handle clicking on those facets intelligently (i.e. would find it whether it was in 1 or 2)
Here's my model so far: (note Country1/2 can be multiple words)
class Project < ActiveRecord::Base
mapping do
indexes :id
indexes :title, :boost => 100
indexes :subtitle
indexes :country1, :type => 'string', :index => 'not_analyzed'
indexes :country2, :type => 'string', :index => 'not_analyzed'
end
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 10) do
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
must { term :country1, params[:country] } if params[:country].present?
end
end
sort { by :display_type, "desc" }
facet "country" do
terms :country1
end
end
end
Any tips greatly appreciated!
This commit https://github.com/karmi/tire/commit/730813f in Tire brings support for aggregating over multiple fields in the "terms" facet.
The interface is:
Tire.search('articles-test') do
query { string 'foo' }
# Pass fields as an array, not string
facet('multi') { terms ['bar', 'baz'] }
end
according to the elasticsearch docs for the terms facet http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html this should be possible:
Multi Fields:
The term facet can be executed against more than one field, returning
the aggregation result across those fields. For example:
{
"query" : {
"match_all" : { }
},
"facets" : {
"tag" : {
"terms" : {
"fields" : ["tag1", "tag2"],
"size" : 10
}
}
}
}
did you try providing an array of fields to the term facet like terms :country1, :country2 ?
This seems to work but I need to test it more: facet('country') { terms fields: [:country1, :country2]}

Resources