I have an Autocomplete class with a results method that queries two different models to return data. It partially works great, but with one major issue.
def results
Searchkick.search #query, index_name: [Location, DiveCenter]
end
I need to limit the hits returned from the index DiveCenter because of the way I have the index set up. It's set up this way because a user will either click on a DiveCenter and be taken to the DiveCenter show page OR click on a location and be taken to /search?(city | country)=value.
searchkick searchable: [:name, :city, :state, :country]
def search_data
{
name: name,
city: location.city,
state: location.state,
country: location.country
}
end
So basically, what I need is this:
def results
Searchkick.search #query, index_name: [Location, DiveCenter], fields: ['location.city', 'location.state', 'location.country', 'dive_center.name']
end
Elasticsearch doesn't provide a way to limit results from a single index.
One approach is to filter the results in Ruby.
Another approach is to use multi search to limit the results for dive centers.
Related
I'm trying to sort my ES results by 2 fields: searchable and year.
The mapping in my Rails app:
# mapping
def as_indexed_json(options={})
as_json(only: [:id, :searchable, :year])
end
settings index: { number_of_shards: 5, number_of_replicas: 1 } do
mapping do
indexes :id, index: :not_analyzed
indexes :searchable
indexes :year
end
end
The query:
#records = Wine.search(query: {match: {searchable: {query:params[:search], fuzziness:2, prefix_length:1}}}, sort: {_score: {order: :desc}, year: {order: :desc}}, size:100)
The interesting thing in the query:
sort: {_score: {order: :desc}, year: {order: :desc}}
I think the query is working well with the 2 sort params.
My problem is the score is not the same for 2 documents with the same name (searchable field).
For example, I'm searching for "winery":
You can see a very different score, even if the searchable field is the same. I think the issue is due to the ID field (it's an UUID in fact). Looks like this ID field influences the score.
But in my schema mapping, I wrote that ID should not be analyzed and in my ES query, I ask to search ONLY in "searchable" field, not in ID too.
What did I miss to math the same score for same fields ? (actually, sorting by year after score is not useful cos' scores are different for same fields)
Scores are different, because they are calculated independently for each shard. See here for more info.
I'm building a search using Algolia rails but having difficulties when doing sort by dynamically. For example, user can choose from a dropdown to sort by price asc or price desc.
Here is my model where I defined the indices
class Product < ActiveRecord::Base
include AlgoliaSearch
algoliasearch per_environment: true, if: :publishable? do
attribute :id, :name, :description, :seller_id, :condition, :location, :slug, :status, :city, :state, :stock_quantity,
:shipping_method, :price
attribute :created_at_i do
created_at.to_i
end
attribute :updated_at_i do
updated_at.to_i
end
attribute :price do
price.to_f
end
attributesToIndex ['name', 'unordered(description)', 'seller_id',
'condition', 'location', 'slug', 'created_at', 'updated_at', 'status',
'geo(city)', 'geo(state)']
geoloc :latitude, :longitude
numericAttributesToIndex ["price", "stock_quantity"]
attributesForFaceting ['price', 'condition', 'shipping_method']
end
and the products controller
def index
queries = { hitsPerPage: 5, page: params[:page].to_i, facets: '*',
facetFilters: [
"condition: #{params[:condition]}",
"shipping_method: #{params[:shipping_method]}"
],
numericFilters: [
"price:#{params[:min_price] || 0} to #{params[:max_price] || 999999999999999}"
],
sortBy: ["asc(price)"]
}
if latLng.present?
queries[:aroundLatLng] = latLng
queries[:aroundRadius] = radius
end
#response = Product.search(params[:query],queries)
Algolia returns error "invalid parameter sortBy". I tried to search on algolia documentation but couldn't find any information.
Thanks for helping.
Here is a list of relevant documentation pages from Algolia's website which will help you understand how Algolia's ranking work:
[Getting started] Tweak Ranking and Relevance
The reason our engine is so fast is because each index has its own settings and rankings. This means you are able to create different sets of searchable attributes and attribute ranking relevance by storing your data in multiple indices. This is accomplished using slave indices which are seamlessly synchronized with a master index. Each slave index can then be configured with its own set of business metrics to tune the relevance calculation.
An index has a specific ranking formula that can't be changed. You can however easily overcome this limitation by using slave indices with a different ranking formula.
[Tutorials][Ranking Formula] Tie Breaking algorithm
[FAQ] How does Algolia's tie breaking algorithm work?
These two links will help you understand how the ranking with Algolia work and how you can tweak it. Basically, depending on your use-case, sorting by price before text relevance doesn't really make sense. So, depending on your use case, you might just want to change your customRanking to price, or you might want to add your price attribute at the top of your ranking formula.
[FAQ] What are slave indices and what are their benefits?
This last link explain in more depth what a "slave index" is for Algolia.
[Rails documentation] Multiple sort criteria
[Rails GitHub documentation] Master/Slave
On these last links, you'll find code examples using Algolia's Rails client with multiple slaves. The first one actually showcases exactly your use case: sorting by price.
With all of this in mind, in the end, you're just looking to add these in your model (if you want to use the customRanking way):
add_slave 'Product_by_price_asc', per_environment: true do
customRanking ['asc(price)']
end
add_slave 'Product_by_price_desc', per_environment: true do
customRanking ['desc(price)']
end
Then in your controller, you can query them this way
query_params = { slave: "Product_by_price_#{params[:sort_order]}", hitsPerPage: 5, ... }
Product.search params[:query], query_params
You should probably also do this implementation in the front-end to be able to fully use Algolia's instant search capabilities, see [FAQ] Searching from the front-end or the back-end.
I have implemented PgSearch on my Node model like so:
include PgSearch
pg_search_scope :node_search, against: [:name, :user_id, :circa],
using: { tsearch: { any_word: true} },
:associated_against => {
comments: [:message],
user: [:first_name, :last_name, :email],
memberships: [:relation]
}
And in my controller I have this:
if params[:search]
#nodes = Node.node_search(params[:search])
end
Ideally, what I would like to be able to do though, is have someone be able to type in the text representation (a flag) of one of the associations and have the search filter just on that flag.
E.g. say: "name: Bouncing Ball", where the search would take place just on the column called name on the nodes model. Aka...it would look for all the nodes with the name Bouncing Ball and not search other columns or models or even any of the associations.
Naturally, I would like to be able to do searches like:
owner: John Brown (which searches for all nodes whose owner/user first_name and last_name are John Brown), comment: Manhattan (which searches for all nodes that have a comment with the text Manhattan in the copy, and so on.
How do I achieve this with PgSearch?
Have you tried to use a combinations of "Dynamic search scopes" with some controller processing of the search string?
name: Bob, parse out the columns/relationship and the searching value then pass it to a pg_search_scope with a lambda block?
I have an object Foo which has many objects Bee.
class Foo
has_many :bees
I index my object A with Sunspot SOLR like this.
searchable do
text :title, boost: 5
text :content, boost: 2
integer :bee_ids, multiple: true
...
end
to keep track of Bee ids related to my Foo object.
Now I have a User that performs searches on Foos objects. The User has many Bees too.
class User
has_many :bees
...
end
When I search Foos objects I would like to boost objects which have bees that matches User's bees.
Foo.search do
fulltext query
any_of do
...
with(:bee_ids, #user.bees.pluck(:id))
end
end
I want to give priorities to objects that matches user's interests. Any idea?
Well. I found a solution for the problem above.
You need to use bq parameter to do that and give a boost to that condition.
I added
adjust_solr_params do |params|
params[:bq] = " bee_ids_im:(#{#user.bees.pluck(:id).join(' OR ')})^20"
end
and I give a boost to the above condition of 20 times.
Actually that the solution to the more generic problem of giving boost to specific conditions in the query.
Sunspot support boost queries with boost command.
So I changed my query with:
Foo.search do
fulltext query do
boost(20.0) do
with(:bee_ids, #user.bees.pluck(:id))
end
end
any_of do
...
with(:bee_ids, #user.bees.pluck(:id))
end
end
and I obtained the same result.
Hope this can help someone else.
def self.search(params)
return [] unless params[:query].present?
tire.search(load: true) do
query { string(params[:query], fields: %w(title description topics
username discussions)) }
sort do
by "likes", "desc"
by "badges_count", "desc"
end
facet :tags do
terms :tags
end
facet :topics do
terms :topics
end
size params[:size] || 5
end.results
end
I'm attempting to perform a search on a particular model. Although the results are currently sorting based on the most likes, and I'd like to base it more on a percentage basis for each column in the sort block.
for example:
50% for "likes" based on strength of semantic match in another column(:header)
20% for "badges_count" based on "badges_count"
Any help would be great as I am a bit stuck on how to expand the block more and create a mini algorithm to sort based by weight.
Index your columns with boost options.
ex:
class YourParticularModel
mapping do
indexes :likes, boost: 100
indexes :badges_count, boost: 40
# .... other indexes
end
end
Then remove sort from your search method. The query result will auto weight the likes and badges_count matches.