Paginate Multiple Models in Kaminari
Asked Answered
H

3

22

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!

Highspirited answered 10/6, 2011 at 17:12 Comment(0)
M
1

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]
Mailbox answered 10/6, 2011 at 17:43 Comment(0)
G
52

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) %>');
Gosport answered 17/7, 2011 at 3:16 Comment(5)
Michael, thanks for the very nice, clear and concise answer. It would be very helpful if you could expand your answer with the necessary javascript to handle this situation. I'm a javascript neophyte, and am struggling mightily to try to get the Kaminari author's javascript example to work across multiple models.Stricklan
Great information here! Helped me solve the problem I was having.Natalyanataniel
@Don for a quick simple solution without javascript, just remove the ":remote => true".Sidetrack
@Michael K Madison any clues on whats been asked? i too struggle with the javascript part.Arin
@PetrosKyriakou hopefully my additional edit sheds a better light.Gosport
M
1

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]
Mailbox answered 10/6, 2011 at 17:43 Comment(0)
E
-1

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])
Exuberant answered 10/6, 2011 at 17:23 Comment(3)
Part of the benefit of gems like kaminari and paginate is that they use the SQL limit and offset clauses. If you solve the problem this way you loose that benefit!Mailbox
Ah, whoops. My bad I was going to suggest full text search since searching with AR feels like a ugly hack.Exuberant
@KevinSylvestre Have you found a good workaround, short of keeping all those models in a single table? Fancy seeing you here!Jumada

© 2022 - 2024 — McMap. All rights reserved.