Skip to content

Commit 393c7ed

Browse files
authored
Search: return relatives URLS (#7376)
Closes #7311
1 parent b4c04ac commit 393c7ed

File tree

8 files changed

+54
-35
lines changed

8 files changed

+54
-35
lines changed

docs/server-side-search.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ This is ``https://docs.readthedocs.io/_/api/v2/search`` for the ``docs`` project
9393
:>json string project: The project slug
9494
:>json string version: The version slug
9595
:>json string title: The title of the page
96-
:>json string link: An absolute URL to the resulting page
96+
:>json string domain: Canonical domain of the resulting page
97+
:>json string path: Path to the resulting page
9798
:>json object highlights: An object containing a list of substrings with matching terms.
9899
Note that the text is HTML escaped with the matching terms inside a <span> tag.
99100
:>json object blocks:
@@ -140,7 +141,8 @@ This is ``https://docs.readthedocs.io/_/api/v2/search`` for the ``docs`` project
140141
"project": "docs",
141142
"version": "latest",
142143
"title": "Server Side Search",
143-
"link": "https://docs.readthedocs.io/en/latest/server-side-search.html",
144+
"domain": "https://docs.readthedocs.io",
145+
"path": "/en/latest/server-side-search.html",
144146
"highlights": {
145147
"title": [
146148
"<span>Server</span> <span>Side</span> <span>Search</span>"

readthedocs/core/static-src/core/js/doc-embed/search.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function attach_elastic_search_query_sphinx(data) {
6969
title = xss(result.highlights.title[0]);
7070
}
7171

72-
var link = result.link + "?highlight=" + $.urlencode(query);
72+
var link = result.path + "?highlight=" + $.urlencode(query);
7373

7474
var item = $('<a>', {'href': link});
7575

@@ -291,7 +291,7 @@ function attach_elastic_search_query_mkdocs(data) {
291291

292292
var item = $('<article>');
293293
item.append(
294-
$('<h3>').append($('<a>', {'href': result.link, 'text': result.title}))
294+
$('<h3>').append($('<a>', {'href': result.path, 'text': result.title}))
295295
);
296296

297297
if (result.project !== project) {
@@ -303,7 +303,7 @@ function attach_elastic_search_query_mkdocs(data) {
303303
var section = blocks[j];
304304

305305
if (section.type === 'section') {
306-
var section_link = result.link + '#' + section.id;
306+
var section_link = result.path + '#' + section.id;
307307
var section_title = section.title;
308308
var section_content = section.content;
309309
if (section_content.length > MAX_SUBSTRING_LIMIT) {

readthedocs/core/static/core/js/readthedocs-doc-embed.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readthedocs/search/serializers.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import re
1111
from functools import namedtuple
1212
from operator import attrgetter
13+
from urllib.parse import urlparse
1314

1415
from django.shortcuts import get_object_or_404
1516
from rest_framework import serializers
@@ -58,11 +59,26 @@ class PageSearchSerializer(serializers.Serializer):
5859
project = serializers.CharField()
5960
version = serializers.CharField()
6061
title = serializers.CharField()
61-
link = serializers.SerializerMethodField()
62+
path = serializers.SerializerMethodField()
63+
domain = serializers.SerializerMethodField()
6264
highlights = PageHighlightSerializer(source='meta.highlight', default=dict)
6365
blocks = serializers.SerializerMethodField()
6466

65-
def get_link(self, obj):
67+
def get_domain(self, obj):
68+
full_path = self._get_full_path(obj)
69+
if full_path:
70+
parsed = urlparse(full_path)
71+
return f'{parsed.scheme}://{parsed.netloc}'
72+
return None
73+
74+
def get_path(self, obj):
75+
full_path = self._get_full_path(obj)
76+
if full_path:
77+
parsed = urlparse(full_path)
78+
return parsed.path
79+
return None
80+
81+
def _get_full_path(self, obj):
6682
"""
6783
Get the page link.
6884
@@ -71,8 +87,6 @@ def get_link(self, obj):
7187
If the result is fetched from the database,
7288
it's cached into ``project_data``.
7389
"""
74-
# TODO: return a relative URL when this is called from the indoc search.
75-
7690
# First try to build the URL from the context.
7791
project_data = self.context.get('projects_data', {}).get(obj.project)
7892
if project_data:

readthedocs/search/tests/test_api.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ def setup_method(self, method):
3535
# installed
3636
self.url = reverse('search_api')
3737

38+
@pytest.fixture(autouse=True)
39+
def setup_settings(self, settings):
40+
settings.PUBLIC_DOMAIN = 'readthedocs.io'
41+
settings.USE_SUBDOMAIN = True
42+
3843
def get_search(self, api_client, search_params):
3944
return api_client.get(self.url, search_params)
4045

@@ -260,7 +265,8 @@ def test_doc_search_subprojects(self, api_client, all_projects):
260265
assert first_result['project'] == subproject.slug
261266
# Check the link is the subproject document link
262267
document_link = subproject.get_docs_url(version_slug=version.slug)
263-
assert document_link in first_result['link']
268+
link = first_result['domain'] + first_result['path']
269+
assert document_link in link
264270

265271
def test_doc_search_unexisting_project(self, api_client):
266272
project = 'notfound'
@@ -360,7 +366,7 @@ def test_search_correct_link_for_normal_page_html_projects(self, api_client, doc
360366

361367
result = resp.data['results'][0]
362368
assert result['project'] == project.slug
363-
assert result['link'].endswith('en/latest/support.html')
369+
assert result['path'] == '/en/latest/support.html'
364370

365371
@pytest.mark.parametrize('doctype', [SPHINX, SPHINX_SINGLEHTML, MKDOCS_HTML])
366372
def test_search_correct_link_for_index_page_html_projects(self, api_client, doctype):
@@ -378,7 +384,7 @@ def test_search_correct_link_for_index_page_html_projects(self, api_client, doct
378384

379385
result = resp.data['results'][0]
380386
assert result['project'] == project.slug
381-
assert result['link'].endswith('en/latest/index.html')
387+
assert result['path'] == '/en/latest/index.html'
382388

383389
@pytest.mark.parametrize('doctype', [SPHINX, SPHINX_SINGLEHTML, MKDOCS_HTML])
384390
def test_search_correct_link_for_index_page_subdirectory_html_projects(self, api_client, doctype):
@@ -396,7 +402,7 @@ def test_search_correct_link_for_index_page_subdirectory_html_projects(self, api
396402

397403
result = resp.data['results'][0]
398404
assert result['project'] == project.slug
399-
assert result['link'].endswith('en/latest/guides/index.html')
405+
assert result['path'] == '/en/latest/guides/index.html'
400406

401407
@pytest.mark.parametrize('doctype', [SPHINX_HTMLDIR, MKDOCS])
402408
def test_search_correct_link_for_normal_page_htmldir_projects(self, api_client, doctype):
@@ -414,7 +420,7 @@ def test_search_correct_link_for_normal_page_htmldir_projects(self, api_client,
414420

415421
result = resp.data['results'][0]
416422
assert result['project'] == project.slug
417-
assert result['link'].endswith('en/latest/support.html')
423+
assert result['path'] == '/en/latest/support.html'
418424

419425
@pytest.mark.parametrize('doctype', [SPHINX_HTMLDIR, MKDOCS])
420426
def test_search_correct_link_for_index_page_htmldir_projects(self, api_client, doctype):
@@ -432,7 +438,7 @@ def test_search_correct_link_for_index_page_htmldir_projects(self, api_client, d
432438

433439
result = resp.data['results'][0]
434440
assert result['project'] == project.slug
435-
assert result['link'].endswith('en/latest/')
441+
assert result['path'] == '/en/latest/'
436442

437443
@pytest.mark.parametrize('doctype', [SPHINX_HTMLDIR, MKDOCS])
438444
def test_search_correct_link_for_index_page_subdirectory_htmldir_projects(self, api_client, doctype):
@@ -450,7 +456,7 @@ def test_search_correct_link_for_index_page_subdirectory_htmldir_projects(self,
450456

451457
result = resp.data['results'][0]
452458
assert result['project'] == project.slug
453-
assert result['link'].endswith('en/latest/guides/')
459+
assert result['path'] == '/en/latest/guides/'
454460

455461
def test_search_advanced_query_detection(self, api_client):
456462
project = Project.objects.get(slug='docs')
@@ -519,8 +525,8 @@ def test_search_custom_ranking(self, api_client):
519525

520526
results = resp.data['results']
521527
assert len(results) == 2
522-
assert results[0]['link'].endswith('/en/latest/index.html')
523-
assert results[1]['link'].endswith('/en/latest/guides/index.html')
528+
assert results[0]['path'] == '/en/latest/index.html'
529+
assert results[1]['path'] == '/en/latest/guides/index.html'
524530

525531
# Query with a higher rank over guides/index.html
526532
page_guides.rank = 5
@@ -537,8 +543,8 @@ def test_search_custom_ranking(self, api_client):
537543

538544
results = resp.data['results']
539545
assert len(results) == 2
540-
assert results[0]['link'].endswith('/en/latest/guides/index.html')
541-
assert results[1]['link'].endswith('/en/latest/index.html')
546+
assert results[0]['path'] == '/en/latest/guides/index.html'
547+
assert results[1]['path'] == '/en/latest/index.html'
542548

543549
# Query with a lower rank over index.html
544550
page_index.rank = -2
@@ -558,8 +564,8 @@ def test_search_custom_ranking(self, api_client):
558564

559565
results = resp.data['results']
560566
assert len(results) == 2
561-
assert results[0]['link'].endswith('/en/latest/guides/index.html')
562-
assert results[1]['link'].endswith('/en/latest/index.html')
567+
assert results[0]['path'] == '/en/latest/guides/index.html'
568+
assert results[1]['path'] == '/en/latest/index.html'
563569

564570
# Query with a lower rank over index.html
565571
page_index.rank = 3
@@ -579,8 +585,8 @@ def test_search_custom_ranking(self, api_client):
579585

580586
results = resp.data['results']
581587
assert len(results) == 2
582-
assert results[0]['link'].endswith('/en/latest/guides/index.html')
583-
assert results[1]['link'].endswith('/en/latest/index.html')
588+
assert results[0]['path'] == '/en/latest/guides/index.html'
589+
assert results[1]['path'] == '/en/latest/index.html'
584590

585591
# Query with a same rank over guides/index.html and index.html
586592
page_index.rank = -10
@@ -600,8 +606,8 @@ def test_search_custom_ranking(self, api_client):
600606

601607
results = resp.data['results']
602608
assert len(results) == 2
603-
assert results[0]['link'].endswith('/en/latest/index.html')
604-
assert results[1]['link'].endswith('/en/latest/guides/index.html')
609+
assert results[0]['path'] == '/en/latest/index.html'
610+
assert results[1]['path'] == '/en/latest/guides/index.html'
605611

606612

607613
class TestDocumentSearch(BaseTestDocumentSearch):

readthedocs/search/tests/test_proxied_api.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ class TestProxiedSearchAPI(BaseTestDocumentSearch):
1010
# This project slug needs to exist in the ``all_projects`` fixture.
1111
host = 'docs.readthedocs.io'
1212

13-
@pytest.fixture(autouse=True)
14-
def setup_settings(self, settings):
15-
settings.PUBLIC_DOMAIN = 'readthedocs.io'
16-
1713
def get_search(self, api_client, search_params):
1814
# TODO: remove once the api is stable
1915
search_params['new-api'] = 'true'

readthedocs/search/tests/test_views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ def test_file_search_exact_match(self, client, project):
281281
# only one project
282282
assert len(results) == 1
283283
assert results[0]['project'] == 'kuma'
284-
assert results[0]['link'] == 'http://readthedocs.org/docs/kuma/en/latest/documentation.html'
284+
assert results[0]['domain'] == 'http://readthedocs.org'
285+
assert results[0]['path'] == '/docs/kuma/en/latest/documentation.html'
285286

286287
blocks = results[0]['blocks']
287288
assert len(blocks) == 1

readthedocs/templates/search/elastic_search.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,14 @@ <h3>
183183
{% endfor %}
184184

185185
{% elif result.type == 'page' %}
186-
<a href="{{ result.link }}?highlight={{ query }}">
186+
<a href="{{ result.domain }}{{ result.path}}?highlight={{ query }}">
187187
{{ result.project }} - {% if result.highlights.title %} {{ result.highlights.title.0|safe }} {% else %} {{ result.title }} {% endif %}
188188
</a>
189189

190190
{% for block in result.blocks %}
191191
{% if block.type == 'domain' %}
192192
<p>
193-
<a href="{{ result.link }}?highlight={{ query }}#{{ block.id }}">
193+
<a href="{{ result.domain }}{{ result.path }}?highlight={{ query }}#{{ block.id }}">
194194
{% if block.highlights.name %}
195195
{% with domain_name=block.highlights.name %}
196196
[{{ block.role }}]: {{ domain_name.0|safe }}
@@ -214,7 +214,7 @@ <h3>
214214

215215
{% elif block.type == 'section' %}
216216
<p>
217-
<a href="{{ result.link }}?highlight={{ query }}#{{ block.id }}">
217+
<a href="{{ result.domain }}{{ result.path }}?highlight={{ query }}#{{ block.id }}">
218218
{% if block.highlights.title %}
219219
{% with section_title=block.highlights.title %}
220220
{{ section_title.0|safe }}

0 commit comments

Comments
 (0)