Pagination in acts_as_ferret


#1

I’m just wondering where I would put the pagination for search results
when using “acts_as_ferret”.

At the moment my search code is…

def search
@query = params[:query] || ‘’
unless @query.blank?
@results = Tutorial.find_by_contents @query
end
end

Cheers
SchmakO


#2

Hi!

On Mon, May 01, 2006 at 08:55:22AM +0200, SchmakO wrote:

end
find_by_contents has two options suitable for paging:
:first_doc (first result to retrieve) and
:num_docs (number of results to retrieve).

so to retrieve results 10 to 20, you would use
@results =
Tutorial.find_by_contents(@query,:first_doc=>10,:num_docs=>10)

hth,
Jens


webit! Gesellschaft für neue Medien mbH www.webit.de
Dipl.-Wirtschaftsingenieur Jens Krämer removed_email_address@domain.invalid
Schnorrstraße 76 Tel +49 351 46766 0
D-01069 Dresden Fax +49 351 46766 66


#3

Nice!

This is very useful!

What code did you snip out of the serach method? Is that required?

Thanks.

Tom D. wrote:

To add to what Jens said, you may find this code useful:

In your model:

def self.search(q, options = {})
return nil if q.nil?
default_options = {:limit => 10, :page => 1}
options = default_options.merge options
options[:offset] = options[:limit] * (options[:page].to_i-1)
… snip …
num = INDEX.search_each(query, {:num_docs => options[:limit],
:first_doc => options[:offset]}) do |doc, score|
… snip …
[num, results]
end

Notice that I return the total matches as num, plus the results. The
total matches is necessary to generate a paginator across all the
items.

For the pagination, I created this simple method in my application
controller (note it assumes a params[:page] being passed around):

def pages_for(size, options = {})
default_options = {:per_page => 10}
options = default_options.merge options
pages = Paginator.new self, size, options[:per_page],
(params[:page]||1)
pages
end

And lastly, to use it in a controller:
@total, @results = YourModel.search(@query, :page =>
(params[:page]||1)
@result_pages = pages_for(@total)

Tom

On 5/3/06, Jens K. removed_email_address@domain.invalid wrote:

unless @query.blank?

Ferret-talk mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/ferret-talk


Tom D.

http://blog.atomgiant.com
http://gifthat.com


#4

I think the simplest way to attack this problem is just to write a
paginator for an array of objects. Then you can just do your usual
find_by_contents, with num_docs set to :all, and then just paginate the
array that is returned.

Tom D. wrote:

To add to what Jens said, you may find this code useful:

In your model:

def self.search(q, options = {})
return nil if q.nil?
default_options = {:limit => 10, :page => 1}
options = default_options.merge options
options[:offset] = options[:limit] * (options[:page].to_i-1)
… snip …
num = INDEX.search_each(query, {:num_docs => options[:limit],
:first_doc => options[:offset]}) do |doc, score|
… snip …
[num, results]
end

Notice that I return the total matches as num, plus the results. The
total matches is necessary to generate a paginator across all the
items.

For the pagination, I created this simple method in my application
controller (note it assumes a params[:page] being passed around):

def pages_for(size, options = {})
default_options = {:per_page => 10}
options = default_options.merge options
pages = Paginator.new self, size, options[:per_page],
(params[:page]||1)
pages
end

And lastly, to use it in a controller:
@total, @results = YourModel.search(@query, :page =>
(params[:page]||1)
@result_pages = pages_for(@total)

Tom

On 5/3/06, Jens K. removed_email_address@domain.invalid wrote:

unless @query.blank?

Ferret-talk mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/ferret-talk


Tom D.

http://blog.atomgiant.com
http://gifthat.com


#5

To add to what Jens said, you may find this code useful:

In your model:

def self.search(q, options = {})
return nil if q.nil?
default_options = {:limit => 10, :page => 1}
options = default_options.merge options
options[:offset] = options[:limit] * (options[:page].to_i-1)
… snip …
num = INDEX.search_each(query, {:num_docs => options[:limit],
:first_doc => options[:offset]}) do |doc, score|
… snip …
[num, results]
end

Notice that I return the total matches as num, plus the results. The
total matches is necessary to generate a paginator across all the
items.

For the pagination, I created this simple method in my application
controller (note it assumes a params[:page] being passed around):

def pages_for(size, options = {})
default_options = {:per_page => 10}
options = default_options.merge options
pages = Paginator.new self, size, options[:per_page],
(params[:page]||1)
pages
end

And lastly, to use it in a controller:
@total, @results = YourModel.search(@query, :page =>
(params[:page]||1)
@result_pages = pages_for(@total)

Tom

On 5/3/06, Jens K. removed_email_address@domain.invalid wrote:

unless @query.blank?

Ferret-talk mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/ferret-talk


Tom D.

http://blog.atomgiant.com
http://gifthat.com


#6

The snipped out portion is just where you process the results. In my
case, I am searching for gifts so here is how I search and collect the
Gift objects:

num = INDEX.search_each(query, {:num_docs => options[:limit],

:first_doc => options[:offset]}) do |doc, score|
logger.debug(“Found doc: #{doc}, id: #{INDEX[doc][‘id’]}, score:
#{score}”)
gifts << Gift.find(INDEX[doc][‘id’])
end

Also, the reason I defined my own search method as opposed to using
find_by_contents is because I am not using the acts_as_ferret plugin.

From looking at the acts_as_ferret code, it looks like you can just
use the SearchResults object returned by the find_by_contents since it
also includes the total hits needed to create the Paginator pages.

Tom

On 7/5/06, guest removed_email_address@domain.invalid wrote:

Tom D. wrote:

num = INDEX.search_each(query, {:num_docs => options[:limit],

controller (note it assumes a params[:page] being passed around):
@total, @results = YourModel.search(@query, :page =>

http://rubyforge.org/mailman/listinfo/ferret-talk

Posted via http://www.ruby-forum.com/.


Ferret-talk mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/ferret-talk


Tom D.

http://atomgiant.com
http://gifthat.com


#7

Oh, one more thing, I just looked and the find_by_contents didn’t
support returning the total results when I wrote my original
pagination post :slight_smile:

Also, I don’t think the current acts_as_ferret release supports it
either (but the trunk does), so you may have to grab the trunk if you
want to paginate in pure acts_as_ferret land.

Tom

On 7/7/06, Tom D. removed_email_address@domain.invalid wrote:

Nice!

... snip ...

def pages_for(size, options = {})
@result_pages = pages_for(@total)


http://gifthat.com


Tom D.

http://atomgiant.com
http://gifthat.com


#8

Hello Jens, just a quick question about your code…

if i am using this:

@test =
County.find_by_contents(@params[‘search_string’],:first_doc=>0,:num_docs=>10)

do i just setup up numbered links in my view to pass a variable to
first_doc? so if i wanted to get the next set, id do:

@test =
County.find_by_contents(@params[‘search_string’],:first_doc=>@params[‘10’],
:num_docs=>10)

if this is the case, is there an easier way and also a way to count how
many sets or how many pages there are in a table for a given query?
would i just get a total from the array and then divide by 10 docs?
etc…?

thanks!

Jens K. wrote:

Hi!

On Mon, May 01, 2006 at 08:55:22AM +0200, SchmakO wrote:

end
find_by_contents has two options suitable for paging:
:first_doc (first result to retrieve) and
:num_docs (number of results to retrieve).

so to retrieve results 10 to 20, you would use
@results =
Tutorial.find_by_contents(@query,:first_doc=>10,:num_docs=>10)

hth,
Jens


webit! Gesellschaft f�r neue Medien mbH www.webit.de
Dipl.-Wirtschaftsingenieur Jens Kr�mer removed_email_address@domain.invalid
Schnorrstra�e 76 Tel +49 351 46766 0
D-01069 Dresden Fax +49 351 46766 66


#9

I guess that’s sort of what you did, but I don’t think you need to
define the self.search method. find_by_contents works just fine.

Mike Michelson wrote:

I think the simplest way to attack this problem is just to write a
paginator for an array of objects. Then you can just do your usual
find_by_contents, with num_docs set to :all, and then just paginate the
array that is returned.


#10

On 10/17/06, koloa removed_email_address@domain.invalid wrote:

Hello Jens, just a quick question about your code…

if i am using this:

@test =
County.find_by_contents(@params[‘search_string’],:first_doc=>0,:num_docs=>10)

For starters, :first_doc is now :offset and :num_docs is now :limit,
as of Ferret 0.10.0.

do i just setup up numbered links in my view to pass a variable to
first_doc? so if i wanted to get the next set, id do:

@test =
County.find_by_contents(@params[‘search_string’],:first_doc=>@params[‘10’],
:num_docs=>10)

Strange parameter name but yes, that’s how you’d do it.

if this is the case, is there an easier way and also a way to count how
many sets or how many pages there are in a table for a given query?
would i just get a total from the array and then divide by 10 docs?
etc…?

I’m not sure about easier way but you can get the total number of
matching hits from @test.total_hits. On the other hand, @test.size
will be the number of hits returned.

Hope that answers your question,
Dave


#11

On Tue, Oct 17, 2006 at 09:30:50AM +0900, David B. wrote:

On 10/17/06, koloa removed_email_address@domain.invalid wrote:
[…]

@test =
County.find_by_contents(@params[‘search_string’],:first_doc=>0,:num_docs=>10)

For starters, :first_doc is now :offset and :num_docs is now :limit,
as of Ferret 0.10.0.

right, though aaf still works with the old naming, too :wink:

[…]

if this is the case, is there an easier way and also a way to count how
many sets or how many pages there are in a table for a given query?
would i just get a total from the array and then divide by 10 docs?
etc…?

I’m not sure about easier way but you can get the total number of
matching hits from @test.total_hits. On the other hand, @test.size
will be the number of hits returned.

exactly. I didn’t try this, but it should be possible to use a Rails
Paginator to handle the whole pagination stuff.

Jens


webit! Gesellschaft für neue Medien mbH www.webit.de
Dipl.-Wirtschaftsingenieur Jens Krämer removed_email_address@domain.invalid
Schnorrstraße 76 Tel +49 351 46766 0
D-01069 Dresden Fax +49 351 46766 66


#12

hey guys, any idea how to use those options with multi_search

I tried it on find_by_contents and it works fine, however, for
multi_search i do:

@results =
User.multi_search(parse(@query),[Book],{:offset=>0,:limit=>5})

or

@results = User.multi_search(parse(@query),[Book],:offset=>0,:limit=>5)

and neither works, however I get no error either. Whats wrong?


#13

http://blog.zmok.net/articles/2006/10/18/full-text-search-in-ruby-on-rails-3-ferret

enjoy