Skip to content

Commit 074a6f0

Browse files
committed
Search: allow to search on different versions of subprojects
1 parent f3590d6 commit 074a6f0

File tree

2 files changed

+50
-17
lines changed

2 files changed

+50
-17
lines changed

readthedocs/search/api.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,16 @@ def _get_all_projects_data(self):
229229
)
230230
for project in subprojects:
231231
version = self._get_subproject_versions_queryset(
232-
main_version=main_version,
232+
version_slug=version.slug,
233233
subproject=project,
234-
).first()
234+
)
235+
# Fallback to the default version of the subproject.
236+
if not version and project.default_version:
237+
version = self._get_subproject_versions_queryset(
238+
version_slug=project.default_version,
239+
subproject=project,
240+
)
241+
235242
if version and self._has_permission(self.request.user, version):
236243
url = project.get_docs_url(version_slug=version.slug)
237244
projects_data[project.slug] = VersionData(
@@ -243,12 +250,13 @@ def _get_all_projects_data(self):
243250
setattr(self, cache_key, projects_data)
244251
return projects_data
245252

246-
def _get_subproject_versions_queryset(self, main_version, subproject):
253+
def _get_subproject_version(self, version_slug, subproject):
247254
"""Get a queryset with the versions from the subproject that match `main_version`."""
248255
return (
249256
Version.internal
250257
.public(user=self.request.user, project=subproject, include_hidden=False)
251-
.filter(slug=main_version.slug)
258+
.filter(slug=version_slug)
259+
.first()
252260
)
253261

254262
def _has_permission(self, user, version):
@@ -282,22 +290,20 @@ def get_queryset(self):
282290
calling ``search.execute().hits``. This is why an DSL search object
283291
is compatible with DRF's paginator.
284292
"""
285-
filters = {}
286-
filters['project'] = list(self._get_all_projects_data().keys())
287-
filters['version'] = self._get_version().slug
293+
projects = {
294+
project: version.slug
295+
for project, version in self._get_all_projects_data().items()
296+
}
288297

289-
# Check to avoid searching all projects in case these filters are empty.
290-
if not filters['project']:
291-
log.info('Unable to find a project to search')
292-
return []
293-
if not filters['version']:
298+
# Check to avoid searching all projects in case it's empty.
299+
if not projects:
294300
log.info('Unable to find a version to search')
295301
return []
296302

297303
query = self.request.query_params['q']
298304
queryset = PageSearch(
299305
query=query,
300-
filters=filters,
306+
projects=projects,
301307
user=self.request.user,
302308
# We use a permission class to control authorization
303309
filter_by_user=False,

readthedocs/search/faceted_search.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
MultiMatch,
1212
Nested,
1313
SimpleQueryString,
14+
Term,
1415
Wildcard,
1516
)
1617

@@ -35,12 +36,23 @@ class RTDFacetedSearch(FacetedSearch):
3536
'post_tags': ['</span>'],
3637
}
3738

38-
def __init__(self, query=None, filters=None, user=None, use_advanced_query=True, **kwargs):
39+
def __init__(
40+
self,
41+
query=None,
42+
filters=None,
43+
projects=None,
44+
user=None,
45+
use_advanced_query=True,
46+
**kwargs,
47+
):
3948
"""
4049
Pass in a user in order to filter search results by privacy.
4150
42-
If `use_advanced_query` is `True`,
43-
force to always use `SimpleQueryString` for the text query object.
51+
:param projects: A dictionary of project slugs mapped to a `VersionData` object.
52+
Results are filter with these values.
53+
54+
:param use_advanced_query: If `True` forces to always use
55+
`SimpleQueryString` for the text query object.
4456
4557
.. warning::
4658
@@ -50,6 +62,7 @@ def __init__(self, query=None, filters=None, user=None, use_advanced_query=True,
5062
self.user = user
5163
self.filter_by_user = kwargs.pop('filter_by_user', True)
5264
self.use_advanced_query = use_advanced_query
65+
self.projects = projects or {}
5366

5467
# Hack a fix to our broken connection pooling
5568
# This creates a new connection on every request,
@@ -280,8 +293,22 @@ def query(self, search, query):
280293
)
281294

282295
queries.extend([sections_nested_query, domains_nested_query])
296+
bool_query = Bool(should=queries)
297+
298+
if self.projects:
299+
versions_query = [
300+
Bool(
301+
must=[
302+
Term(project={'value': project}),
303+
Term(version={'value': version}),
304+
]
305+
)
306+
for project, version in self.projects.items()
307+
]
308+
bool_query = Bool(must=[bool_query, Bool(should=versions_query)])
309+
283310
final_query = FunctionScore(
284-
query=Bool(should=queries),
311+
query=bool_query,
285312
script_score=self._get_script_score(),
286313
)
287314
search = search.query(final_query)

0 commit comments

Comments
 (0)