I would like to use the kaminari gem to paginate over search results. Since the gem does not "pollute" ActiveRecord::Base and Array, kaminari is actually my preferred way to go (from an architectural point of view). Unfortunately there is Rails 3 and its lazy loading concept that makes things a little bit confusing (at least at my scenario).
In my HAML-Template I would like to list all of my search results. When the result set is empty, I want to display a message telling the user that no results have been found. In the past I could do the following:
- unless #results.empty?
... search results ...
- else
... no results found ...
= paginate #results
Since .empty? does not force loading this does obviously no longer work in Rails 3. Jep, I could use .all in my Query to force loading. Unfortunately .all returns an Array and kaminari's paginate View helper does not work with Arrays (the right philosophy imho). I am curious how you would handle this kind of situation. Any best practices? All of my attempts ended in ugly constructions (like the one below). :/
To make things a little bit clearer:
My Controller action:
helper_method :keys
def groups
#results = []
if params[:query].present?
#results = Group.public
.where({:body.matches_any => keys} | {:subject.matches_any => keys})
.order('groups.updated_at DESC')
.page(params[:page])
.per(1)
end
respond_to do |format|
format.html
end
end
My "ugly" HAML-Template:
%h1= "Search results for \"#{params[:query]}\""
%br
%ol
- #results.each do |group|
%li
= link_to group.subject, group
%br
= group.body
%br
= group.created_at
- if #results.empty?
%div No results found ...
- else
=paginate(#results)
The problem here: it creates an ol-tag although no results have been found.
Any Ideas?
Thanks in advance!
works for me:
- if #applications.present?
%ul
- #applications.each do |application|
%li= application.name
%p= paginate #applications
- else
%p No results found ...
with kaminari (0.12.3)
Related
My will_paginate code in controller:
#posts = Post.paginate(page: params[:page], per_page: 100).order('created_at DESC')
My pagy code that replaced it:
#pagy, #posts = pagy_countless(Post.order('created_at DESC'), items:5, link_extra: 'class="" style="color:#222222; margin:3px;"')
My view where the problem is:
<% #posts.where(user_id: current_user.friends).each do |post| %>
The error message:
undefined method `where' for #<Array:0x00007f94329d1f70>
Did you mean? when
What am I doing wrong? The previous will_paginate implementation worked in terms of showing all the posts of user's friends. I'm lost as to what changed in switching to pagy.
UPDATE:
So for the moment, I've solved this by moving the "where..." logic to controller. So:
#pagy, #posts = pagy_countless(Post.where(user_id: current_user.friends).order('created_at DESC'), items:5, link_extra: 'class="" style="color:#222222; margin:3px;"')
And now it works as before. But I'm still confused as to why I had to do this.
Be aware pagy_countless returns an array of items, instead of an ActiveRecord collection. That's why you can't use ActiveRecord methods on #posts (like where).
Follow my link to see that collection is transformed into array using to_a and then it is returned as second argument of pagy_countless: https://github.com/ddnexus/pagy/blob/bd88866ad6001ae1ef6ce63080d36ecff7bc5603/lib/pagy/extras/countless.rb#L29
Two comments:
1. You must use ActiveRecord filters before calling pagy_countless on your collection (eg: where clauses)
2. If you need to filter records after pagination (which does not make sense in most of the cases), you must use Array methods like select of reject as following #posts.select { |record| record.user.in?(current_user.friends) }
I am still a beginner in ruby on rails and I'm trying to create a simple search option for a website that I'm developing.
I have number of models such as Sundries, Curry and Starter.
My search works fine if I have one model but how do I search all models title or name?
I have added following method to each of the models and tried to display results in home page. but no luck!
def self.search(search)
where("LOWER(title) LIKE ?", "%#{search.downcase}%")
#where("content LIKE ?", "%#{search}%")
end
Can someone please help me figure this out with an example please?
Thank you.
You can add ActiveRecord_Relations together, and it will give you a simple array
#results = Sundries.search(search_term) + Curry.search(search_term) + Starter.search(search_term)
In the view...
<% #results.each do |result| %>
<%= "#{result.title} part of our fine #{result.class.name} choices" %>
<% end %>
Answering for anyone looking at this in 2019+... scenic is the gem you want to use (as long as your database is postgres).
There are several approaches how you can implement this. You can use advanced search engines such as ElasticSearch or Sphinx. Or you can use (for example) pg_search gem in case you are using Postgresql as your DataBase.
You can find RailsCasts for each of these approaches (some of them are probably for subscribers only).
As soon as you learning Rails, I would recommend to try out pg_search at first and avoid using search engines. Because it can be tricky sometimes for beginners. You can get to them later than you will have more data in your DB, or you would need to improve your skills. Try to make working solution in the first place and then improve it.
You can use this ransack to implement search functionality.
In your view side
<%= form_tag search_path, method: :get do %>
<%= text_field_tag :q, nil %>
<%= end %>
and in your controller action method,
q = params[:q]
#sunderies = Sundries.search(title_cont: q).result
#curry = Curry.search(title_cont: q).result
#starter = Starter.search(title_cont: q).result
#total_results = #sunderies + #curry + #starter
After headaches with ThinkingSphinx and Solr/Sunspot, we're trying out ElasticSearch and Tire as our search back-end - but I've hit a problem.
Here's my search command in the controller:
#results = Item.search params[:search], :page => ( params[:page] || 1 ), :per_page => 20
And this is the problem section of the view:
<%= page_entries_info #results %>
The error message I'm getting is
undefined method `offset' for #<Tire::Results::Collection:0xa3f01b0>
but only when there is more than one page's worth of results. If there are less than 20 items returned, then they get shown fine.
The only similar reported issue I could find elsewhere was solved by passing the :page and :per_page parameters into the search function, but I'm already doing that, to no avail.
Tire has a Pagination module but it doesn't define offset. You could file an issue with them to add it, but in the meantime you can monkeypatch it in your app:
Tire::Results::Pagination.module_eval do
def offset
(#options[:per_page] || #options[:size] || 10 ).to_i * (current_page - 1)
end
end
in my testapp, results are paginated just fine, with will_paginate 3.0 and tire 0.3. I wasn't aware will_paginate needed the offset method.
I've added it, however, copying over the "lint" test from will_paginate specs: https://github.com/karmi/tire/commit/e0e7730. Should be part of the next release.
I'm creating a search page that will do an application wide search on users, posts, and comments. I currently have:
# POST /search
def index
query = params[:query]
#users = User.search(query).page(params[:page])
#posts = Post.search(query).page(params[:page])
#comments = Comment.search(query).page(params[:page])
respond_to do |format|
format.html
end
end
However I'm really trying to get something where all the results are mixed together then paginated. What are some of the strategies for doing paginated search like this? Thanks!
Ever since this commit: https://github.com/amatsuda/kaminari/commit/f9f529fb68ab89feea38773a4c625c1b14859128
You can do the following
In your view you can do this:
<%= paginate #users, :remote => true, :param_name => "user_page" %>
<%= paginate #posts, :remote => true, :param_name => "post_page" %>
<%= paginate #comments, :remote => true, :param_name => "comment_#{some_post_id}_page" %>
and then in your controller you can refer to them in this way:
#users = User.search(query).page(params[:user_page])
#posts = Post.search(query).page(params[:post_page])
#comments = Comment.search(query).page(params[:comment_page])
and your view's js.erb you might have something like:
$('#posts').html('<%= escape_javascript render(#posts) %>');
$('.table-pager').html('<%= escape_javascript(paginate(#posts, :remote => true).to_s) %>');
Before thinking about a solution, you need to first define exactly what you want the final result to be. If you want to display a few of each type of record on the results page you can modify the approach you posted and combine the three paginated results using:
#results = #users + #posts + #comments
#results.sort! { |a, b| a.score(query) > b.score(query) }
Each object will need to have an instance method 'score' that will let it sort based on the query priority. Also, you will need to modify your view to handle correct rendering of each item and ensure that the pagination is called on the model with the most pages.
Alternatively, a more robust method would be to add a full-text search service (such as Index Tank, Web Solr, Thinking Sphinx). The technology for what's hot for these moves quickly, so do some research and find one that fits your needs. Example syntax for this would be something like:
User.multi_solr_search query, models: [Post, Comment]
You could combine the results from the query and run page on that.
users = User.search(query)
posts = Post.search(query)
comments = Comment.search(query)
#results = users + posts + comments
#results.page(params[:page])
Ok so I have decided to use Kaminari for pagination in a rails 3 project. I have followed the video from RailsCasts http://railscasts.com/episodes/254-pagination-with-kaminari
All goes well up until the point or running the server.
controllers/stories_controller.rb
def index
#stories = Story.all
#pages = Story.page(params[:page]).per(3)
#stories = Story.search(params[:search])
end
views/stories/index.html.erb
<%= paginate #pages %>
When i start the server the index page in question displays all the stories from the DB and renders the pagination view showing (1 2 Next > Last ยป). What am I missing to get the pagination working?
I still can not understand your code. Why do you assign Story.all to #stories in the 1st line and overwrite the variable in the 3rd line?
Anyways, #stories will display "all the stories from the DB" because you're not calling the pagination method (.per) on #stories. The pagination links will show you the paginated counts because you're calling per method on #page variable and passing it to the helper.
I mean, you need to call .per on the relation before passing it to <%= paginate %> helper.
It's quite simple.
I guess you want to get results from your search, right?
Try
#stories = Story.search(params[:search]).page(params[:page]).per(3)
and something like:
<% #stories.each do |story| %>
<%= render story %>
<% end %>
<%= paginate #stories %>
in your view