Tire not working with will_paginate in controller - ruby-on-rails

I'm currently searching two models (Posts and Channels), using Tire and ElasticSearch.
I need to add pagination using will_paginate. The issue is, Tire doesn't appear to support will_paginate in the controller (:page and :per_page don't work...). It turns out, to use pagination with Tire, you have to call it in your model.
The problem with calling Tire in my models is, if I do this, Tire won't let me search my two models (Posts and Channels) at the same time.
So... long story short... if I use Tire in the controller, I can't have pagination but I can search my two models at the same time. If I use Tire in my models, I can't search both of my models at the same time, but I can have pagination.
This just seems dumb. Is there a way around this?
For reference, here's the code from my controller that allows me to search both of my models at the same time:
def browse
#user = current_user
#search_items = Tire.search(['posts_index', 'channels_index'], load: true) do |search|
if params[:query].present?
search.query do |q|
q.string params[:query], default_operator: "AND"
end
end
search.filter :term, :visibility => ['public']
search.sort { by :created_at, "desc" }
end
#results = #search_items.results
end

Related

How to paginate multiple models with the Pagy gem in Rails?

I'm using Searchkick with the Pagy gem to paginate my search results and it works great if I'm only searching and paginating on one model, but I can't figure out how to do this with Pagy when I need to combine multiple models into one set of Searchkick search results.
I tried using Pagy's "array extra" to combine all the the individual Searchkick model queries into an array and then loop through the results in the view, but that did not return any search results.
Here's the controller code that works for a single model:
query = params[:q].presence || "*"
results = BlogPost.pagy_search(query, suggest: true, per_page: 20)
#pagy, #results = pagy_searchkick(results, items: 10)
And in the view:
<%= pagy_nav(#pagy).html_safe %>
The following works as a combined Searchkick query across multiple models, but it's not paginated:
#search_results = Searchkick.search(query, models: [PressRelease, BlogPost, User])
How am I supposed to paginate across multiple models, then? The docs for Pagy's "Array Extra" warns:
if the data in the array comes from some DB or other persisted storage
(i.e. not some in-memory storage), then you should definitely review
your code
That's exactly what I'm trying to do. Using Pagy to paginate Searchkick results from multiple models seems like something that should be possible. If you're not supposed to use a Pagy array to do this, then how are you supposed to do this?
You can put this in the pagy initializer:
Searchkick.extend Pagy::Searchkick
Then you can use it as usual:
results = Searchkick.pagy_search(query, models: [PressRelease, BlogPost, User])
#pagy, #results = pagy_searchkick(results, items: 10)
It is not documented, so you should probably create a Documentation Issue for the missing documentation.

Search Model and will_paginate fail

I'm new at the rails world, i am trying to implement will_paginate with my Search model. The Search model is responsible for searching products.
My problem is that i can't figure out how to make will_paginate works with this two models, because the products are displayed by the search model and will_paginate only deals with controllers, i have spent a couple of days trying to make this work but without luck :(
This is my Search.rb
class Search < ActiveRecord::Base
def search_products
products = Product.all
products = products.where(["modelo LIKE ?","%#{modelo}%"]).order(created_at: :desc) if modelo.present?
products = products.where(["preco >= ?",min_price]) if min_price.present?
products = products.where(["preco <= ?",max_price]) if max_price.present?
products = products.where(["troca LIKE ?","%#{troca}%"]) if troca.present?
products = products.where(["estado_id LIKE ?","%#{estado_id}%"])
return products
end
end
Search controller
def show
#search = Search.find(params[:id])
#estado = Estado.all
#products = #search.search_products.paginate(:page => params[:page], :per_page => 2).order('id DESC')
end
and search show
<%= will_paginate #products%>
I think that is missing something ;S Please guys, give me a north of how to solve this problem ;S
ps: To make the Search i followed the Railscast tutorial.

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.

Join tables when searching using Sunspot/Solr in Rails 3

I have an ActiveRecord model Products with associated Suppliers (via belongs_to/has_many association). I am using Sunspot for full-text searching. I make a search with that code:
#search = Products.search do
fulltext params[:search]
end
#products = #search.results
But I'd like to include suppliers too, so every time I call, for example,
#products.first.supplier
it wouldn't make a new request to the database. I tried to use
#search = Products.search(include: :supplier) do
but it didn't help. Is there any possible way to do that in Sunspot?
You can try this
#search = Sunspot.search[Products, Supplier] do
.....
end

How to filter results by multiple fields?

I am working on a survey application in ruby on rails and on the results page I want to let users filter the answers by a bunch of demographic questions I asked at the start of the survey.
For example I asked users what their gender and career was. So I was thinking of having dropdowns for gender and career. Both dropdowns would default to all but if a user selected female and marketer then my results page would so only answers from female marketers.
I think the right way of doing this is to use named_scopes where I have a named_scope for every one of my demographic questions, in this example gender and career, which would take in a sanitized value from the dropdown to use at the conditional but i'm unsure on how to dynamically create the named_scope chain since I have like 5 demographic questions and presumably some of them are going to be set to all.
You can chain named scopes together:
def index
#results = Results.scoped
#results = #results.gender(params[:gender]) unless params[:gender].blank?
#results = #results.career(params[:career]) unless params[:career].blank?
end
I prefer however to use the has_scope gem:
has_scope :gender
has_scope :career
def index
#results = apply_scopes(Results).all
end
If you use has_scope with inherited_resources, you don't even need to define the index action.
named_scope :gender,lambda { |*args|
unless args.first.blank?
{ :conditions => [ "gender = ?", args.first] }
end
}
If you write named scopes in this way, you can have all them chained, and if one of your params will be blank wont breaks.
Result.gender("Male") will return male results.
Result.gender("") will return male and female too.
And you can chain all of your methods like this. Finally as a filtering you can have like:
Result.age(16).gender("male").career("beginer")
Result.age(nil).gender("").career("advanced") - will return results with advanced career, etc.
Try some like this:
VistaFact.where( if active then {:field => :vista2} else {} end)
Or like this:
VistaFact.where(!data.blank? ? {:field=>data.strip} : {}).where(some? ? {:field2 => :data2} : {}).where ...
That work for me very nice!

Resources