rails tire elasticsearch weird error - ruby-on-rails

I have indexed a Car model with one car record mercedes benz in the database. If I search for the word benz I get an error:
ActiveRecord::RecordNotFound in CarsController#index
Couldn't find all Cars with IDs (1, 3) (found 1 results, but was looking for 2)
If I search for hello I get:
Couldn't find Car with id=2
Other random search terms work returning accurate results.
So it's basically random errors generated by random search terms. What could be the cause of this?
Controller:
def index
if params[:query].present?
#cars = Car.search(params)
else
#cars = Car.paginate(:page => params[:page], :per_page => 10)
end
end
Model:
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 10) do |s|
s.query { string params[:query]} if params[:query].present?
end
end

This happens because, you are using the load => true option to load the search results from database. The activerecord seems to be missing in DB, but the elasticsearch index contains the same document.
Reindex is not always the solution IMHO.
The best solution is to delete the document when it is deleted in db. You can use the after_destroy callback for this.
Tire remove api is used to remove a single document from index.
Tire.index('my-index-name').remove('my-document-type', 'my-document-id')
Reference: https://github.com/karmi/tire/issues/43

Related

Rails query where associated (joined) 'rich_text' is not nil/empty

I have the following right now, which works successfully to produce a list of users, with their rich_text_tlk_with_me 'rich_text' attachments.
User.joins(:rich_text_tlk_with_me).paginate(page: params[:page], per_page: 5).order(created_at: :desc)
However, right now this query is also displaying users who have empty rich_text attachments.
I would like to run a query which only returns the users that have a non empty rich_text attachment.
I have tried:
User.joins(:rich_text_tlk_with_me).where.not(rich_text_tlk_with_me: [nil, ""]).paginate(page: params[:page], per_page: 5).order(created_at: :desc)
But this did not return anything.
I have also searched for documentation, but not found anything relevant.
Assuming user has_many :rich_text_tlk_with_mes and you are using Rails version > 5.
You can get all users who have a associated rich_text_tlk_with_me by the following query.
User.left_outer_joins(:rich_text_tlk_with_mes).where.not(rich_text_tlk_with_mes: {id: nil}).paginate(page: params[:page], per_page: 5).order(created_at: :desc)

Sunspot solr doesn't indexing all models

I have a model Post. Also I have STI model Review. Here is search config for Post:
searchable do
text :title, :content
text :username do
user.try(:username)
end
text :user_full_name do
user.try(:full_name_with_username)
end
text :user_full_name_with_username do
user.try(:full_name_with_username)
end
end
The problem is, that not all models were indexed even if I add certain model through Sunspot.index(Review.find(id)) and Sunspot.commit
After indexing, I trying to find some reviews by username :
reviews_ids = Review.search do
fulltext params[:titles_search] do
fields(:username)
end
end.results.map(&:id)
and there are not all reviews in results.
What could it be? How to debug it?
The search results from Solr are always paginated so I suspect that you're not seeing all the results from your search. By default, I believe the results are paginated to 20 results per page.
To see all the search results at once you could do something like:
search = Review.search do
fulltext params[:titles_search] do
fields(:username)
end
paginate page: 1, per_page: Review.count
end
This sets the "number of elements per page" equal to the number of total Reviews. So this should allow you to see all the search results without paging. Obviously if you have a large number of Review objects, this is going to be a huge memory hog.
A better way to do it is to work with the pagination behavior, so if you rewrite your search like this:
def search_reviews(opts = {})
options = {
page: nil,
per_page: 20,
}.merge(opts)
Review.search do
fulltext params[:titles_search] do
fields(:username)
end
paginate page: options[:page] if options[:page].present?
paginate per_page: options[:per_page] if options[:per_page].present?
end.results
end
You can call it successively with different page numbers to get all your results.

Sorting 2 colums in a table in Ruby on Rails using haml

I am kind of new at rails, currently using version 3.23 and I am trying to enable 'sort' on two columns in my table. I managed to create the links on these column headers and actually got one column working/sorting!But couldnt achieve the same result when I modified my code in the movie controller rb, my code can only work for one column! is:
def index
#movies = Movie.all.sort_by { |movie| movie.title }
end
Works perfectly, but when I combine another parameter i.e. release date I get an error!
def index
#movies = Movie.all.sort_by { |movie| movie.title } then { |release date| release.date}
end
Can someone please help me resolve this issue? I have researched it on google but I've gotten nothing conclusive!. Any help will be most appreciated.
Assuming that you have to sort on title and release_date fields in movies table.
You can perform the sorting at database level itself as below:
In Rails 4.x:
Below will sort all the movie records with title and release_date in ascending order(as default).
def index
#movies = Movie.order(:title, :release_date)
end
If you want to change the order, you can specify as asc or desc as:
def index
#movies = Movie.order(title: :asc, release_date: :desc)
end
In Rails 3.x:
def index
#movies = Movie.all(:order => "title ASC, release_date ASC")
end
If you want to change the order, you can specify as DESC in the above case.
You can order your listing by this
def index
#movies = Movie.order(title: :asc, release_data: :desc)
end

tire elasticsearch kaminari pagination with overriden search in active record model

How to paginate results optimally when using a custom search method in model. I've also pushed ordering of the results to elasticsearch and after fetching from db the results are again sorted based on elasticsearch order.
My model search method looks like this:
def self.search query
model_objs = Model.tire.search do
query do
boolean do
should { string "field:#{query}", boost: 10}
should { string "#{query}"}
//other boolean queries
end
end
sort do
by "fieldname"
end
end
ids = model_objs.results.map {|x| x.id.to_i}
model_objs = Model.find(ids)
ids.collect {|id| model_objs.detect {|x| x.id == id}}
end
And in my controller I just have an action to get the results.
def search
search_term = params[:search_term].strip
#model_objs = Model.search search_term
end
I have two goals here, first I want to optimize the number of calls going to elasticsearch or to my database. And I want to paginate the results.
The default pagination mentioned in tire does not work cause I've overridden my search method.
#articles = Article.search params[:q], :page => (params[:page] || 1)
Also using the approach of getting paginated results from elastic search using the from and size would mean I make calls to elasticsearch over and over to fetch results, so I dont want to do something like this.
def self.search query, page_num
model_objs = Model.tire.search do
query do
boolean do
should { string "field:#{query}", boost: 10}
should { string "#{query}"}
//other boolean queries
end
end
sort do
by "fieldname"
end
size 10
from (page_num - 1) * 10
end
ids = model_objs.results.map {|x| x.id.to_i}
model_objs = Model.find(ids)
ids.collect {|id| model_objs.detect {|x| x.id == id}}
end
How can I achieve this with limited network calls?
you can pass page parameter to elastic search
model_objs = Model.tire.search({page: page_num}) do
query do
boolean do
should { string "field:#{query}", boost: 10}
should { string "#{query}"}
//other boolean queries
end
end
no need to pass add 'from'.
Disclaimer: I work with the OP, and this is just to post the solution we took.
Instead of fetching part of the attributes from ES and fetching the rest from the database, the Elastic Search index can be made to hold all the fields that's required for displaying the search results.
This way, query call to elasticsearch is good to display the search result page, without making any extra network calls.
The actual detailed view of the entity can fetch all that is required, but that would be only per entity, which works for us.

Solr (Sunspot) query time boost on non keyword searches

Given that I want to find 20 relevant results how would I go about boosting the first criteria inside any_of (with(:id).any_of(co_author_ids)) so that if there are 20 results which match said criteria it will return as opposed to trying to match based on the second criteria?
#solr_search = User.solr_search do
paginate(:per_page => 20)
with(:has_email, true)
any_of do
with(:id).any_of(co_author_ids)
with(:hospitals_id).any_of(hopital_ids)
end
end
Initially I didn't think boosting was necessary as I figured any_of would have a cascading effect but it does not appear to work like that. I know who to do query time boosting on keywords and fulltext searches but have been unable to get it working with with() methods.
since co_author_ids is a multivalued key , i have enough reasons to believe that there is no way to achieve that. although with single value keys it is possible to achive this cascading effect by using solr sort using function query. http://wiki.apache.org/solr/FunctionQuery#Sort_By_Function aong with the adjust_solr-params http://sunspot.github.io/docs/Sunspot/DSL/Adjustable.html
Example:
suppose you have query like this:
#solr_search = User.solr_search do
paginate(:per_page => 20)
with(:has_email, true)
any_of do
with(:id,author_id) #assuming author id is a solr index
with(:hospitals_id).any_of(hopital_ids)
end
end
and now in this case you want to have a cascading effect and want to give more preference to exact matches with author_id you can do this way
#solr_search = User.solr_search do
paginate(:per_page => 20)
with(:has_email, true)
any_of do
with(:id,author_id) #assuming author id is a solr index
with(:hospitals_id).any_of(hopital_ids)
end
adjust_solr_params do |p|
p["sort"] = "if(author_id_i = #{id},1,0) desc" #note author_id_i solr eq of author_id
end
end
so this will sort on the basis of the value of if(author_id_i = #{id},1,0) and in return will put all the records with auhtor_id as same of the user on top.
i somehow was getting problems using IF function so i instead used (practicaaly both of them are same):
#solr_search = User.solr_search do
paginate(:per_page => 20)
with(:has_email, true)
any_of do
with(:id,author_id) #assuming author id is a solr index
with(:hospitals_id).any_of(hopital_ids)
end
adjust_solr_params do |p|
p[:sort] = "min(abs(sub(author_id_i,#{id})),1) asc"
end
end
i stumbled upon this also http://wiki.apache.org/solr/SpatialSearch while looking for a solution for this and if you want to sort by distance you can do something like:
#solr_search = User.solr_search do
paginate(:per_page => 20)
with(:has_email, true)
any_of do
with(:id,author_id) #assuming author id is a solr index
with(:hospitals_id).any_of(hopital_ids)
end
adjust_solr_params do |p|
p[:pt] = "#{latitude_of_your_interest},#{longitude_of_your_interest}"
p[:sfield] = :author_location #your solr index which stores location of the author
p[:sort] = "geodist() asc"
end
end
overall i would say you can do a lot of cool things with p["sort"] but in this particular case it cant be done( imho) because it being a multivalued field
ex:
Using multivalued field in map function
Solr function query that operates on count of multivalued field
I wish they could just provide a include function for multivalued field and we can just write
p["sort"] ="if(include(co_authors_ids,#{id}), 1, 0) desc"
but as of now its not possible(again imho).

Resources