Exposing a new table and column to rails with elastic search & searchkick - ruby-on-rails

I have added a new field to searchkick, and can get the results to return for that model but I am trying to find the parent model as a result.
People can have many tags. I can search for People just fine by any attribute that exist on that schema. I can search for Tags just fine and searchkick and elasticsearch will return the result.
I want to search by the tag name and then return the people associated with that result
search_query = if #q.present?
{
query: {
multi_match: {
query: #q.strip.downcase,
fields: %w(first_name last_name email_address phone_number address_1 name),
type: 'cross_fields',
operator: 'AND'
}
}
}
else
{
query: {
query_string: {
query: ('*'),
default_operator: 'AND'
}
},
# order: { signup_at: :desc },
# page: search_params[:page],
# per_page: (Kaminari.config.default_per_page unless request.format == :csv)
}
end
#results = Person.search search_query
#people = #results.records.where(active: true).order("people.#{sort_column}" + ' ' + sort_direction).page(search_params[:page])
This currently works fine for searching people only attributes. If I replace
#results = Tag.search search_query When inputting a tag name I get the resulting tag.
An older query was in place that worked fine, but had to be changed to allow full name searching. The old query was
query: {
query_string: {
query: (#q.strip.downcase),
default_operator: 'AND'
}
},
And that returned the associated tags with the rest of the code remaining unchanged.
Here is the search_data method that exist on the person model` # For Searchkick
def search_data
attributes.
each { |_k, v| v.downcase if v.is_a? String }.
merge({
tag: tags.map { |t| t.name.downcase },
trait: traits.map { |t| t.name.downcase },
trait_value: person_traits.map { |pt| pt.value.downcase },
question: questions.map(&:id),
answer: answers.map { |a| a.value.downcase },
last_participated: last_participation_date.to_s,
signup_at: signup_at.to_s
})
end
Please let me know if I can provide other information to help.

Since you have tags in the search_data method on Person, you can do:
Person.search("sometag", fields: [:tag])
Make sure your search_data method returns correct data with:
Person.first.search_data
And make sure you have reindexed.
Person.reindex

Related

Elastic Search results did not search space between words

I am using elasticsearch-rails gem in my rails application. Everything is setup, but i am having trouble when i search two words with space between them.
Here is my code:
SEARCH_FIELDS = %w"name email"
attribute :text, type: String, default: ''
response = MyModelName.search([query, filter, sort].compact.reduce(:merge)).page(page).per(per_page)
def query
query = {}.tap do |q|
q[:query_string] = {query: text, default_operator: 'AND', fields: SEARCH_FIELDS, allow_leading_wildcard: true}
end
return {query: query}
end
def filter
filters = []
filters << {term: {user_id: user_id}}
filters << {term: {hide: (hide.nil? ? false : hide)}}
return {filter: {and: filters}}
end
If i search first initials of name like ('John') that works fine, but name with space('John doe') did not get found.
make sure you're building the right syntax for your query, is you want exact match in ES you must set query between qoutes
search_field:"daniel acevedo"
{
"size": 10,
"query": {
"query_string": {
"query": "search_field:\"daniel acevedo\""
}
}
}

How do you filter a query with elasticsearch-rails gem?

I have a Piece model with a boolean attribute of published.
I want the search results to only contain Pieces that are plublished: true.
My index action for the PiecesController is:
def index
#list = params[:list]
#sort = params[:sort]
if params[:q]
#pieces = Piece.search(params[:q]).records
else
#pieces = Piece.all_in_category(#list, #sort)
end
end
From searching around it seems that I should overwrite the search method in the Piece controller but I am not sure the correct way of doing this to maintain the current search methods functionality.
What is the best way to filter the elasticsearch results using the elasticsearch-rails gem?
Try this format:
Elasticsearch v2.4
#customers = Customer.__elasticsearch__.search(
query: {
bool: {
filter: {
term: {"account_id" => current_account.id.to_s}
},
must: {
query_string: {
query: "*#{params[:q]}*"
}
}
},
},
size: options[:per_page],
from: options[:from]
).records
have you tried
#pieces = Piece.search(params[:q]).records.where(published: true)
it works on one of my ES models

searchkick advanced search not working

class Product < ActiveRecord::Base
belongs_to :city
has_and_belongs_to_many :categories
before_destroy { categories.clear }
searchkick locations: ["location"]
def search_data
{
istatus: i_status,
name: name,
price: price,
city_id: city_id,
value: value,
discount: discount,
expiry_date: expiry_date,
created_at: created_at,
products_sold: products_sold,
city: city.name,
deal_type: deal_type,
country: city.country.name,
category_id: categories.map(&:id),
location: [latitude, longitude]
}
end
def self.apply_filters(request)
# #product = Product.search "Tex-Mex", limit:10 #=>this works
#product = Product.search body: {match: {name: "Tex-Mex"}},limit: 10 #=>does not work, the limit part work
end
end
when i use advanced search using body.. it does not return the desired results although the limit:10 part us working as it does return 10 results only
I believe there is some missing information in the documentation.
Here's a reference to a body query that works based on the tests written in SearchKick:
https://github.com/ankane/searchkick/blob/c8f8dc65df2e85b97ea508e14ded299bb8111942/test/index_test.rb#L47
For advanced search to work, the way it should be written is:
#product = Product.search body: { query: { match: {name: "Tex-Mex"}}},limit: 10
You need a query key following the body.
// conditions = {}
query = Product.search params[:query], execute: false, where : conditions
query.body[:query] = { match: {name: "Tex-Mex"} }
query.body[:size] = 10
query.execute
You would need to build your query using the Elasticsearch DSL. Specifically, using size and match.
Product.search body: { query: { match: {name: "Tex-Mex"} }, size: 10 }
When using Advanced search, Searchkick ignores parameters outside the body hash. While the body hash allows you to use the full ES DSL.

How to filter search by attribute only if it exists using ElasticSearch and Tire?

Right now I wrote
Tire.search INDEX_NAME do
query do
filtered do
query { string term }
filter :or, { missing: { field: :app_id } },
{ terms: { app_id: app_ids } }
end
end
end.results.to_a
Well returning items that either have no app_id or one that matches your terms sounds like a job for an or filter - I'd try
filter :or, [
{:not => {:exists => {:field => :app_id}}},
{:terms => {:app_id => app_ids}}
]

Facet Troubles with Elasticsearch on Query

When adding a term to my query instead of a filter I am getting 0 facets. FYI I am using the tire gem with Ruby.
Here is my model code with its mapping:
class Property < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_and_belongs_to_many :tags
mapping do
indexes :id, type: 'integer'
indexes :status
indexes :refno, type: 'integer'
indexes :name, :analyzer => 'snowball', :boost => 100
indexes :description
indexes :tags, type: 'object',
properties: {
name: { type: 'multi_field',
fields: {
name: { type: 'string', analyzer: 'snowball' },
exact: { type: 'string', index: 'not_analyzed' }
}
}
}
end
def to_indexed_json
to_json( include: {
tags: { only: [:name] },
})
end
Then here is the search method
def self.search(params={})
tire.search(page: params[:page], per_page: 2, load: true) do
query do
boolean do
must { string params[:name], default_operator: "AND" } if params[:name].present?
must { term :status, 'live' }
must { term :refno, params[:refno]} if params[:refno].present?
# must { term :tag, params[:tag]} if params[:tag].present? ## does not work either
must { term 'tags.name.exact', params[:tag]} if params[:tag].present?
end
end
facet "tags" do
terms 'tags.name.exact'
end
raise to_json
# raise to_curl
end
end
I get 0 Facets. But if I move facets to a filter ie below I get full facets.
def self.search(params={})
tire.search(page: params[:page], per_page: 2, load: true) do
query do
boolean do
must { string params[:name], default_operator: "AND" } if params[:name].present?
must { term :status, 'live' }
must { term :refno, params[:refno]} if params[:refno].present?
end
end
filter :term, 'tags.name.exact' => params[:tag] if params[:tag].present?
facet "tags" do
terms 'tags.name.exact'
end
raise to_json
# raise to_curl
end
end
While this is ok it's not want, When a facet filter is clicked I want to remove non available tags from my facet filter and update the new facet count.
If it helps here is the json for the query which works and does not.
## No Factes
{
"query":{
"bool":{
"must":[
{
"query_string":{
"query":"England",
"default_operator":"AND"
}
},
{
"term":{
"status":"live"
}
},
{
"term":{
"tags.name.exact":[
"Pet Friendly"
]
}
}
]
}
},
"facets":{
"tags":{
"terms":{
"field":"tags.name.exact",
"size":10,
"all_terms":false
}
}
},
"size":2
}
## Facets working
{
"query":{
"bool":{
"must":[
{
"query_string":{
"query":"England",
"default_operator":"AND"
}
},
{
"term":{
"status":"live"
}
}
]
}
},
"facets":{
"tags":{
"terms":{
"field":"tags.name.exact",
"size":10,
"all_terms":false
}
}
},
"filter":{
"term":{
"tags.name.exact":[
"Pet Friendly"
]
}
},
"size":2
}
Really hope someone can advise. Starting to pull my hair out on this one.
You should use filtered query for facet seach to get exact result:
query do
filtered do
query { <search keywords> }
filter <your filter> (pass in facet values)
end
end
<facet>
...
<facet>
I was actually very close. As my tags can have multiple I needed to use terms not term ie,
def self.search(params={})
tire.search(page: params[:page], per_page: 2, load: true) do
query do
boolean do
must { string params[:name], default_operator: "AND" } if
must { term :status, 'live' }
must { term :refno, params[:refno]} if params[:refno].present?
must { terms 'tags.name.exact', params[:tag]} if params[:tag].present?
end
end
facet "tags" do
terms 'tags.name.exact'
end
# raise to_json
# raise to_curl
end
end
Thank you for your advise though imotov, Hoang.
A search request usually consists of two parts: a query and a filter. If a search request contains only a query part, facets are calculated based on the complete search result. In other words if a search result contains 10 records with the tag "Pet Friendly" and 5 records with the tag "No Pets Allowed", the facet response will contain two facets: "Pet Friendly" and "No Pets Allowed". Now let's assume a user limits results by selecting the "Pet Friendly" tag. If the "Pet Friendly" clause is added to the query part of the request, the search result will be limited to 10 records with the "Pet Friendly" tag, and only one facet will be returned: "Pet Friendly". However, if the "Pet Friendly" clause is added as a filter, the search result will be still limited to 10 records, but two facets will be returned. It happens because facets are calculated based only on the query portion of the search request and query portion didn't change - it still produces search results with 15 records with two different facets.
To answer your question, if a query returns no results (for example, user selected both "Pet Friendly" and "No Pets Allowed" tags) then results have no facets in them, so no facets are returned.

Resources