Skip to content

Commit 2297b54

Browse files
committed
Merge branch 'move-search-api' into refactor-search-serializer-context
2 parents 485c200 + 7ea8ca8 commit 2297b54

File tree

3 files changed

+104
-103
lines changed

3 files changed

+104
-103
lines changed

readthedocs/search/api/__init__.py

-102
Original file line numberDiff line numberDiff line change
@@ -1,102 +0,0 @@
1-
from collections import namedtuple
2-
from math import ceil
3-
4-
from django.utils.translation import gettext as _
5-
from rest_framework.exceptions import NotFound
6-
from rest_framework.pagination import PageNumberPagination
7-
8-
9-
class PaginatorPage:
10-
11-
"""
12-
Mimics the result from a paginator.
13-
14-
By using this class, we avoid having to override a lot of methods
15-
of `PageNumberPagination` to make it work with the ES DSL object.
16-
"""
17-
18-
def __init__(self, page_number, total_pages, count):
19-
self.number = page_number
20-
Paginator = namedtuple("Paginator", ["num_pages", "count"])
21-
self.paginator = Paginator(total_pages, count)
22-
23-
def has_next(self):
24-
return self.number < self.paginator.num_pages
25-
26-
def has_previous(self):
27-
return self.number > 1
28-
29-
def next_page_number(self):
30-
return self.number + 1
31-
32-
def previous_page_number(self):
33-
return self.number - 1
34-
35-
36-
class SearchPagination(PageNumberPagination):
37-
38-
"""Paginator for the results of PageSearch."""
39-
40-
page_size = 50
41-
page_size_query_param = "page_size"
42-
max_page_size = 100
43-
44-
def _get_page_number(self, number):
45-
try:
46-
if isinstance(number, float) and not number.is_integer():
47-
raise ValueError
48-
number = int(number)
49-
except (TypeError, ValueError):
50-
number = -1
51-
return number
52-
53-
def paginate_queryset(self, queryset, request, view=None):
54-
"""
55-
Override to get the paginated result from the ES queryset.
56-
57-
This makes use of our custom paginator and slicing support from the ES DSL object,
58-
instead of the one used by django's ORM.
59-
60-
Mostly inspired by https://github.com/encode/django-rest-framework/blob/acbd9d8222e763c7f9c7dc2de23c430c702e06d4/rest_framework/pagination.py#L191 # noqa
61-
"""
62-
# Needed for other methods of this class.
63-
self.request = request
64-
65-
page_size = self.get_page_size(request)
66-
page_number = request.query_params.get(self.page_query_param, 1)
67-
68-
original_page_number = page_number
69-
page_number = self._get_page_number(page_number)
70-
71-
if page_number <= 0:
72-
msg = self.invalid_page_message.format(
73-
page_number=original_page_number,
74-
message=_("Invalid page"),
75-
)
76-
raise NotFound(msg)
77-
78-
start = (page_number - 1) * page_size
79-
end = page_number * page_size
80-
81-
result = []
82-
total_count = 0
83-
total_pages = 1
84-
85-
if queryset:
86-
result = queryset[start:end].execute()
87-
total_count = result.hits.total["value"]
88-
hits = max(1, total_count)
89-
total_pages = ceil(hits / page_size)
90-
91-
if total_pages > 1 and self.template is not None:
92-
# The browsable API should display pagination controls.
93-
self.display_page_controls = True
94-
95-
# Needed for other methods of this class.
96-
self.page = PaginatorPage(
97-
page_number=page_number,
98-
total_pages=total_pages,
99-
count=total_count,
100-
)
101-
102-
return result

readthedocs/search/api/pagination.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from collections import namedtuple
2+
from math import ceil
3+
4+
from django.utils.translation import gettext as _
5+
from rest_framework.exceptions import NotFound
6+
from rest_framework.pagination import PageNumberPagination
7+
8+
9+
class PaginatorPage:
10+
11+
"""
12+
Mimics the result from a paginator.
13+
14+
By using this class, we avoid having to override a lot of methods
15+
of `PageNumberPagination` to make it work with the ES DSL object.
16+
"""
17+
18+
def __init__(self, page_number, total_pages, count):
19+
self.number = page_number
20+
Paginator = namedtuple("Paginator", ["num_pages", "count"])
21+
self.paginator = Paginator(total_pages, count)
22+
23+
def has_next(self):
24+
return self.number < self.paginator.num_pages
25+
26+
def has_previous(self):
27+
return self.number > 1
28+
29+
def next_page_number(self):
30+
return self.number + 1
31+
32+
def previous_page_number(self):
33+
return self.number - 1
34+
35+
36+
class SearchPagination(PageNumberPagination):
37+
38+
"""Paginator for the results of PageSearch."""
39+
40+
page_size = 50
41+
page_size_query_param = "page_size"
42+
max_page_size = 100
43+
44+
def _get_page_number(self, number):
45+
try:
46+
if isinstance(number, float) and not number.is_integer():
47+
raise ValueError
48+
number = int(number)
49+
except (TypeError, ValueError):
50+
number = -1
51+
return number
52+
53+
def paginate_queryset(self, queryset, request, view=None):
54+
"""
55+
Override to get the paginated result from the ES queryset.
56+
57+
This makes use of our custom paginator and slicing support from the ES DSL object,
58+
instead of the one used by django's ORM.
59+
60+
Mostly inspired by https://github.com/encode/django-rest-framework/blob/acbd9d8222e763c7f9c7dc2de23c430c702e06d4/rest_framework/pagination.py#L191 # noqa
61+
"""
62+
# Needed for other methods of this class.
63+
self.request = request
64+
65+
page_size = self.get_page_size(request)
66+
page_number = request.query_params.get(self.page_query_param, 1)
67+
68+
original_page_number = page_number
69+
page_number = self._get_page_number(page_number)
70+
71+
if page_number <= 0:
72+
msg = self.invalid_page_message.format(
73+
page_number=original_page_number,
74+
message=_("Invalid page"),
75+
)
76+
raise NotFound(msg)
77+
78+
start = (page_number - 1) * page_size
79+
end = page_number * page_size
80+
81+
result = []
82+
total_count = 0
83+
total_pages = 1
84+
85+
if queryset:
86+
result = queryset[start:end].execute()
87+
total_count = result.hits.total["value"]
88+
hits = max(1, total_count)
89+
total_pages = ceil(hits / page_size)
90+
91+
if total_pages > 1 and self.template is not None:
92+
# The browsable API should display pagination controls.
93+
self.display_page_controls = True
94+
95+
# Needed for other methods of this class.
96+
self.page = PaginatorPage(
97+
page_number=page_number,
98+
total_pages=total_pages,
99+
count=total_count,
100+
)
101+
102+
return result

readthedocs/search/api/v2/views.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from readthedocs.core.utils.extend import SettingsOverrideObject
1414
from readthedocs.projects.models import Feature, Project
1515
from readthedocs.search import tasks
16-
from readthedocs.search.api import SearchPagination
16+
from readthedocs.search.api.v2.serializers import PageSearchSerializer
17+
from readthedocs.search.api.pagination import SearchPagination
1718
from readthedocs.search.api.v2.serializers import PageSearchSerializer
1819
from readthedocs.search.faceted_search import PageSearch
1920

0 commit comments

Comments
 (0)