Skip to content

Commit

Permalink
reshuffled stuff around to add a django-style paginator to the projec…
Browse files Browse the repository at this point in the history
…t. courtesy of Dan Chudnov dchud@umich.edu

git-svn-id: http://solrpy.googlecode.com/svn/trunk@29 3a1802d4-3049-0410-b936-3fc652297a5e
  • Loading branch information
ed.summers committed Mar 11, 2009
1 parent 9a10424 commit 0f98a7f
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions solr/__init__.py
@@ -0,0 +1 @@
from core import *
File renamed without changes.
123 changes: 123 additions & 0 deletions solr/paginator.py
@@ -0,0 +1,123 @@
import math

class SolrPaginator:
"""
Create a Django like Paginator for a solr response object. Can be handy
when you want to hand off a Paginator and/or Page to a template to
display results, and provide links to next page, etc.
For example:
>>> from solr import SolrConnection
>>> from solr.paginator import SolrPaginator
>>>
>>> conn = SolrConnection('http://localhost:8083/solr')
>>> response = conn.query('title:huckleberry')
>>> paginator = SolrPaginator(response)
>>> print paginator.num_pages
>>> page = paginator.get_page(5)
For more details see Django Paginator documentation and solrpy unittests.
http://docs.djangoproject.com/en/dev/topics/pagination/
"""

def __init__(self, result):
self.params = result.header['params']
self.result = result
self.conn = result._connection

@property
def count(self):
return int(self.result.numFound)

@property
def num_pages(self):
if self.count == 0:
return 0
real_num = (self.count * 1.0) / len(self.result.results)
return int(math.ceil(real_num))

@property
def page_range(self):
"""List the index numbers of the available result pages."""
if self.count == 0:
return []
# Add one because range is right-side exclusive
return range(1, self.num_pages + 1)

def _fetch_page(self, start=0):
"""Retrieve a new result response from Solr."""
# need to convert the keys to strings to pass them as parameters
new_params = {}
for k, v in self.params.items():
new_params[str(k)] = v

if new_params['sort']:
field, order = new_params['sort'].split(' ')
new_params['sort'] = field
new_params['sort_order'] = order

# get the new start index
new_params['start'] = start
return self.conn.query(**new_params)

def page(self, page_num=1):
"""Return the requested Page object"""
try:
int(page_num)
except:
raise 'PageNotAnInteger'

if page_num not in self.page_range:
raise 'EmptyPage', 'That page does not exist.'

# Page 1 starts at 0; take one off before calculating
start = (page_num - 1) * len(self.result.results)
new_result = self._fetch_page(start=start)
return SolrPage(new_result.results, page_num, self)


class SolrPage:
"""A single Paginator-style page."""

def __init__(self, result, page_num, paginator):
self.result = result
self.number = page_num
self.paginator = paginator

@property
def object_list(self):
return self.result

def has_next(self):
if self.number < self.paginator.num_pages:
return True
return False

def has_previous(self):
if self.number > 1:
return True
return False

def has_other_pages(self):
if self.paginator.num_pages > 1:
return True
return False

def start_index(self):
# off by one because self.number is 1-based w/django,
# but start is 0-based in solr
return (self.number - 1) * len(self.result)

def end_index(self):
# off by one because we want the last one in this set,
# not the next after that, to match django paginator
return self.start_index() + len(self.result) - 1

def next_page_number(self):
return self.number + 1

def previous_page_number(self):
return self.number - 1

41 changes: 41 additions & 0 deletions tests/test_all.py
Expand Up @@ -17,6 +17,7 @@

# solrpy
from solr import SolrConnection
from solr.paginator import SolrPaginator, SolrPage

SOLR_PATH = "/solr"
SOLR_HOST = "localhost"
Expand Down Expand Up @@ -975,5 +976,45 @@ def tearDown(self):
self.conn.close()


class TestPaginator(unittest.TestCase):

def setUp(self):
self.conn = SolrConnection(SOLR_HTTP)
self.conn.delete_query('*:*')
for i in range(0,20):
self.conn.add(id=i, data='data_%02i' % i)
self.conn.commit()

def test_pagination(self):
result = self.conn.query('*:*', sort='data', sort_order='desc')
paginator = SolrPaginator(result)
self.assertEqual(paginator.num_pages, 2)
self.assertEqual(paginator.count, 20)
self.assertEqual(paginator.page_range, [1,2])

page = paginator.page(1)
self.assertEqual(page.has_other_pages(), True)
self.assertEqual(page.has_next(), True)
self.assertEqual(page.has_previous(), False)
self.assertEqual(page.next_page_number(), 2)
self.assertEqual(page.start_index(), 0)
self.assertEqual(page.end_index(), 9)
self.assertEqual(len(page.object_list), 10)
self.assertEqual(page.object_list[0]['data'], 'data_19')

page = paginator.page(2)
self.assertEqual(page.has_other_pages(), True)
self.assertEqual(page.has_next(), False)
self.assertEqual(page.has_previous(), True)
self.assertEqual(page.previous_page_number(), 1)
self.assertEqual(page.start_index(), 10)
self.assertEqual(page.end_index(), 19)
self.assertEqual(len(page.object_list), 10)
self.assertEqual(len(page.object_list), 10)
self.assertEqual(page.object_list[0]['data'], 'data_09')

def tearDown(self):
self.conn.close()

if __name__ == "__main__":
unittest.main()

0 comments on commit 0f98a7f

Please sign in to comment.