Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Set :total_entries < 0 for uncounted paging. #250

wants to merge 2 commits into from

6 participants


Pagination on really large data sets is very slow because of the count(*) requirement to find number of pages. This can be fixed by setting :total_entries to the correct size. However, if you do not know what that is supposed to be you are stuck approximating.

This patch makes it possible to have just previous/next links whenever there are pages behind/ahead by setting :total_entries to -1. I figure -1 is a pretty good value for "I have no idea how many there are, but I DON'T want you counting either!"

There is one caveat with this method. The last page may be blank if the next to last page has the last item and is full.


This is similar to #127. I'm thinking of supporting something like this. Counts suck sometimes


One way to fix the "last page being blank" issue is to fetch one more item than will actually be on the page. So if you are showing 10 per page, fetch 11. Then you can see if there's another page to display.


@mislav what do you think needs to happen to support this properly?


@joevandyk I don't like any of the implementations so far. They make the current arithmetic more convoluted, add no tests and seem poorly thought of.

An ideal solution would have a simple implementation and tests.


Count complete kills the performance of our database. The count alone takes 10 seconds on a complex SQL. We need a better way to just skip the count.


+1 This would be great as well. On many of our actions, the count query takes as long as the actual query for an action that usually only yields 1 page of items. If we could return a single page and skip the count, that would be a 100% performance improvement.

This can be done manually, but it would be great if will_paginate supported this out of the box!


Depending on what database engine you are using, simply supplying a field to count can make a huge difference.

A few years ago, on one database with many millions of records (admittedly it was a slow machine but that amplifies the difference), count(*) took several MINUTES to complete, yet count(id) returned almost immediately.

I would suggest using something like count(id) as a default, and make using other columns an option. It should always be an indexed column with no nulls. The primary key is the obvious choice here.

If somebody wants to use count(*) they can, but making the worst case (i.e., count(*)) the default is not the best idea, in my opinion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
6 lib/will_paginate/collection.rb 100644 → 100755
@@ -13,7 +13,9 @@ module WillPaginate
# This module provides few of these methods.
module CollectionMethods
def total_pages
- ? 1 : (total_entries / per_page.to_f).ceil
+ return 1 if
+ return -1 if total_entries < 0
+ (total_entries / per_page.to_f).ceil
# current_page - 1 or nil if there is no previous page
@@ -23,7 +25,7 @@ def previous_page
# current_page + 1 or nil if there is no next page
def next_page
- current_page < total_pages ? (current_page + 1) : nil
+ ((current_page < total_pages) || (total_pages < 0)) ? (current_page + 1) : nil
# Helper method that is true when someone tries to fetch a page with a
5 lib/will_paginate/view_helpers.rb 100644 → 100755
@@ -71,10 +71,13 @@ class << self
def will_paginate(collection, options = {})
# early exit if there is nothing to render
- return nil unless collection.total_pages > 1
+ return nil if collection.total_pages == 0 ||
+ (collection.current_page == 1 && collection.length < collection.per_page)
options = WillPaginate::ViewHelpers.pagination_options.merge(options)
+ options[:page_links] = false if collection.total_pages < 0
options[:previous_label] ||= will_paginate_translate(:previous_label) { '&#8592; Previous' }
options[:next_label] ||= will_paginate_translate(:next_label) { 'Next &#8594;' }
4 lib/will_paginate/view_helpers/link_renderer.rb 100644 → 100755
@@ -59,7 +59,9 @@ def previous_page
def next_page
- num = @collection.current_page < @collection.total_pages && @collection.current_page + 1
+ num = ((@collection.current_page < @collection.total_pages) ||
+ (@collection.total_pages < 0 && @collection.length >= @collection.per_page)) &&
+ @collection.current_page + 1
previous_or_next_page(num, @options[:next_label], 'next_page')
Something went wrong with that request. Please try again.