From fbb62cfda9600d2caa4d6f097745d59cea3a431b Mon Sep 17 00:00:00 2001 From: Will Lachance Date: Sat, 25 May 2024 11:59:22 -0400 Subject: [PATCH 01/24] Boost title-matching scores if main title Related to #12391. Intended as a proof of concept, not a full solution. --- sphinx/themes/basic/static/searchtools.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index eaed90953f4..a6701bf394b 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -328,10 +328,14 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let score = Math.round(100 * queryLower.length / title.length) + let isMainTitle = titles[file] === title + // score these a little bit above document matches, with more of a boost + // for main document titles + let baseScore = Scorer.title + (isMainTitle ? 2 : 1) + let score = Math.round(baseScore * queryLower.length / title.length) normalResults.push([ docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, + isMainTitle ? title : `${titles[file]} > ${title}`, id !== null ? "#" + id : "", null, score, From 96e28940066f81b798314c3756a9e3ba7fdf280c Mon Sep 17 00:00:00 2001 From: James Addison Date: Wed, 19 Jun 2024 00:22:59 +0100 Subject: [PATCH 02/24] [search] Add fixture data for use with title relevance test cases. --- tests/js/fixtures/titles/searchindex.js | 1 + tests/js/roots/titles/conf.py | 6 ++++++ tests/js/roots/titles/index.rst | 20 ++++++++++++++++++++ tests/js/roots/titles/relevance.py | 4 ++++ tests/js/roots/titles/relevance.rst | 13 +++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 tests/js/fixtures/titles/searchindex.js create mode 100644 tests/js/roots/titles/conf.py create mode 100644 tests/js/roots/titles/index.rst create mode 100644 tests/js/roots/titles/relevance.py create mode 100644 tests/js/roots/titles/relevance.rst diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js new file mode 100644 index 00000000000..6de342bcc6d --- /dev/null +++ b/tests/js/fixtures/titles/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"Main Page": [[0, "main-page"]], "Relevance": [[0, "relevance"], [1, "relevance"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "example_modul": [], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file diff --git a/tests/js/roots/titles/conf.py b/tests/js/roots/titles/conf.py new file mode 100644 index 00000000000..e5f6bb97a20 --- /dev/null +++ b/tests/js/roots/titles/conf.py @@ -0,0 +1,6 @@ +import os +import sys + +sys.path.insert(0, os.path.abspath('.')) + +extensions = ['sphinx.ext.autodoc'] diff --git a/tests/js/roots/titles/index.rst b/tests/js/roots/titles/index.rst new file mode 100644 index 00000000000..464cd954b5c --- /dev/null +++ b/tests/js/roots/titles/index.rst @@ -0,0 +1,20 @@ +Main Page +========= + +This is the main page of the ``titles`` test project. + +In particular, this test project is intended to demonstrate how Sphinx +can handle scoring of query matches against document titles and subsection +heading titles relative to other document matches such as terms found within +document text and object names extracted from code. + +Relevance +--------- + +In the context of search engines, we can say that a document is **relevant** +to a user's query when it contains information that seems likely to help them +find an answer to a question they're asking, or to improve their knowledge of +the subject area they're researching. + +.. automodule:: relevance + :members: diff --git a/tests/js/roots/titles/relevance.py b/tests/js/roots/titles/relevance.py new file mode 100644 index 00000000000..95f1aecbcd7 --- /dev/null +++ b/tests/js/roots/titles/relevance.py @@ -0,0 +1,4 @@ +class Example: + """Example class""" + num_attribute = 5 + text_attribute = "string" diff --git a/tests/js/roots/titles/relevance.rst b/tests/js/roots/titles/relevance.rst new file mode 100644 index 00000000000..18f494fe109 --- /dev/null +++ b/tests/js/roots/titles/relevance.rst @@ -0,0 +1,13 @@ +Relevance +========= + +In some domains, it can be straightforward to determine whether a search result +is relevant to the user's query. + +For example, if we are in a software programming language domain, and a user +has issued a query for the term ``printf``, then we could consider a document +in the corpus that describes a built-in language function with the same name +as (highly) relevant. A document that only happens to mention the ``printf`` +function name as part of some example code that appears on the page would +also be relevant, but likely less relevant than the one that describes the +function itself in detail. From a2a4b609fe2dfe55d88374ff5f29cada8774ce8e Mon Sep 17 00:00:00 2001 From: James Addison Date: Wed, 19 Jun 2024 22:08:55 +0100 Subject: [PATCH 03/24] [search] tests: add test coverage for title-related relevance scoring. --- tests/js/searchtools.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index 5e97572fb3e..6584b8bf321 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -7,6 +7,21 @@ describe('Basic html theme search', function() { return req.responseText; } + function checkRanking(expectedRanking, results) { + nextExpected = expectedRanking.pop(0); + + results.forEach(result => { + let [expectedPage, expectedTitle, expectedTarget] = nextExpected; + let [page, title, target] = result; + + if (page == expectedPage && title == expectedTitle && target == expectedTarget) { + nextExpected = expectedRanking.pop(0); + } + }); + + expect(expectedRanking.length).toEqual(0); + } + describe('terms search', function() { it('should find "C++" when in index', function() { @@ -94,6 +109,25 @@ describe('Basic html theme search', function() { }); + describe('search result ranking', function() { + + it('should score an object-name match above a page-title match', function() { + eval(loadFixture("titles/searchindex.js")); + + expectedRanking = [ + ['index', 'relevance', '#module-relevance'], /* py:module documentation */ + ['relevance', 'Relevance', '#relevance'], /* main title */ + ['index', 'Main Page > Relevance', '#relevance'], /* subsection heading title */ + ]; + + searchParameters = Search._parseQuery('relevance'); + results = Search._performSearch(...searchParameters); + + checkRanking(expectedRanking, results); + }); + + }); + }); describe("htmlToText", function() { From cb0f6e7ffe5ec3afe8beebfeeba24b50451e7971 Mon Sep 17 00:00:00 2001 From: James Addison Date: Wed, 19 Jun 2024 22:16:39 +0100 Subject: [PATCH 04/24] [search] regenerate JS fixture root titles searchindex from fresh/clean build. --- tests/js/fixtures/titles/searchindex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 6de342bcc6d..72daaa39d0e 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, "main-page"]], "Relevance": [[0, "relevance"], [1, "relevance"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "example_modul": [], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, "main-page"]], "Relevance": [[0, "relevance"], [1, "relevance"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file From 5eaea64b866242123880ac0246ac22e6e7e5ba63 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 24 Jun 2024 11:22:28 +0100 Subject: [PATCH 05/24] [search] Refactor scoring logic adjustment: * Minimize diff relative to mainline codebase to ease review/historic viewing. * Move updated main-title score variable into a ``Scorer`` constant. --- sphinx/themes/basic/static/searchtools.js | 9 +++------ tests/js/searchtools.js | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index a6701bf394b..b21446926e3 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -39,6 +39,7 @@ if (typeof Scorer === "undefined") { objPrioDefault: 0, // query found in title + mainTitle: 16, title: 15, partialTitle: 7, // query found in terms @@ -328,14 +329,10 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let isMainTitle = titles[file] === title - // score these a little bit above document matches, with more of a boost - // for main document titles - let baseScore = Scorer.title + (isMainTitle ? 2 : 1) - let score = Math.round(baseScore * queryLower.length / title.length) + let score = Math.round(titles[file] !== title ? Scorer.title : Scorer.mainTitle * queryLower.length / title.length) normalResults.push([ docNames[file], - isMainTitle ? title : `${titles[file]} > ${title}`, + titles[file] !== title ? `${titles[file]} > ${title}` : title, id !== null ? "#" + id : "", null, score, diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index de61b20b3c4..c3b55c9c1fa 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -100,7 +100,7 @@ describe('Basic html theme search', function() { 'Main Page', '#main-page', null, - 17, + 16, 'index.rst' ] ]; From afb1685248fe4fb060792ea70d612eda665d5d1b Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 24 Jun 2024 12:04:23 +0100 Subject: [PATCH 06/24] Add CHANGES.rst entry. --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0a31a6746bd..073e1428859 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -58,6 +58,11 @@ Bugs fixed Patch by Bénédikt Tran. * #12220: Fix loading custom template translations for ``en`` locale. Patch by Nicolas Peugnet. +* #12391: Adjust scoring of matches during HTML search so that document main + titles tend to rank more highly than subsection titles, and also to provide + a gain to matches on the name of programming domain objects relative to + title/subtitle matches. + Patch by James Addison. Improvements ------------ From 5a5e2717ebb895eb3ff169e2e213ff949060a632 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 24 Jun 2024 12:07:34 +0100 Subject: [PATCH 07/24] Fixup: add missing credit to CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 073e1428859..83ec224a10f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -62,7 +62,7 @@ Bugs fixed titles tend to rank more highly than subsection titles, and also to provide a gain to matches on the name of programming domain objects relative to title/subtitle matches. - Patch by James Addison. + Patch by James Addison and Will Lachance. Improvements ------------ From 7418a712c4b68926fd3ad54bd76138d91dc36800 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 24 Jun 2024 23:13:39 +0100 Subject: [PATCH 08/24] Fixup: use intended operator precedence. --- sphinx/themes/basic/static/searchtools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index b21446926e3..4d9ef1f905c 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -329,7 +329,7 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let score = Math.round(titles[file] !== title ? Scorer.title : Scorer.mainTitle * queryLower.length / title.length) + let score = Math.round((titles[file] !== title ? Scorer.title : Scorer.mainTitle) * queryLower.length / title.length) normalResults.push([ docNames[file], titles[file] !== title ? `${titles[file]} > ${title}` : title, From 17367eb99024126c3b582f44d38e9fdc01622659 Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 25 Jun 2024 11:04:44 +0100 Subject: [PATCH 09/24] Revert "Fixup: use intended operator precedence." This reverts commit 7418a712c4b68926fd3ad54bd76138d91dc36800. --- sphinx/themes/basic/static/searchtools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 4d9ef1f905c..b21446926e3 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -329,7 +329,7 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let score = Math.round((titles[file] !== title ? Scorer.title : Scorer.mainTitle) * queryLower.length / title.length) + let score = Math.round(titles[file] !== title ? Scorer.title : Scorer.mainTitle * queryLower.length / title.length) normalResults.push([ docNames[file], titles[file] !== title ? `${titles[file]} > ${title}` : title, From 96526a9610c3f56eb3de744fda807fee1d1106eb Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 25 Jun 2024 11:04:45 +0100 Subject: [PATCH 10/24] Revert "[search] Refactor scoring logic adjustment:" This reverts commit 5eaea64b866242123880ac0246ac22e6e7e5ba63. --- sphinx/themes/basic/static/searchtools.js | 9 ++++++--- tests/js/searchtools.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index b21446926e3..a6701bf394b 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -39,7 +39,6 @@ if (typeof Scorer === "undefined") { objPrioDefault: 0, // query found in title - mainTitle: 16, title: 15, partialTitle: 7, // query found in terms @@ -329,10 +328,14 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let score = Math.round(titles[file] !== title ? Scorer.title : Scorer.mainTitle * queryLower.length / title.length) + let isMainTitle = titles[file] === title + // score these a little bit above document matches, with more of a boost + // for main document titles + let baseScore = Scorer.title + (isMainTitle ? 2 : 1) + let score = Math.round(baseScore * queryLower.length / title.length) normalResults.push([ docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, + isMainTitle ? title : `${titles[file]} > ${title}`, id !== null ? "#" + id : "", null, score, diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index c3b55c9c1fa..de61b20b3c4 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -100,7 +100,7 @@ describe('Basic html theme search', function() { 'Main Page', '#main-page', null, - 16, + 17, 'index.rst' ] ]; From 6c3ffa2456de3a52a9c9ea6e0bbd88c4fde8b8cf Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 25 Jun 2024 11:13:14 +0100 Subject: [PATCH 11/24] [search] Refactor scoring logic adjustments (second attempt/suggestion) --- sphinx/themes/basic/static/searchtools.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index a6701bf394b..02fd46289fb 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -328,17 +328,14 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let isMainTitle = titles[file] === title - // score these a little bit above document matches, with more of a boost - // for main document titles - let baseScore = Scorer.title + (isMainTitle ? 2 : 1) - let score = Math.round(baseScore * queryLower.length / title.length) + let score = Math.round(Scorer.title * queryLower.length / title.length); + let boost = titles[file] === title ? 1 : 0; // add a boost for document titles normalResults.push([ docNames[file], - isMainTitle ? title : `${titles[file]} > ${title}`, + titles[file] !== title ? `${titles[file]} > ${title}` : title, id !== null ? "#" + id : "", null, - score, + score + boost, filenames[file], ]); } From fd36010169d832bd801ae5885f363e5f298f89f4 Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 25 Jun 2024 11:16:00 +0100 Subject: [PATCH 12/24] Fixup: adjust test search result score expectation. Relates-to commit 6c3ffa2456de3a52a9c9ea6e0bbd88c4fde8b8cf. --- tests/js/searchtools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index de61b20b3c4..c3b55c9c1fa 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -100,7 +100,7 @@ describe('Basic html theme search', function() { 'Main Page', '#main-page', null, - 17, + 16, 'index.rst' ] ]; From 2f0cbe1151c39c3afb6f597e95ca74a3243afd90 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:03:41 +0100 Subject: [PATCH 13/24] Tests: refactor checkRanking function to use JavaScript array-unpacking. --- tests/js/searchtools.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index f941b0d1056..bd2ab2ff6ad 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -8,18 +8,18 @@ describe('Basic html theme search', function() { } function checkRanking(expectedRanking, results) { - nextExpected = expectedRanking.pop(0); + let [nextExpected, ...remainingItems] = expectedRanking.reverse(); results.forEach(result => { let [expectedPage, expectedTitle, expectedTarget] = nextExpected; let [page, title, target] = result; if (page == expectedPage && title == expectedTitle && target == expectedTarget) { - nextExpected = expectedRanking.pop(0); + [nextExpected, ...remainingItems] = remainingItems; } }); - expect(expectedRanking.length).toEqual(0); + expect(remainingItems.length).toEqual(0); } describe('terms search', function() { From bf576cd349808cbe3b5b661669a517e8ee4bcf9a Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:14:36 +0100 Subject: [PATCH 14/24] Tests: refactor checkRanking function to use JavaScript ``for...of`` loop. --- tests/js/searchtools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index bd2ab2ff6ad..2db5d460345 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -10,14 +10,14 @@ describe('Basic html theme search', function() { function checkRanking(expectedRanking, results) { let [nextExpected, ...remainingItems] = expectedRanking.reverse(); - results.forEach(result => { + for (result of results) { let [expectedPage, expectedTitle, expectedTarget] = nextExpected; let [page, title, target] = result; if (page == expectedPage && title == expectedTitle && target == expectedTarget) { [nextExpected, ...remainingItems] = remainingItems; } - }); + } expect(remainingItems.length).toEqual(0); } From d1a71974b68d6160fe80ff9317312d0a956b7be5 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:18:19 +0100 Subject: [PATCH 15/24] Tests: add early-exit path to checkRanking function. --- tests/js/searchtools.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index 2db5d460345..aab7ced8e1b 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -11,6 +11,8 @@ describe('Basic html theme search', function() { let [nextExpected, ...remainingItems] = expectedRanking.reverse(); for (result of results) { + if (!nextExpected) break; + let [expectedPage, expectedTitle, expectedTarget] = nextExpected; let [page, title, target] = result; From 5d5b079a2a6e464741a669461ee41fd80d08a409 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:23:19 +0100 Subject: [PATCH 16/24] Tests: extract two distinct relevance-related test cases from existing single test case. --- tests/js/searchtools.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index aab7ced8e1b..7081693e83f 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -110,6 +110,19 @@ describe('Basic html theme search', function() { expectedRanking = [ ['index', 'relevance', '#module-relevance'], /* py:module documentation */ ['relevance', 'Relevance', '#relevance'], /* main title */ + ]; + + searchParameters = Search._parseQuery('relevance'); + results = Search._performSearch(...searchParameters); + + checkRanking(expectedRanking, results); + }); + + it('should score an main-title match above a subheading-title match', function() { + eval(loadFixture("titles/searchindex.js")); + + expectedRanking = [ + ['relevance', 'Relevance', '#relevance'], /* main title */ ['index', 'Main Page > Relevance', '#relevance'], /* subsection heading title */ ]; From e9bdf2f2fdbfd9e63cc25653368b9535cc7cdb83 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:29:13 +0100 Subject: [PATCH 17/24] Tests: nitpick: reverse the ``results`` array instead of reversing the ``expectedRanking`` array. --- tests/js/searchtools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index 7081693e83f..f92f6df0138 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -8,9 +8,9 @@ describe('Basic html theme search', function() { } function checkRanking(expectedRanking, results) { - let [nextExpected, ...remainingItems] = expectedRanking.reverse(); + let [nextExpected, ...remainingItems] = expectedRanking; - for (result of results) { + for (result of results.reverse()) { if (!nextExpected) break; let [expectedPage, expectedTitle, expectedTarget] = nextExpected; From e75891edbc056dea55af825f6429c83b04eadbd8 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:32:43 +0100 Subject: [PATCH 18/24] Regenerate JS test search fixtures using ``utils/generate_js_fixtures.py`` Relates-to merge commit 7f8a5f655ba1872e563e21ee43b94ae629bcde9f. --- tests/js/fixtures/titles/searchindex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 72daaa39d0e..ace33fdc98b 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, "main-page"]], "Relevance": [[0, "relevance"], [1, "relevance"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file From d5d8717f8c4d8d1675914d8e863839d33be1b6b6 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 8 Jul 2024 14:42:45 +0100 Subject: [PATCH 19/24] Tests: update expectations since main titles now have empty anchor targets. Relates-to merge commit 7f8a5f655ba1872e563e21ee43b94ae629bcde9f and test index regeneration commit e75891edbc056dea55af825f6429c83b04eadbd8. --- tests/js/searchtools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index f92f6df0138..51ad36fad62 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -109,7 +109,7 @@ describe('Basic html theme search', function() { expectedRanking = [ ['index', 'relevance', '#module-relevance'], /* py:module documentation */ - ['relevance', 'Relevance', '#relevance'], /* main title */ + ['relevance', 'Relevance', ''], /* main title */ ]; searchParameters = Search._parseQuery('relevance'); @@ -122,7 +122,7 @@ describe('Basic html theme search', function() { eval(loadFixture("titles/searchindex.js")); expectedRanking = [ - ['relevance', 'Relevance', '#relevance'], /* main title */ + ['relevance', 'Relevance', ''], /* main title */ ['index', 'Main Page > Relevance', '#relevance'], /* subsection heading title */ ]; From 388aef3609e57c3e9ee168ef2bbcc2c3859926c5 Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 9 Jul 2024 15:51:24 +0100 Subject: [PATCH 20/24] Code review: apply phrasing/typo-fixup suggestions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Will Lachance Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- CHANGES.rst | 5 ++--- tests/js/searchtools.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8931046f379..f084835d807 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -93,9 +93,8 @@ Bugs fixed removing duplication of search results. Patch by James Addison. * #12391: Adjust scoring of matches during HTML search so that document main - titles tend to rank more highly than subsection titles, and also to provide - a gain to matches on the name of programming domain objects relative to - title/subtitle matches. + titles tend to rank higher than subsection titles. In addition, boost matches + on the name of programming domain objects relative to title/subtitle matches. Patch by James Addison and Will Lachance. Testing diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index 51ad36fad62..fc1e4f5c93e 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -118,7 +118,7 @@ describe('Basic html theme search', function() { checkRanking(expectedRanking, results); }); - it('should score an main-title match above a subheading-title match', function() { + it('should score a main-title match above a subheading-title match', function() { eval(loadFixture("titles/searchindex.js")); expectedRanking = [ From 4d819cc3d42d485568d3934365babbc08b0296bd Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 9 Jul 2024 15:53:56 +0100 Subject: [PATCH 21/24] Value-safety: use ``const`` for scoring variables. Co-authored-by: Will Lachance --- sphinx/themes/basic/static/searchtools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 02fd46289fb..b08d58c9b9b 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -328,8 +328,8 @@ const Search = { for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { - let score = Math.round(Scorer.title * queryLower.length / title.length); - let boost = titles[file] === title ? 1 : 0; // add a boost for document titles + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles normalResults.push([ docNames[file], titles[file] !== title ? `${titles[file]} > ${title}` : title, From 9d59eaf3853f2174d27cf0c074b1a74c5e99de9f Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 9 Jul 2024 17:02:41 +0100 Subject: [PATCH 22/24] Tests: self-documentation: add comment to describe the purpose of the search relevance tests. --- tests/js/searchtools.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index fc1e4f5c93e..f10ae365271 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -104,6 +104,20 @@ describe('Basic html theme search', function() { describe('search result ranking', function() { + /* + * These tests should not proscribe precise expected ordering of search + * results; instead each test case should describe a single relevance rule + * that helps users to locate relevant information efficiently. + * + * If you think that one of the rules seems to be poorly-defined or is + * limiting the potential for search algorithm improvements, please check + * for existing discussion/bugreports related to it on GitHub[1] before + * creating one yourself. Suggestions for possible improvements are also + * welcome. + * + * [1] - https://github.com/sphinx-doc/sphinx.git/ + */ + it('should score an object-name match above a page-title match', function() { eval(loadFixture("titles/searchindex.js")); From 7b772a2ae2fc48b455c2e3eca1306c50ceabe593 Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 9 Jul 2024 17:21:33 +0100 Subject: [PATCH 23/24] Code review feedback: add class-level attribute with same name as a (main) title. --- tests/js/fixtures/titles/searchindex.js | 2 +- tests/js/roots/titles/relevance.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index ace33fdc98b..56855ca9a1b 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "attribut": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "docstr": 0, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file diff --git a/tests/js/roots/titles/relevance.py b/tests/js/roots/titles/relevance.py index 95f1aecbcd7..c4d0eec557f 100644 --- a/tests/js/roots/titles/relevance.py +++ b/tests/js/roots/titles/relevance.py @@ -2,3 +2,6 @@ class Example: """Example class""" num_attribute = 5 text_attribute = "string" + + relevance = "testing" + """attribute docstring""" From 4e07078c618f541b04d0f3e970d97ebeeac6f330 Mon Sep 17 00:00:00 2001 From: James Addison Date: Wed, 10 Jul 2024 16:55:09 +0100 Subject: [PATCH 24/24] Tests: split object-vs-title matching into two distinct rules. --- tests/js/searchtools.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js index f10ae365271..a71047dae9f 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.js @@ -118,7 +118,7 @@ describe('Basic html theme search', function() { * [1] - https://github.com/sphinx-doc/sphinx.git/ */ - it('should score an object-name match above a page-title match', function() { + it('should score a code module match above a page-title match', function() { eval(loadFixture("titles/searchindex.js")); expectedRanking = [ @@ -132,6 +132,20 @@ describe('Basic html theme search', function() { checkRanking(expectedRanking, results); }); + it('should score a main-title match above an object member match', function() { + eval(loadFixture("titles/searchindex.js")); + + expectedRanking = [ + ['relevance', 'Relevance', ''], /* main title */ + ['index', 'relevance.Example.relevance', '#module-relevance'], /* py:class attribute */ + ]; + + searchParameters = Search._parseQuery('relevance'); + results = Search._performSearch(...searchParameters); + + checkRanking(expectedRanking, results); + }); + it('should score a main-title match above a subheading-title match', function() { eval(loadFixture("titles/searchindex.js"));