This is an automated email from the ASF dual-hosted git repository.
dsmiley pushed a commit to branch branch_9_11
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9_11 by this push:
new 87173ebc34c SOLR-17870 - UnsupportedOperationException Fix - Return
Partial Results collations when query Limit exceeded in SpellCheckCollator
(#3496)
87173ebc34c is described below
commit 87173ebc34c66ed75cda98993812c968911046ea
Author: Puneet Sharma <[email protected]>
AuthorDate: Sun Sep 13 21:44:57 2026 -0400
SOLR-17870 - UnsupportedOperationException Fix - Return Partial Results
collations when query Limit exceeded in SpellCheckCollator (#3496)
Co-authored-by: Puneet Sharma <[email protected]>
Co-authored-by: Claude <[email protected]>
(cherry picked from commit 751c858ba7af3ff3afb59f9323bd095dff19a088)
(cherry picked from commit 1fae678fc992ac806d1d08a13040c135bd4d3dd0)
(cherry picked from commit 3026efdc675d2e073dd98c1b205082ccf8654780)
---
...R-17870-partial-spellcheck-collator-results.yml | 10 ++
.../apache/solr/spelling/SpellCheckCollator.java | 2 +-
.../solr/spelling/SpellCheckCollatorTest.java | 108 +++++++++++++++++++++
3 files changed, 119 insertions(+), 1 deletion(-)
diff --git
a/changelog/unreleased/SOLR-17870-partial-spellcheck-collator-results.yml
b/changelog/unreleased/SOLR-17870-partial-spellcheck-collator-results.yml
new file mode 100644
index 00000000000..72ba578c495
--- /dev/null
+++ b/changelog/unreleased/SOLR-17870-partial-spellcheck-collator-results.yml
@@ -0,0 +1,10 @@
+title: >
+ SpellCheckCollator now returns the partial (mutable) list of collations it
had gathered so far,
+ instead of throwing an UnsupportedOperationException, when the query time
limit is exceeded
+ while collating results.
+type: fixed
+authors:
+ - name: Puneet Sharma
+links:
+ - name: SOLR-17870
+ url: https://issues.apache.org/jira/browse/SOLR-17870
diff --git
a/solr/core/src/java/org/apache/solr/spelling/SpellCheckCollator.java
b/solr/core/src/java/org/apache/solr/spelling/SpellCheckCollator.java
index fd5f465d098..344088c83ca 100644
--- a/solr/core/src/java/org/apache/solr/spelling/SpellCheckCollator.java
+++ b/solr/core/src/java/org/apache/solr/spelling/SpellCheckCollator.java
@@ -96,7 +96,7 @@ public class SpellCheckCollator {
while (tryNo < maxTries && collNo < maxCollations &&
possibilityIter.hasNext()) {
if (queryLimits.maybeExitWithPartialResults("SpellCheck collator")) {
- return List.of();
+ return collations;
}
PossibilityIterator.RankedSpellPossibility possibility =
possibilityIter.next();
diff --git
a/solr/core/src/test/org/apache/solr/spelling/SpellCheckCollatorTest.java
b/solr/core/src/test/org/apache/solr/spelling/SpellCheckCollatorTest.java
index f7687441d02..de5fa783814 100644
--- a/solr/core/src/test/org/apache/solr/spelling/SpellCheckCollatorTest.java
+++ b/solr/core/src/test/org/apache/solr/spelling/SpellCheckCollatorTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.solr.spelling;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -31,12 +32,17 @@ import org.apache.solr.common.params.SpellingParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.SolrCore;
+import org.apache.solr.handler.component.QueryComponent;
+import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.handler.component.SpellCheckComponent;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
+import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.search.QueryLimit;
+import org.apache.solr.util.TestInjection;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -756,4 +762,106 @@ public class SpellCheckCollatorTest extends
SolrTestCaseJ4 {
List<?> collations = (List<?>) collationList.getAll("collation");
assertEquals(1, collations.size());
}
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testCollationWithQueryLimitsSupportsPartialResults() throws
Exception {
+ // Create a SpellingResult with multiple suggestions to test collation
+ SpellingResult result = new SpellingResult();
+
+ // Add tokens with multiple suggestions to generate many possible
collations
+ Token token1 = new Token();
+ token1.copyBuffer("test".toCharArray(), 0, 4);
+ token1.setOffset(0, 4);
+ result.add(token1, "best", 1);
+ result.add(token1, "rest", 2);
+ result.add(token1, "nest", 3);
+ result.add(token1, "fest", 4);
+
+ Token token2 = new Token();
+ token2.copyBuffer("query".toCharArray(), 0, 5);
+ token2.setOffset(5, 10);
+ result.add(token2, "quarry", 1);
+ result.add(token2, "quiry", 2);
+ result.add(token2, "quary", 3);
+ result.add(token2, "querry", 4);
+
+ // Create a QueryLimit that will trigger during collation processing
+ final int[] callCount = {0};
+ TestInjection.queryTimeout =
+ new QueryLimit() {
+ @Override
+ public boolean shouldExit() {
+ callCount[0]++;
+ // Return true after a few calls to force early exit during
collation
+ return callCount[0] > 3;
+ }
+
+ @Override
+ public Object currentValue() {
+ return callCount[0];
+ }
+ };
+
+ try {
+ SolrCore core = h.getCore();
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ params.add(CommonParams.Q, "test query");
+ params.add(CommonParams.DF, "teststop");
+
+ SolrQueryRequest req = new LocalSolrQueryRequest(core, params);
+ SolrQueryResponse response = new SolrQueryResponse();
+ response.addResponseHeader(new SimpleOrderedMap<>());
+
+ // Set up proper request context for QueryLimits
+ SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, response));
+
+ try {
+ // Create ResponseBuilder with QueryComponent for collation
+ List<SearchComponent> components = new ArrayList<>();
+ QueryComponent queryComponent = new QueryComponent();
+ components.add(queryComponent);
+
+ ResponseBuilder rb = new ResponseBuilder(req, response, components);
+
+ // Create SpellCheckCollator with settings for multiple collations
+ SpellCheckCollator collator = new SpellCheckCollator();
+ collator.setMaxCollationTries(0); // Disable query verification
+ collator.setMaxCollations(20); // High number to ensure partial results
+
+ // This should trigger the maybeExitWithPartialResults condition
+ List<SpellCheckCollation> collations = collator.collate(result, "test
query", rb);
+
+ // Verify that we got partial results due to timeout
+ assertNotNull("Collations should not be null", collations);
+ assertTrue("Should have some collations before timeout",
collations.size() > 0);
+ assertTrue("Should have partial results due to timeout",
collations.size() < 20);
+
+ // Verify that the timeout was triggered
+ assertTrue("Timeout should have been triggered", callCount[0] > 3);
+
+ // Verify that the response is marked as partial
+ Object partialResults =
response.getResponseHeader().get("partialResults");
+ assertTrue(
+ "Response should be marked as partial when query limits are
exceeded",
+ partialResults != null && Boolean.TRUE.equals(partialResults));
+
+ // Most importantly: verify that the list is mutable (supports partial
results)
+ try {
+ collations.add(new SpellCheckCollation());
+ // If we get here, the list is mutable - this is the key requirement
+ } catch (UnsupportedOperationException e) {
+ fail("Collations list should be mutable to support partial results");
+ }
+
+ } finally {
+ req.close();
+ }
+
+ } finally {
+ // Clean up
+ TestInjection.queryTimeout = null;
+ SolrRequestInfo.clearRequestInfo();
+ }
+ }
}