@@ -120,19 +120,16 @@ def paginate_queryset(self, queryset, request, view=None):
120
120
class PageSearchAPIView (CDNCacheTagsMixin , GenericAPIView ):
121
121
122
122
"""
123
- Main entry point to perform a search using Elasticsearch .
123
+ Server side search API .
124
124
125
- Required query params :
125
+ Required query parameters :
126
126
127
- - q (search term)
128
- - project
129
- - version
127
+ - **q**: Search term.
128
+ - ** project**: Project to search.
129
+ - ** version**: Version to search.
130
130
131
- .. note::
132
-
133
- The methods `_get_project` and `_get_version`
134
- are called many times, so a basic cache is implemented.
135
- """
131
+ Check our [docs](https://docs.readthedocs.io/en/stable/server-side-search.html#api) for more information.
132
+ """ # noqa
136
133
137
134
http_method_names = ['get' ]
138
135
permission_classes = [IsAuthorizedToViewVersion ]
@@ -208,55 +205,64 @@ def _get_all_projects_data(self):
208
205
main_version = self ._get_version ()
209
206
main_project = self ._get_project ()
210
207
208
+ if not self ._has_permission (self .request .user , main_version ):
209
+ return {}
210
+
211
211
projects_data = {
212
- main_project .slug : ProjectData (
213
- alias = None ,
214
- version = VersionData (
215
- slug = main_version .slug ,
216
- docs_url = main_project .get_docs_url (version_slug = main_version .slug ),
217
- ),
218
- )
212
+ main_project .slug : self ._get_project_data (main_project , main_version ),
219
213
}
220
214
221
- subprojects = Project .objects .filter (
222
- superprojects__parent_id = main_project .id ,
223
- )
215
+ subprojects = Project .objects .filter (superprojects__parent_id = main_project .id )
224
216
for subproject in subprojects :
225
- version = self ._get_subproject_version (
217
+ version = self ._get_project_version (
218
+ project = subproject ,
226
219
version_slug = main_version .slug ,
227
- subproject = subproject ,
220
+ include_hidden = False ,
228
221
)
229
222
230
223
# Fallback to the default version of the subproject.
231
224
if not version and subproject .default_version :
232
- version = self ._get_subproject_version (
225
+ version = self ._get_project_version (
226
+ project = subproject ,
233
227
version_slug = subproject .default_version ,
234
- subproject = subproject ,
228
+ include_hidden = False ,
235
229
)
236
230
237
231
if version and self ._has_permission (self .request .user , version ):
238
- url = subproject .get_docs_url (version_slug = version .slug )
239
- project_alias = subproject .superprojects .values_list ('alias' , flat = True ).first ()
240
- version_data = VersionData (
241
- slug = version .slug ,
242
- docs_url = url ,
243
- )
244
- projects_data [subproject .slug ] = ProjectData (
245
- alias = project_alias ,
246
- version = version_data ,
232
+ projects_data [subproject .slug ] = self ._get_project_data (
233
+ subproject , version
247
234
)
248
235
249
236
return projects_data
250
237
251
- def _get_subproject_version (self , version_slug , subproject ):
252
- """Get a version from the subproject."""
238
+ def _get_project_data (self , project , version ):
239
+ """Build a `ProjectData` object given a project and its version."""
240
+ url = project .get_docs_url (version_slug = version .slug )
241
+ project_alias = project .superprojects .values_list ("alias" , flat = True ).first ()
242
+ version_data = VersionData (
243
+ slug = version .slug ,
244
+ docs_url = url ,
245
+ )
246
+ return ProjectData (
247
+ alias = project_alias ,
248
+ version = version_data ,
249
+ )
250
+
251
+ def _get_project_version (self , project , version_slug , include_hidden = True ):
252
+ """
253
+ Get a version from a given project.
254
+
255
+ :param project: A `Project` object.
256
+ :param version_slug: The version slug.
257
+ :param include_hidden: If hidden versions should be considered.
258
+ """
253
259
return (
254
260
Version .internal
255
261
.public (
256
262
user = self .request .user ,
257
- project = subproject ,
258
- include_hidden = False ,
263
+ project = project ,
259
264
only_built = True ,
265
+ include_hidden = include_hidden ,
260
266
)
261
267
.filter (slug = version_slug )
262
268
.first ()
@@ -272,14 +278,16 @@ def _has_permission(self, user, version):
272
278
"""
273
279
return True
274
280
281
+ def _get_search_query (self ):
282
+ return self .request .query_params ["q" ]
283
+
275
284
def _record_query (self , response ):
276
285
project_slug = self ._get_project ().slug
277
286
version_slug = self ._get_version ().slug
278
287
total_results = response .data .get ('count' , 0 )
279
288
time = timezone .now ()
280
289
281
- query = self .request .query_params ['q' ]
282
- query = query .lower ().strip ()
290
+ query = self ._get_search_query ().lower ().strip ()
283
291
284
292
# Record the query with a celery task
285
293
tasks .record_search_query .delay (
@@ -290,6 +298,10 @@ def _record_query(self, response):
290
298
time .isoformat (),
291
299
)
292
300
301
+ def _use_advanced_query (self ):
302
+ main_project = self ._get_project ()
303
+ return not main_project .has_feature (Feature .DEFAULT_TO_FUZZY_SEARCH )
304
+
293
305
def get_queryset (self ):
294
306
"""
295
307
Returns an Elasticsearch DSL search object or an iterator.
@@ -300,9 +312,6 @@ def get_queryset(self):
300
312
calling ``search.execute().hits``. This is why an DSL search object
301
313
is compatible with DRF's paginator.
302
314
"""
303
- main_project = self ._get_project ()
304
- projects = {}
305
-
306
315
projects = {
307
316
project : project_data .version .slug
308
317
for project , project_data in self ._get_all_projects_data ().items ()
@@ -312,12 +321,12 @@ def get_queryset(self):
312
321
log .info ('Unable to find a version to search' )
313
322
return []
314
323
315
- query = self .request . query_params [ 'q' ]
324
+ query = self ._get_search_query ()
316
325
queryset = PageSearch (
317
326
query = query ,
318
327
projects = projects ,
319
328
aggregate_results = False ,
320
- use_advanced_query = not main_project . has_feature ( Feature . DEFAULT_TO_FUZZY_SEARCH ),
329
+ use_advanced_query = self . _use_advanced_query ( ),
321
330
)
322
331
return queryset
323
332
0 commit comments