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;

Reply via email to