From 5e0be892ffcee15858b275bea125605c410f3b69 Mon Sep 17 00:00:00 2001 From: Renato Haeberli Date: Fri, 29 May 2026 17:05:38 +0200 Subject: [PATCH 1/5] skip empty BooleanQuery with Occur=SHOULD --- .../org/apache/solr/search/join/FiltersQParser.java | 9 ++++++++- .../apache/solr/search/TestMmBoolQParserPlugin.java | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java index 451de63337a0..e706308b6649 100644 --- a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java +++ b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java @@ -59,7 +59,14 @@ protected BooleanQuery parseImpl() throws SyntaxError { BooleanQuery.Builder builder = createBuilder(); for (Map.Entry clause : clauses.entrySet()) { - builder.add(unwrapQuery(clause.getKey().getQuery(), clause.getValue()), clause.getValue()); + + Query query = clause.getKey().getQuery(); + Occur occur = clause.getValue(); + + if (occur == Occur.SHOULD && query instanceof BooleanQuery boolQ && boolQ.clauses().isEmpty()) { + continue; + } + builder.add(unwrapQuery(query, occur), occur); } // what about empty query? return builder.build(); diff --git a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java index 3a2d976a97cb..4e81ca279973 100644 --- a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java +++ b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java @@ -109,6 +109,17 @@ public void testMinShouldMatchThresholdsLower() throws Exception { assertEquals(expected, actual); } + @Test + public void testMinShouldMatchWithEmptyClauseCausedByStopWord() throws Exception { + + Query actual = + parseQuery(req("q", "{!bool should=name:foo should=name:bar should=name_sw:to mm=-1}")); + + BooleanQuery expected = shouldBuilder("foo", "bar").setMinimumNumberShouldMatch(1).build(); + + assertEquals(expected, actual); + } + @Test public void testMinShouldMatchThresholdsUpper() throws Exception { Query actual = From eadfc22cbb6a1dd96942ff1877743d5e7c89ce73 Mon Sep 17 00:00:00 2001 From: Renato Haeberli Date: Sat, 30 May 2026 21:30:33 +0200 Subject: [PATCH 2/5] skip empty BooleanQuery with Occur=SHOULD --- solr/core/src/test-files/solr/collection1/conf/schema.xml | 1 + .../test/org/apache/solr/search/TestMmBoolQParserPlugin.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/solr/core/src/test-files/solr/collection1/conf/schema.xml b/solr/core/src/test-files/solr/collection1/conf/schema.xml index e20f31d4bf3b..4cff86be2266 100644 --- a/solr/core/src/test-files/solr/collection1/conf/schema.xml +++ b/solr/core/src/test-files/solr/collection1/conf/schema.xml @@ -156,6 +156,7 @@ + diff --git a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java index 4e81ca279973..a68459c32068 100644 --- a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java +++ b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java @@ -113,7 +113,7 @@ public void testMinShouldMatchThresholdsLower() throws Exception { public void testMinShouldMatchWithEmptyClauseCausedByStopWord() throws Exception { Query actual = - parseQuery(req("q", "{!bool should=name:foo should=name:bar should=name_sw:to mm=-1}")); + parseQuery(req("q", "{!bool should=name:foo should=name:bar should=teststop:to mm=-1}")); BooleanQuery expected = shouldBuilder("foo", "bar").setMinimumNumberShouldMatch(1).build(); From d8e3e8f8eb9223a57f397762ad6c87713ef3e46b Mon Sep 17 00:00:00 2001 From: Renato Haeberli Date: Mon, 1 Jun 2026 09:50:15 +0200 Subject: [PATCH 3/5] override SolrQueryParserBase.createFieldQuery in order to handle null case --- .../solr/parser/SolrQueryParserBase.java | 18 ++++++++++++++++++ .../solr/search/join/FiltersQParser.java | 5 +++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java index 42cedf829799..6ba66c680361 100644 --- a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java +++ b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java @@ -538,6 +538,24 @@ protected Query newFieldQuery( return query; } + /** + * Delegates to {@link QueryBuilder#createFieldQuery(org.apache.lucene.analysis.Analyzer, + * org.apache.lucene.search.BooleanClause.Occur, java.lang.String, java.lang.String, boolean, + * int)} but returns MatchNoDocsQuery rather than null + */ + protected Query createFieldQuery( + Analyzer analyzer, + BooleanClause.Occur operator, + String field, + String queryText, + boolean quoted, + int phraseSlop) { + + Query fieldQuery = + super.createFieldQuery(analyzer, operator, field, queryText, quoted, phraseSlop); + return fieldQuery != null ? fieldQuery : new MatchNoDocsQuery("empty query"); + } + /** * Base implementation delegates to {@link #getFieldQuery(String,String,boolean,boolean)}. This * method may be overridden, for example, to return a SpanNearQuery instead of a PhraseQuery. diff --git a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java index e706308b6649..b66c5fb37e36 100644 --- a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java +++ b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java @@ -26,6 +26,7 @@ import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.StrUtils; @@ -63,8 +64,8 @@ protected BooleanQuery parseImpl() throws SyntaxError { Query query = clause.getKey().getQuery(); Occur occur = clause.getValue(); - if (occur == Occur.SHOULD && query instanceof BooleanQuery boolQ && boolQ.clauses().isEmpty()) { - continue; + if (query instanceof MatchNoDocsQuery) { + continue; } builder.add(unwrapQuery(query, occur), occur); } From 408d3d08ad243019c54f0e6e153e78a18e8040e3 Mon Sep 17 00:00:00 2001 From: Renato Haeberli Date: Mon, 1 Jun 2026 11:56:48 +0200 Subject: [PATCH 4/5] override SolrQueryParserBase.createFieldQuery in order to handle null case --- .../java/org/apache/solr/search/join/FiltersQParser.java | 4 ---- .../src/java/org/apache/solr/util/SolrPluginUtils.java | 3 ++- .../org/apache/solr/search/TestMmBoolQParserPlugin.java | 8 ++++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java index b66c5fb37e36..6e864ba36738 100644 --- a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java +++ b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.StrUtils; @@ -64,9 +63,6 @@ protected BooleanQuery parseImpl() throws SyntaxError { Query query = clause.getKey().getQuery(); Occur occur = clause.getValue(); - if (query instanceof MatchNoDocsQuery) { - continue; - } builder.add(unwrapQuery(query, occur), occur); } // what about empty query? diff --git a/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java b/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java index ed6178be4f9b..3a1a3f0cd3d9 100644 --- a/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java +++ b/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java @@ -46,6 +46,7 @@ import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.solr.common.SolrException; @@ -595,7 +596,7 @@ public static void setMinShouldMatch(BooleanQuery.Builder q, String spec, boolea optionalDismaxClauses++; } } else { - optionalClauses++; + if (!(c.query() instanceof MatchNoDocsQuery)) optionalClauses++; } } } diff --git a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java index a68459c32068..f55df0ced89a 100644 --- a/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java +++ b/solr/core/src/test/org/apache/solr/search/TestMmBoolQParserPlugin.java @@ -20,6 +20,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.NamedMatches; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; @@ -115,8 +116,11 @@ public void testMinShouldMatchWithEmptyClauseCausedByStopWord() throws Exception Query actual = parseQuery(req("q", "{!bool should=name:foo should=name:bar should=teststop:to mm=-1}")); - BooleanQuery expected = shouldBuilder("foo", "bar").setMinimumNumberShouldMatch(1).build(); - + BooleanQuery expected = + shouldBuilder("foo", "bar") + .setMinimumNumberShouldMatch(1) + .add(new MatchNoDocsQuery(""), BooleanClause.Occur.SHOULD) + .build(); assertEquals(expected, actual); } From b536b30eb00a5fd147bcde0694a1f08d0d14b0bc Mon Sep 17 00:00:00 2001 From: Renato Haeberli Date: Mon, 1 Jun 2026 11:58:58 +0200 Subject: [PATCH 5/5] override SolrQueryParserBase.createFieldQuery in order to handle null case --- .../java/org/apache/solr/search/join/FiltersQParser.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java index 6e864ba36738..451de63337a0 100644 --- a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java +++ b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java @@ -59,11 +59,7 @@ protected BooleanQuery parseImpl() throws SyntaxError { BooleanQuery.Builder builder = createBuilder(); for (Map.Entry clause : clauses.entrySet()) { - - Query query = clause.getKey().getQuery(); - Occur occur = clause.getValue(); - - builder.add(unwrapQuery(query, occur), occur); + builder.add(unwrapQuery(clause.getKey().getQuery(), clause.getValue()), clause.getValue()); } // what about empty query? return builder.build();