Skip to content

Commit 985793d

Browse files
committed
Put under feature flag
1 parent 2ff01d5 commit 985793d

File tree

3 files changed

+104
-21
lines changed

3 files changed

+104
-21
lines changed

readthedocs/projects/models.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,8 +1568,14 @@ def add_features(sender, **kwargs):
15681568
SKIP_SYNC_VERSIONS = 'skip_sync_versions'
15691569
CACHED_ENVIRONMENT = 'cached_environment'
15701570
LIMIT_CONCURRENT_BUILDS = 'limit_concurrent_builds'
1571+
1572+
# Search related features
15711573
DISABLE_SERVER_SIDE_SEARCH = 'disable_server_side_search'
15721574
ENABLE_MKDOCS_SERVER_SIDE_SEARCH = 'enable_mkdocs_server_side_search'
1575+
DEFAULT_TO_FUZZY_SEARCH = 'default_to_fuzzy_search'
1576+
INDEX_FROM_HTML_FILES = 'index_from_html_files'
1577+
SEARCH_SUBPROJECTS_ON_DEFAULT_VERSION = 'search_subprojects_on_default_version'
1578+
15731579
FORCE_SPHINX_FROM_VENV = 'force_sphinx_from_venv'
15741580
LIST_PACKAGES_INSTALLED_ENV = 'list_packages_installed_env'
15751581
VCS_REMOTE_LISTING = 'vcs_remote_listing'
@@ -1578,8 +1584,6 @@ def add_features(sender, **kwargs):
15781584
USE_SPHINX_BUILDERS = 'use_sphinx_builders'
15791585
DEDUPLICATE_BUILDS = 'deduplicate_builds'
15801586
USE_SPHINX_RTD_EXT_LATEST = 'rtd_sphinx_ext_latest'
1581-
DEFAULT_TO_FUZZY_SEARCH = 'default_to_fuzzy_search'
1582-
INDEX_FROM_HTML_FILES = 'index_from_html_files'
15831587
DONT_CREATE_INDEX = 'dont_create_index'
15841588
USE_NEW_PIP_RESOLVER = 'use_new_pip_resolver'
15851589
DONT_INSTALL_LATEST_PIP = 'dont_install_latest_pip'
@@ -1667,6 +1671,8 @@ def add_features(sender, **kwargs):
16671671
LIMIT_CONCURRENT_BUILDS,
16681672
_('Limit the amount of concurrent builds'),
16691673
),
1674+
1675+
# Search related features.
16701676
(
16711677
DISABLE_SERVER_SIDE_SEARCH,
16721678
_('Disable server side search'),
@@ -1675,6 +1681,19 @@ def add_features(sender, **kwargs):
16751681
ENABLE_MKDOCS_SERVER_SIDE_SEARCH,
16761682
_('Enable server side search for MkDocs projects'),
16771683
),
1684+
(
1685+
DEFAULT_TO_FUZZY_SEARCH,
1686+
_('Default to fuzzy search for simple search queries'),
1687+
),
1688+
(
1689+
INDEX_FROM_HTML_FILES,
1690+
_('Index content directly from html files instead or relying in other sources'),
1691+
),
1692+
(
1693+
SEARCH_SUBPROJECTS_ON_DEFAULT_VERSION,
1694+
_('When searching subprojects default to its default version if it doesn\'t have the same version as the main project'),
1695+
),
1696+
16781697
(
16791698
FORCE_SPHINX_FROM_VENV,
16801699
_('Force to use Sphinx from the current virtual environment'),
@@ -1710,14 +1729,6 @@ def add_features(sender, **kwargs):
17101729
USE_SPHINX_RTD_EXT_LATEST,
17111730
_('Use latest version of the Read the Docs Sphinx extension'),
17121731
),
1713-
(
1714-
DEFAULT_TO_FUZZY_SEARCH,
1715-
_('Default to fuzzy search for simple search queries'),
1716-
),
1717-
(
1718-
INDEX_FROM_HTML_FILES,
1719-
_('Index content directly from html files instead or relying in other sources'),
1720-
),
17211732
(
17221733
DONT_CREATE_INDEX,
17231734
_('Do not create index.md or README.rst if the project does not have one.'),

readthedocs/search/api.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,13 @@ def _get_all_projects_data(self):
232232
version_slug=main_version.slug,
233233
subproject=project,
234234
)
235+
235236
# Fallback to the default version of the subproject.
236-
if not version and project.default_version:
237+
if (
238+
not version
239+
and main_project.has_feature(Feature.SEARCH_SUBPROJECTS_ON_DEFAULT_VERSION)
240+
and project.default_version
241+
):
237242
version = self._get_subproject_version(
238243
version_slug=project.default_version,
239244
subproject=project,
@@ -297,24 +302,40 @@ def get_queryset(self):
297302
calling ``search.execute().hits``. This is why an DSL search object
298303
is compatible with DRF's paginator.
299304
"""
300-
projects = {
301-
project: version.slug
302-
for project, version in self._get_all_projects_data().items()
303-
}
304-
305-
# Check to avoid searching all projects in case it's empty.
306-
if not projects:
307-
log.info('Unable to find a version to search')
308-
return []
305+
main_project = self._get_project()
306+
main_version = self._get_version()
307+
projects = {}
308+
filters = {}
309+
310+
if main_project.has_feature(Feature.SEARCH_SUBPROJECTS_ON_DEFAULT_VERSION):
311+
projects = {
312+
project: version.slug
313+
for project, version in self._get_all_projects_data().items()
314+
}
315+
# Check to avoid searching all projects in case it's empty.
316+
if not projects:
317+
log.info('Unable to find a version to search')
318+
return []
319+
else:
320+
filters['project'] = list(self._get_all_projects_data().keys())
321+
filters['version'] = main_version.slug
322+
# Check to avoid searching all projects in case these filters are empty.
323+
if not filters['project']:
324+
log.info('Unable to find a project to search')
325+
return []
326+
if not filters['version']:
327+
log.info('Unable to find a version to search')
328+
return []
309329

310330
query = self.request.query_params['q']
311331
queryset = PageSearch(
312332
query=query,
313333
projects=projects,
334+
filters=filters,
314335
user=self.request.user,
315336
# We use a permission class to control authorization
316337
filter_by_user=False,
317-
use_advanced_query=not self._get_project().has_feature(Feature.DEFAULT_TO_FUZZY_SEARCH),
338+
use_advanced_query=not main_project.has_feature(Feature.DEFAULT_TO_FUZZY_SEARCH),
318339
)
319340
return queryset
320341

readthedocs/search/tests/test_api.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,62 @@ def test_doc_search_subprojects(self, api_client, all_projects):
264264
# First result should be the subproject
265265
first_result = data[0]
266266
assert first_result['project'] == subproject.slug
267+
# The result is from the same version as the main project.
268+
assert first_result['version'] == version.slug
267269
# Check the link is the subproject document link
268270
document_link = subproject.get_docs_url(version_slug=version.slug)
269271
link = first_result['domain'] + first_result['path']
270272
assert document_link in link
271273

274+
def test_doc_search_subprojects_default_version(self, api_client, all_projects):
275+
"""Return results from subprojects that match the version from the main project or fallback to its default version."""
276+
project = all_projects[0]
277+
version = project.versions.all()[0]
278+
feature, _ = Feature.objects.get_or_create(
279+
feature_id=Feature.SEARCH_SUBPROJECTS_ON_DEFAULT_VERSION,
280+
)
281+
project.feature_set.add(feature)
282+
283+
subproject = all_projects[1]
284+
subproject_version = subproject.versions.all()[0]
285+
286+
# Change the name of the version, and make it default.
287+
subproject_version.slug = 'different'
288+
subproject_version.save()
289+
subproject.default_version = subproject_version.slug
290+
subproject.save()
291+
subproject.versions.filter(slug=version.slug).delete()
292+
293+
# Refresh index
294+
version_files = HTMLFile.objects.all().filter(version=subproject_version)
295+
for f in version_files:
296+
PageDocument().update(f)
297+
298+
# Add another project as subproject of the project
299+
project.add_subproject(subproject)
300+
301+
# Now search with subproject content but explicitly filter by the parent project
302+
query = get_search_query_from_project_file(project_slug=subproject.slug)
303+
search_params = {
304+
'q': query,
305+
'project': project.slug,
306+
'version': version.slug
307+
}
308+
resp = self.get_search(api_client, search_params)
309+
assert resp.status_code == 200
310+
311+
data = resp.data['results']
312+
assert len(data) >= 1 # there may be results from another projects
313+
314+
# First result should be the subproject
315+
first_result = data[0]
316+
assert first_result['project'] == subproject.slug
317+
assert first_result['version'] == 'different'
318+
# Check the link is the subproject document link
319+
document_link = subproject.get_docs_url(version_slug=subproject_version.slug)
320+
link = first_result['domain'] + first_result['path']
321+
assert document_link in link
322+
272323
def test_doc_search_unexisting_project(self, api_client):
273324
project = 'notfound'
274325
assert not Project.objects.filter(slug=project).exists()

0 commit comments

Comments
 (0)