Group Solr results by class - ruby-on-rails

I'm searching two models with Solr like so:
#query = Sunspot.search Location, Employee do
with(:category_id, params[:category_id]) if params[:category_id].present?
fulltext params[:search]
facet :category_id
order_by(:weight, :desc)
paginate :page => params[:page], :per_page => 10
end
I would like to create two discrete objects from the results, containing matches from both models so I can split them up in the UI.
I'm currently doing:
#results = #query.results.group_by(&:class)
#locations = #results[Location]
#employees = #results[Employee]
but that's not working. #locations and #employees are both empty. What am I missing here? If i debug #results, I see matches and they're already grouped by model. I just want to expose that to my view so I can split up results into hideable/showable containers. Thoughts?

I don't have solr, so I can't assume exactly what #results looks like (I assume it's a hash).
With that assumption, the hash keys are either Symbols or Strings.
So try
#locations = #results[:Location]
#employees = #results[:Employee]
or
#locations = #results["Location"]
#employees = #results["Employee"]
One of the two of those (probably the first) should give you the information in each instance variable. The reason is that ruby will set the hash keys of class names as symbols. If the names were already strings, the keys will be strings.

Related

Rails - Combining will_paginate and ransack

I am using Ransack for Filtering and will_paginate for Pagination and it seems I have a problem combining both.
Analyzing memory problems of my app I saw that my Category controller was loading all entries from the database at each call.
def index
#q = Category.all_cached.paginate(:page => params[:page], :per_page => 30).ransack(params[:q])
#categories = #q.result(distinct: true)
end
In order to get the only the first entries for each page I tried the following change:
def index
#q = Category.paginate(:page => params[:page], :per_page => 30).ransack(params[:q])
#categories = #q.result(distinct: true)
end
Problem is that now the pagination is not working any longer.
Error says: Couldn't find Category with 'id'=x even though enough records are available.
I have two questions:
How can I avoid loading all entries at first call but instead per
page
As I also retrieve information about different products in
the category and I want to avoid n+1 calls, how can I add this
[includes(:product)] into the Category Controller?
Try this way:
def index
#q = Category.paginate(page: params[:page], per_page: 30).ransack(params[:q])
#categories = #q.result(distinct: true).includes(:products)
end

Will_Paginate Gem With Rails Displaying Results In Wrong Order

Attempting to sort an entry by an associated field, unfortunately when paginating with Will Paginate the sorting is wrong.
#categories = Category.includes(:posts)
.page(params[:page])
.order("posts.post_at DESC")
I have tried many variations, but unfortunately will paginate does not seem to respect the sort order of categories. Without the pagination the categories are sorted correctly.
How can I get Will Paginate to respect the order?
The end goal is Categories sorted by posts.post_at and paginated while maintaining that order.
Thanks.
Try
#categories = Category.includes(:posts).order("posts.post_at DESC").paginate(:page => params[:page], :per_page => 10)
Sorry can't comment due to low rep, but what are you trying to sort by and what do you want listed?
If you want categories in a certain order with the associated posts, is that by alphabetical?
#categories = Category.includes(:posts).order('categories.name??').page(params[:page])
Category with most recent posts?
#categories = Category.includes(:posts).order("posts.post_at DESC").page(params[:page])
Or are you wanting the most recent posts with the categories listed along with it?
#posts = Post.includes(:category).order('posts.post_at DESC').page(params[:page])
It's unclear what your end goal is with this query.

How can I order when using pagination?

This might be quite simple question.
Here's my code. Then I want to sort #codes ordered by the column code.user.last_active_at
#here, I retrieve only the records that contains particular keyword.
#search = Code.search do
fulltext params[:search]
with(:community_id, community_search)
paginate :page => 1, :per_page => 1000000
end
#Here, I try pagination. 10 records per each page.
#codes = #search.results
#codes = Kaminari.paginate_array(#codes).page(params[:page]).per(10)
I want to sort them ordered by the column #code.user.last_active_at
How can I do that?
I tried this
#codes = Kaminari.paginate_array(#codes.joins(:user).order('user.last_active_at DESC')).page(params[:page]).per(10)
But it returned this error
NoMethodError (undefined method `joins' for #
<Sunspot::Search::PaginatedCollection:0x0000001ca851a8>):
app/controllers/codes_controller.rb:95:in `index'
How can I sort while I'm using Kaminari pagination?
Kaminari not affect on sorting. first you have to sort and then get page(x).per(x)
so for example in your controller
#codes = #search.results
#codes = #codes.joins(:user).order('user.last_active_at DESC').page(params[:page]).per(10)
edit:
after check sunspot documentation I think you can make something like
#search = Code.search(include: :user) do
fulltext params[:search]
with(:community_id, community_search)
order_by('user.last_active_at DESC')
#paginate :page => params[:page], :per_page => 10 you can add this and not use Kaminari
end
you can try joins except include

Find in ActiveRecord object

I have a page where I can either display all customers' details, or if specified in the params, just one customer's details.
My controller looks something like this.
def display_customers
#all_customers = Customer.all
if params[:customer_id]
#customers = Customer.find(:all, conditions: ["id = ?", params[:customer_id]])
else
#customers = #all_customers
end
end
I use #all_customers to populate a dropdown of customers.
I use #customers to perform an each loop through each customer.
Then, if the customer_id param is specified, #customers will just be the single customer.
This works just fine but #customers = Customer.find(...) is an extra DB query.
I already have all the customers in #all_customers so I though there would be a better way to grab the one record I need from there - instead of going back to the DB again.
You can use the find which is built in to all Enumerables...
if params[:customer_id]
#customer = #all_customers.find { |c| c.id == params[:customer_id] }
else
#...
... but probably shouldn't.
Depending on the number of records, this may not be any faster, as your database query will use an index, where the above code uses a linear search through all the returned records. Chances are a second database query is the way to go.
You should also rewrite your query. Rails is smart enough that you don't have to pass conditions when finding a record by its ID; you just find it. These are identical:
# BAD:
#customers = Customer.find(:all, conditions: ["id = ?", params[:customer_id]])
# BETTER:
#customers = Customer.find_by_id(params[:customer_id])
# BEST:
#customer = Customer.find(params[:customer_id])
If you really want it to be an array, just wrap the result in [].
How about this
def display_customers
#all_customers = []
if params[:customer_id].empty?
#all_customers = Customer.all
else
#all_customers << Customer.find(params[customer_id])
end
#all_customers
end
this way
your "select * from customers" query will only be executed when there is no customer_id param (not always). I believe its more efficient rather than selecting all from DB and then filtering

rails tire elasticsearch weird error

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

Resources