From 6b4fb06d0af41ce9da3cb8de822a4d750aba9b2c Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Fri, 2 Jan 2026 22:24:30 -0500 Subject: [PATCH 1/6] feat: enable spellcheck suggestions with new `spellcheck` field --- solr/cores/conf/schema.xml | 17 +++++++++++++++-- solr/cores/conf/solrconfig.xml | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/solr/cores/conf/schema.xml b/solr/cores/conf/schema.xml index c42b48d8..93abed83 100644 --- a/solr/cores/conf/schema.xml +++ b/solr/cores/conf/schema.xml @@ -79,16 +79,29 @@ - + + + + + + + + + + + + - + + + \ No newline at end of file diff --git a/solr/cores/conf/solrconfig.xml b/solr/cores/conf/solrconfig.xml index 86d0a5e2..76256f92 100644 --- a/solr/cores/conf/solrconfig.xml +++ b/solr/cores/conf/solrconfig.xml @@ -72,7 +72,16 @@ explicit 10 text + + true + default + true + true + + + spellcheck + @@ -84,6 +93,20 @@ text + + + + + default + spellcheck + solr.DirectSolrSpellChecker + 3 + 0.3 + 2 + internal + + + text From 79c1c615ee5eafaa0cdb027fda176ddb8aabf0bb Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Fri, 2 Jan 2026 22:33:32 -0500 Subject: [PATCH 2/6] feat: add spellcheck suggestion handling for Solr queries - Adds `spellcheck_suggestion` to the context - If the current query returns no results, re-queries Solr using the suggested query --- .../apps/instruments/views/instrument_list.py | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/web-app/django/VIM/apps/instruments/views/instrument_list.py b/web-app/django/VIM/apps/instruments/views/instrument_list.py index 3dce35b8..b3d07697 100644 --- a/web-app/django/VIM/apps/instruments/views/instrument_list.py +++ b/web-app/django/VIM/apps/instruments/views/instrument_list.py @@ -141,6 +141,7 @@ def get_context_data(self, **kwargs): instruments, has_other_pages, facet_data, + spellcheck_suggestion, ) = self._paginate_solr_results(page_size) # Add pagination data to context @@ -163,6 +164,9 @@ def get_context_data(self, **kwargs): context["hbs_facet"] = hbs_facet context["search_query"] = search_query if search_query else None + # Add spellcheck suggestion to context + context["spellcheck_suggestion"] = spellcheck_suggestion + # Get contextual HBS facets (respects current search) hbs_facet_list = self._get_contextual_hbs_facets(facet_data) context["hbs_facets"] = hbs_facet_list @@ -278,6 +282,30 @@ def _get_solr_page_results( ] total_count = solr_response.hits # pysolr's hits corresponds to Solr's numFound + spellcheck_suggestion = None + if total_count == 0: # only attempt correction if no results + raw = solr_response.raw_response + spellcheck = raw.get("spellcheck", {}) + collations = spellcheck.get("collations", []) + if len(collations) >= 2: + spellcheck_suggestion = collations[1] + + if spellcheck_suggestion: + corrected_params = solr_params.copy() + corrected_params["q"] = spellcheck_suggestion + + corrected_response = solr.search(**corrected_params) + + if corrected_response.hits > 0: + instruments = [ + SolrInstrument(doc, lang_code=lang_code) + for doc in corrected_response.docs + ] + total_count = corrected_response.hits + else: + # If suggestion also fails, keep as zero results + spellcheck_suggestion = None + # Return facet data if available facet_data = None if hasattr(solr_response, "facets") and solr_response.facets: @@ -285,7 +313,7 @@ def _get_solr_page_results( "hbs_prim_cat_s,hbs_prim_cat_label_s", [] ) - return instruments, total_count, facet_data + return instruments, total_count, facet_data, spellcheck_suggestion def _get_contextual_hbs_facets(self, contextual_facet_data): """Get HBS facets showing all categories with contextual counts (including zero).""" @@ -358,15 +386,25 @@ def _paginate_solr_results(self, page_size): query_params = self._build_solr_query(language, include_facets=True) # Get page results, total count, and facet data in one query - page_results, total_count, facet_data = self._get_solr_page_results( - solr, query_params, page_size, start - ) + ( + page_results, + total_count, + facet_data, + spellcheck_suggestion, + ) = self._get_solr_page_results(solr, query_params, page_size, start) # Create paginator and page objects paginator = SolrPaginator(page_results, page_size, total_count) page = Page(page_results, page_number, paginator) - return (paginator, page, page_results, page.has_other_pages(), facet_data) + return ( + paginator, + page, + page_results, + page.has_other_pages(), + facet_data, + spellcheck_suggestion, + ) def get(self, request, *args, **kwargs): language_en = request.GET.get("language", None) From ae96d8c0cd4dede780d4efb0fcce9f2ee4fc798d Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Fri, 2 Jan 2026 22:39:25 -0500 Subject: [PATCH 3/6] feat: display spellcheck suggestion in the frontend - Updates template logic to differentiate between: - No results at all - Results for the suggested spellcheck query - Results for the original search --- .../VIM/templates/instruments/index.html | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/web-app/django/VIM/templates/instruments/index.html b/web-app/django/VIM/templates/instruments/index.html index aa2c41ee..4dff17bd 100644 --- a/web-app/django/VIM/templates/instruments/index.html +++ b/web-app/django/VIM/templates/instruments/index.html @@ -169,11 +169,28 @@

- Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} entries - {% if hbs_facet_name %}for{% endif %} - {{ hbs_facet_name }} - {% if search_query %} - for search query "{{ search_query }}" + + {% if page_obj.paginator.count == 0 %} + No results found + + {% elif spellcheck_suggestion %} + Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} + {% if hbs_facet_name %} + for {{ hbs_facet_name }} + {% endif %} + {% if search_query %} + for search query "{{ spellcheck_suggestion }}", as no results were found for "{{ search_query }}". + {% endif %} + + {% else %} + Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} + {% if hbs_facet_name %} + for {{ hbs_facet_name }} + {% endif %} + {% if search_query %} + for search query "{{ search_query }}" + {% endif %} + {% endif %} {% endif %} From 4b0378b9649ac26e6491891b24f6ac7ed7d8ff3f Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Sat, 3 Jan 2026 08:43:59 -0500 Subject: [PATCH 4/6] fix: correct facet handling when applying spellcheck suggestions - spellcheck-based re-query for HBS facets - updates UI messaging --- .../apps/instruments/views/instrument_list.py | 73 +++++++++---------- .../VIM/templates/instruments/index.html | 20 ++--- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/web-app/django/VIM/apps/instruments/views/instrument_list.py b/web-app/django/VIM/apps/instruments/views/instrument_list.py index b3d07697..e6d384b2 100644 --- a/web-app/django/VIM/apps/instruments/views/instrument_list.py +++ b/web-app/django/VIM/apps/instruments/views/instrument_list.py @@ -141,7 +141,7 @@ def get_context_data(self, **kwargs): instruments, has_other_pages, facet_data, - spellcheck_suggestion, + effective_query, ) = self._paginate_solr_results(page_size) # Add pagination data to context @@ -165,7 +165,7 @@ def get_context_data(self, **kwargs): context["search_query"] = search_query if search_query else None # Add spellcheck suggestion to context - context["spellcheck_suggestion"] = spellcheck_suggestion + context["effective_query"] = effective_query # Get contextual HBS facets (respects current search) hbs_facet_list = self._get_contextual_hbs_facets(facet_data) @@ -267,7 +267,7 @@ def _build_solr_query(self, language: Language, include_facets: bool = False): def _get_solr_page_results( self, solr, query_params: dict, page_size: int, start: int ): - """Get a specific page of Solr search results with filter queries and total count.""" + """Get a specific page of Solr search results with consistent facets and spellcheck support.""" solr_params = { **query_params, "rows": page_size, @@ -276,44 +276,43 @@ def _get_solr_page_results( # Remove our custom params lang_code = solr_params.pop("lang_code") - solr_response = solr.search(**solr_params) - instruments = [ - SolrInstrument(doc, lang_code=lang_code) for doc in solr_response.docs - ] - total_count = solr_response.hits # pysolr's hits corresponds to Solr's numFound + effective_query = solr_params.get("q") + + # Execute initial query + response = solr.search(**solr_params) - spellcheck_suggestion = None - if total_count == 0: # only attempt correction if no results - raw = solr_response.raw_response + # Attempt spellcheck if no results + if response.hits == 0: + raw = response.raw_response spellcheck = raw.get("spellcheck", {}) collations = spellcheck.get("collations", []) if len(collations) >= 2: spellcheck_suggestion = collations[1] - - if spellcheck_suggestion: - corrected_params = solr_params.copy() - corrected_params["q"] = spellcheck_suggestion - - corrected_response = solr.search(**corrected_params) - - if corrected_response.hits > 0: - instruments = [ - SolrInstrument(doc, lang_code=lang_code) - for doc in corrected_response.docs - ] - total_count = corrected_response.hits - else: - # If suggestion also fails, keep as zero results - spellcheck_suggestion = None - - # Return facet data if available - facet_data = None - if hasattr(solr_response, "facets") and solr_response.facets: - facet_data = solr_response.facets.get("facet_pivot", {}).get( - "hbs_prim_cat_s,hbs_prim_cat_label_s", [] - ) - return instruments, total_count, facet_data, spellcheck_suggestion + corrected_params = solr_params.copy() + corrected_params["q"] = spellcheck_suggestion + + corrected_response = solr.search(**corrected_params) + + if corrected_response.hits > 0: + response = corrected_response + effective_query = spellcheck_suggestion + + instruments = [ + SolrInstrument(doc, lang_code=lang_code) for doc in response.docs + ] + total_count = response.hits + + facet_data = response.facets.get("facet_pivot", {}).get( + "hbs_prim_cat_s,hbs_prim_cat_label_s", [] + ) + + return ( + instruments, + total_count, + facet_data, + effective_query, + ) def _get_contextual_hbs_facets(self, contextual_facet_data): """Get HBS facets showing all categories with contextual counts (including zero).""" @@ -390,7 +389,7 @@ def _paginate_solr_results(self, page_size): page_results, total_count, facet_data, - spellcheck_suggestion, + effective_query, ) = self._get_solr_page_results(solr, query_params, page_size, start) # Create paginator and page objects @@ -403,7 +402,7 @@ def _paginate_solr_results(self, page_size): page_results, page.has_other_pages(), facet_data, - spellcheck_suggestion, + effective_query, ) def get(self, request, *args, **kwargs): diff --git a/web-app/django/VIM/templates/instruments/index.html b/web-app/django/VIM/templates/instruments/index.html index 4dff17bd..6503c750 100644 --- a/web-app/django/VIM/templates/instruments/index.html +++ b/web-app/django/VIM/templates/instruments/index.html @@ -172,21 +172,21 @@

{% if page_obj.paginator.count == 0 %} No results found - - {% elif spellcheck_suggestion %} - Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} - {% if hbs_facet_name %} - for {{ hbs_facet_name }} + {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} + {% if search_query %} + for search query "{{ search_query }}" {% endif %} + + {% elif effective_query != search_query %} + Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} + {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} {% if search_query %} - for search query "{{ spellcheck_suggestion }}", as no results were found for "{{ search_query }}". + for search query "{{ effective_query }}", as no results were found for "{{ search_query }}". {% endif %} - + {% else %} Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} - {% if hbs_facet_name %} - for {{ hbs_facet_name }} - {% endif %} + {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} {% if search_query %} for search query "{{ search_query }}" {% endif %} From c5b108f501c3b767c8a8b1d7f88abeca316e3b14 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Sat, 3 Jan 2026 09:21:10 -0500 Subject: [PATCH 5/6] fix: add `notranslate` functionality to queries --- web-app/django/VIM/templates/instruments/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web-app/django/VIM/templates/instruments/index.html b/web-app/django/VIM/templates/instruments/index.html index 6503c750..e22b08f0 100644 --- a/web-app/django/VIM/templates/instruments/index.html +++ b/web-app/django/VIM/templates/instruments/index.html @@ -174,21 +174,21 @@

No results found {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} {% if search_query %} - for search query "{{ search_query }}" + for search query "{{ search_query }}". {% endif %} {% elif effective_query != search_query %} Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} {% if search_query %} - for search query "{{ effective_query }}", as no results were found for "{{ search_query }}". + for search query "{{ effective_query }}", as no results were found for "{{ search_query }}". {% endif %} {% else %} Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} {% if hbs_facet_name %}for {{ hbs_facet_name }}{% endif %} {% if search_query %} - for search query "{{ search_query }}" + for search query "{{ search_query }}". {% endif %} {% endif %} {% endif %} From f64de070348b0122c2f7bbb32c88fb1231dfef56 Mon Sep 17 00:00:00 2001 From: PouyaMohseni Date: Sat, 3 Jan 2026 09:22:15 -0500 Subject: [PATCH 6/6] fix: pass `spellcheck.q` to enable case-insensitive spellcheck --- web-app/django/VIM/apps/instruments/views/instrument_list.py | 1 + web-app/django/VIM/templates/instruments/index.html | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/web-app/django/VIM/apps/instruments/views/instrument_list.py b/web-app/django/VIM/apps/instruments/views/instrument_list.py index e6d384b2..f847d23b 100644 --- a/web-app/django/VIM/apps/instruments/views/instrument_list.py +++ b/web-app/django/VIM/apps/instruments/views/instrument_list.py @@ -277,6 +277,7 @@ def _get_solr_page_results( lang_code = solr_params.pop("lang_code") effective_query = solr_params.get("q") + solr_params["spellcheck.q"] = effective_query # Execute initial query response = solr.search(**solr_params) diff --git a/web-app/django/VIM/templates/instruments/index.html b/web-app/django/VIM/templates/instruments/index.html index e22b08f0..8a8dd30f 100644 --- a/web-app/django/VIM/templates/instruments/index.html +++ b/web-app/django/VIM/templates/instruments/index.html @@ -191,7 +191,6 @@

for search query "{{ search_query }}". {% endif %} {% endif %} - {% endif %} {% include "instruments/includes/masonryView.html" %}