From e6c89ea9262ce1cb025ba2a745c06ce27fcf9916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Re=C3=A9?= Date: Wed, 29 Jul 2026 17:02:52 +0200 Subject: [PATCH 1/2] Local search: path-scoped suggestions, explicit path_prefix wins (#101) Port of the local-scoping fixes from the feature-ai-rag line (#100), without the AI-search parts: - @solr-suggest accepts an optional path_prefix and filters with path_parents, so the livesearch suggestions honor a subtree scope like the search results do (integration test included; pathPrefix argument on the solrSearchSuggestions action). - searchPathPrefix(): an explicit path_prefix URL param wins over the getPathPrefix heuristic, which treats every single-segment path as a language root and silently dropped the prefix for top-level subsites and workspaces. The multilingual path handling neutralizing path_prefix on non-PAM sites (#101 item 3) remains open. --- backend/src/kitconcept/solr/services/suggest.py | 7 +++++++ backend/tests/services/suggest/test_suggest.py | 10 ++++++++++ frontend/packages/volto-solr/news/101.bugfix | 1 + .../actions/solrsearch/solrSearchSuggestions.js | 14 ++++++++++++-- .../src/components/theme/SolrSearch/SolrSearch.jsx | 9 ++++++++- 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 frontend/packages/volto-solr/news/101.bugfix diff --git a/backend/src/kitconcept/solr/services/suggest.py b/backend/src/kitconcept/solr/services/suggest.py index 20f2e5e8..3da156e7 100644 --- a/backend/src/kitconcept/solr/services/suggest.py +++ b/backend/src/kitconcept/solr/services/suggest.py @@ -58,6 +58,13 @@ def query_suggest(self, query): if lang: d["fq"] = d["fq"] + ["Language:(" + escape(lang) + ")"] + # Optional path scoping: restricts suggestions to a subtree, + # e.g. a subsite or workspace, matching the local search. + if path_prefix := self.request.form.get("path_prefix", "").strip(): + portal_path = "/".join(api.portal.get().getPhysicalPath()) + prefix = portal_path + path_prefix.rstrip("/") + d["fq"] = d["fq"] + [f'path_parents:"{prefix}"'] + d["fq"] = " AND ".join(d["fq"]) querystring = urllib.parse.urlencode(d) url = "{}/{}".format(connection.solrBase, f"suggest?{querystring}") diff --git a/backend/tests/services/suggest/test_suggest.py b/backend/tests/services/suggest/test_suggest.py index ba2489e7..e2a93db4 100644 --- a/backend/tests/services/suggest/test_suggest.py +++ b/backend/tests/services/suggest/test_suggest.py @@ -91,3 +91,13 @@ def test_suggest_result_props( get_suggest_result_props(expected_dict).items() <= get_suggest_result_props(get_suggest_item(self.data, index)).items() ) + + +class TestSuggestPathPrefix(TestSuggestDefault): + """path_prefix restricts suggestions to a subtree (e.g. a workspace).""" + + url = "/@solr-suggest?query=chomsky&path_prefix=/mydocument" + + def test_only_prefixed_results(self, get_suggest_result_path): + paths = [get_suggest_result_path(item) for item in self.data["suggestions"]] + assert paths == ["/plone/mydocument"] diff --git a/frontend/packages/volto-solr/news/101.bugfix b/frontend/packages/volto-solr/news/101.bugfix new file mode 100644 index 00000000..d587f9a7 --- /dev/null +++ b/frontend/packages/volto-solr/news/101.bugfix @@ -0,0 +1 @@ +Local search fixes: @solr-suggest accepts an optional path_prefix so suggestions honor a subtree scope, and an explicit path_prefix URL param wins over the getPathPrefix heuristic, which silently dropped top-level (single-segment) prefixes. @reebalazs diff --git a/frontend/packages/volto-solr/src/actions/solrsearch/solrSearchSuggestions.js b/frontend/packages/volto-solr/src/actions/solrsearch/solrSearchSuggestions.js index c686733f..ac87b413 100644 --- a/frontend/packages/volto-solr/src/actions/solrsearch/solrSearchSuggestions.js +++ b/frontend/packages/volto-solr/src/actions/solrsearch/solrSearchSuggestions.js @@ -1,11 +1,21 @@ export const GET_SOLR_SEARCH_SUGGESTIONS = 'GET_SOLR_SEARCH_SUGGESTIONS'; -export function solrSearchSuggestions(term) { +/** + * Fetch live search suggestions. + * @param {string} term The (url-encoded) search term. + * @param {string=} pathPrefix Optional path to restrict the suggestions + * to a subtree (e.g. a subsite or workspace). + */ +export function solrSearchSuggestions(term, pathPrefix) { + const params = [`query=${term}`]; + if (pathPrefix) { + params.push(`path_prefix=${encodeURIComponent(pathPrefix)}`); + } return { type: GET_SOLR_SEARCH_SUGGESTIONS, request: { op: 'get', - path: `/@solr-suggest?query=${term}`, + path: `/@solr-suggest?${params.join('&')}`, }, }; } diff --git a/frontend/packages/volto-solr/src/components/theme/SolrSearch/SolrSearch.jsx b/frontend/packages/volto-solr/src/components/theme/SolrSearch/SolrSearch.jsx index 03b04fe9..32c7b232 100644 --- a/frontend/packages/volto-solr/src/components/theme/SolrSearch/SolrSearch.jsx +++ b/frontend/packages/volto-solr/src/components/theme/SolrSearch/SolrSearch.jsx @@ -269,11 +269,18 @@ class SolrSearch extends Component { ...params, sort_on: params.sort_on !== 'relevance' ? params.sort_on : '', b_start: (this.state.currentPage - 1) * config.settings.defaultPageSize, - path_prefix: getPathPrefix(window.location), + path_prefix: this.searchPathPrefix(params), doEmptySearch: this.props.doEmptySearch, }); }; + // An explicit path_prefix URL param wins over the URL heuristic: + // getPathPrefix treats every single-segment path as a language root + // (/de, /en), so a top-level subsite or workspace (/my-workspace) + // would silently lose its prefix. + searchPathPrefix = (params) => + params.path_prefix || getPathPrefix(window.location); + updateSearch = () => { this.props.history.replace({ search: qs.stringify(queryStateToParams(this.state)), From 334d857bbd49ed58d75b96cbcd0a575355fe8160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Re=C3=A9?= Date: Wed, 29 Jul 2026 17:05:43 +0200 Subject: [PATCH 2/2] Add backend news fragment (#101) --- backend/news/101.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 backend/news/101.bugfix diff --git a/backend/news/101.bugfix b/backend/news/101.bugfix new file mode 100644 index 00000000..52f460ef --- /dev/null +++ b/backend/news/101.bugfix @@ -0,0 +1 @@ +@solr-suggest accepts an optional path_prefix (path_parents filter), so livesearch suggestions honor a subtree scope like the search results do. @reebalazs