This is an automated email from the ASF dual-hosted git repository.
sigram pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_10x by this push:
new 4aac35171f5 SOLR-18522: Fix for SortedIntDocSet union affecting facet
in Combined Query (#5060)
4aac35171f5 is described below
commit 4aac35171f53ac51adf5e2c4873ffffd4959d84c
Author: Sonu Sharma <[email protected]>
AuthorDate: Thu Oct 8 21:59:14 2026 +0530
SOLR-18522: Fix for SortedIntDocSet union affecting facet in Combined Query
(#5060)
(cherry picked from commit ff3ab1e726ee0ea895accf1143cfa02ec7a358df)
---
.../SOLR-18522-fix-facet-combined-query.yml | 8 +++++
.../src/java/org/apache/solr/search/BitDocSet.java | 7 +++-
.../component/CombinedQueryComponentTest.java | 38 ++++++++++++++++++++++
.../test/org/apache/solr/search/TestDocSet.java | 30 +++++++++++++++++
4 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/changelog/unreleased/SOLR-18522-fix-facet-combined-query.yml
b/changelog/unreleased/SOLR-18522-fix-facet-combined-query.yml
new file mode 100644
index 00000000000..fb6723634b5
--- /dev/null
+++ b/changelog/unreleased/SOLR-18522-fix-facet-combined-query.yml
@@ -0,0 +1,8 @@
+title: Fix for SortedIntDocSet union affecting facet in Combined Query
+type: fixed
+authors:
+ - name: Sonu Sharma
+ nick: ercsonusharma
+links:
+ - name: SOLR-18522
+ url: https://issues.apache.org/jira/browse/SOLR-18522
diff --git a/solr/core/src/java/org/apache/solr/search/BitDocSet.java
b/solr/core/src/java/org/apache/solr/search/BitDocSet.java
index f42185473fe..37a4f7552ae 100644
--- a/solr/core/src/java/org/apache/solr/search/BitDocSet.java
+++ b/solr/core/src/java/org/apache/solr/search/BitDocSet.java
@@ -271,8 +271,13 @@ public class BitDocSet extends DocSet {
}
final int base = context.docBase;
- final int max = base + maxDoc; // one past the max doc in this segment.
final FixedBitSet bs = bits;
+ // bits may be shorter than the index's maxDoc (e.g. from
SortedIntDocSet.union)
+ if (base >= bs.length()) {
+ return null;
+ }
+ // one past the max doc to visit in this segment
+ final int max = Math.min(base + maxDoc, bs.length());
return new DocIdSetIterator() {
int pos = base - 1;
diff --git
a/solr/core/src/test/org/apache/solr/handler/component/CombinedQueryComponentTest.java
b/solr/core/src/test/org/apache/solr/handler/component/CombinedQueryComponentTest.java
index c9d5e9f8a28..4bf7b18fcf9 100644
---
a/solr/core/src/test/org/apache/solr/handler/component/CombinedQueryComponentTest.java
+++
b/solr/core/src/test/org/apache/solr/handler/component/CombinedQueryComponentTest.java
@@ -203,6 +203,44 @@ public class CombinedQueryComponentTest extends
BaseDistributedSearchTestCase {
.get("combinerDetails"));
}
+ /**
+ * Tests faceting on a string docValues field when every sub-query matches
only a few docs in the
+ * first segment, and a later segment exists beyond them.
+ */
+ @Test
+ public void testFacetWithSmallMatchesAcrossSegments() throws Exception {
+ del("*:*");
+ for (int segment = 0; segment < 2; segment++) {
+ for (int i = 1; i <= 500; i++) {
+ int id = segment * 500 + i;
+ indexDoc(sdoc("id", Integer.toString(id), "color_sdv", id % 2 == 0 ?
"red" : "blue"));
+ }
+ commit();
+ }
+ QueryResponse rsp =
+ query(
+ "/search",
+ params(
+ CommonParams.JSON,
+ """
+ {
+ "queries": {
+ "lexical1": {"lucene": {"query": "id:(1 OR 2)"}},
+ "lexical2": {"lucene": {"query": "id:(3 OR 4)"}}
+ },
+ "limit": 5,
+ "fields": ["id", "score"],
+ "params": {
+ "combiner": true,
+ "facet": true,
+ "facet.field": "color_sdv",
+ "combiner.query": ["lexical1", "lexical2"]
+ }
+ }"""));
+ assertEquals(4, rsp.getResults().size());
+ assertEquals("[blue (2), red (2)]",
rsp.getFacetField("color_sdv").getValues().toString());
+ }
+
/**
* Tests that using unsupported features with Combined Queries throws the
expected exception.
*
diff --git a/solr/core/src/test/org/apache/solr/search/TestDocSet.java
b/solr/core/src/test/org/apache/solr/search/TestDocSet.java
index 4f8e78a6acf..b174ad348cb 100644
--- a/solr/core/src/test/org/apache/solr/search/TestDocSet.java
+++ b/solr/core/src/test/org/apache/solr/search/TestDocSet.java
@@ -613,6 +613,36 @@ public class TestDocSet extends SolrTestCase {
}
}
+ /**
+ * Per-segment iteration of a {@link BitDocSet} whose bitset is shorter than
maxDoc, as produced
+ * by {@link SortedIntDocSet#union}, must not read beyond the bitset.
+ */
+ public void testIteratorWithBitSetShorterThanMaxDoc() throws IOException {
+ IndexReader r = new MultiReader(dummyIndexReader(2000),
dummyIndexReader(5000));
+ List<LeafReaderContext> leaves = r.leaves();
+ // union of SortedIntDocSets yields a BitDocSet sized to the largest doc,
not maxDoc
+ DocSet union =
+ new SortedIntDocSet(new int[] {10, 100}).union(new SortedIntDocSet(new
int[] {100, 300}));
+ assertTrue(union instanceof BitDocSet);
+
+ DocIdSetIterator it = union.iterator(leaves.get(0));
+ assertEquals(10, it.nextDoc());
+ assertEquals(100, it.advance(50));
+ assertEquals(300, it.nextDoc());
+ assertEquals(DocIdSetIterator.NO_MORE_DOCS, it.nextDoc());
+
+ it = union.iterator(leaves.get(1));
+ assertTrue(it == null || it.nextDoc() == DocIdSetIterator.NO_MORE_DOCS);
+
+ // bitset ends mid-segment
+ DocSet midSeg = new SortedIntDocSet(new int[] {5}).union(new
SortedIntDocSet(new int[] {2100}));
+ it = midSeg.iterator(leaves.get(1));
+ assertEquals(100, it.nextDoc());
+ assertEquals(DocIdSetIterator.NO_MORE_DOCS, it.nextDoc());
+ it = midSeg.iterator(leaves.get(1));
+ assertEquals(DocIdSetIterator.NO_MORE_DOCS, it.advance(101));
+ }
+
public void testFilter() throws IOException {
// keeping these numbers smaller help hit more edge cases
int maxSeg = 4;