This is an automated email from the ASF dual-hosted git repository.

gortiz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 24c520be6e0 Make FixedBitMVForwardIndexReader.getNumValuesMV advance 
the reader context (#19992)
24c520be6e0 is described below

commit 24c520be6e063f0253a97902051b6853b42bf1bb
Author: Gonzalo Ortiz Jaureguizar <[email protected]>
AuthorDate: Thu Oct 22 09:40:24 2026 +0200

    Make FixedBitMVForwardIndexReader.getNumValuesMV advance the reader context 
(#19992)
---
 .../forward/FixedBitMVForwardIndexReader.java      |  5 ++
 .../index/forward/FixedBitMVForwardIndexTest.java  | 69 ++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/forward/FixedBitMVForwardIndexReader.java
 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/forward/FixedBitMVForwardIndexReader.java
index b690fdf0d37..f927e191512 100644
--- 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/forward/FixedBitMVForwardIndexReader.java
+++ 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/forward/FixedBitMVForwardIndexReader.java
@@ -199,6 +199,11 @@ public final class FixedBitMVForwardIndexReader implements 
ForwardIndexReader<Fi
     } else {
       endIndex = _bitmapReader.getNextSetBitOffset(startIndex + 1);
     }
+    // Record the position like getDictIdMV() does. Without it, every call for 
a sequential scan takes the bitmap
+    // scan from the context position (or the chunk start) instead of the O(1) 
next-doc path, which makes counting
+    // the values of a whole segment quadratic in the number of docs per chunk.
+    context._docId = docId;
+    context._endOffset = endIndex;
     return endIndex - startIndex;
   }
 
diff --git 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/forward/FixedBitMVForwardIndexTest.java
 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/forward/FixedBitMVForwardIndexTest.java
index 2b35db720de..f39d73df000 100644
--- 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/forward/FixedBitMVForwardIndexTest.java
+++ 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/forward/FixedBitMVForwardIndexTest.java
@@ -113,6 +113,75 @@ public class FixedBitMVForwardIndexTest implements 
PinotBuffersAfterMethodCheckR
     }
   }
 
+  /// Mixes getNumValuesMV() and getDictIdMV() calls on one context, in 
sequential, skipping and random doc order.
+  /// Few values per doc give about a thousand docs per chunk, so the calls 
cross chunks and also move inside one.
+  @Test
+  public void testNumValuesMVSharesContextWithDictIdMV()
+      throws Exception {
+    int numDocs = 5000;
+    int numBitsPerValue = 8;
+    int[][] valuesArray = new int[numDocs][];
+    int totalNumValues = 0;
+    for (int i = 0; i < numDocs; i++) {
+      int[] values = new int[RANDOM.nextInt(3) + 1];
+      for (int j = 0; j < values.length; j++) {
+        values[j] = RANDOM.nextInt(1 << numBitsPerValue);
+      }
+      valuesArray[i] = values;
+      totalNumValues += values.length;
+    }
+    try (FixedBitMVForwardIndexWriter writer = new 
FixedBitMVForwardIndexWriter(INDEX_FILE, numDocs, totalNumValues,
+        numBitsPerValue)) {
+      for (int[] values : valuesArray) {
+        writer.putDictIds(values);
+      }
+    }
+
+    int[] randomOrder = new int[numDocs];
+    for (int i = 0; i < numDocs; i++) {
+      randomOrder[i] = RANDOM.nextInt(numDocs);
+    }
+    try (PinotDataBuffer dataBuffer = 
PinotDataBuffer.mapReadOnlyBigEndianFile(INDEX_FILE);
+        FixedBitMVForwardIndexReader reader = new 
FixedBitMVForwardIndexReader(dataBuffer, numDocs, totalNumValues,
+            numBitsPerValue)) {
+      int[] valueBuffer = new int[3];
+      // Sequential, counting only
+      try (FixedBitMVForwardIndexReader.Context context = 
reader.createContext()) {
+        for (int i = 0; i < numDocs; i++) {
+          assertEquals(reader.getNumValuesMV(i, context), 
valuesArray[i].length, "docId: " + i);
+        }
+      }
+      // Sequential, counting and then reading the same doc
+      try (FixedBitMVForwardIndexReader.Context context = 
reader.createContext()) {
+        for (int i = 0; i < numDocs; i++) {
+          assertEquals(reader.getNumValuesMV(i, context), 
valuesArray[i].length, "docId: " + i);
+          assertEquals(reader.getDictIdMV(i, context), valuesArray[i], "docId: 
" + i);
+        }
+      }
+      // Skipping docs, alternating the two calls
+      try (FixedBitMVForwardIndexReader.Context context = 
reader.createContext()) {
+        for (int i = 0; i < numDocs; i += 3) {
+          if (i % 2 == 0) {
+            assertEquals(reader.getNumValuesMV(i, context), 
valuesArray[i].length, "docId: " + i);
+          } else {
+            int numValues = reader.getDictIdMV(i, valueBuffer, context);
+            assertEquals(numValues, valuesArray[i].length, "docId: " + i);
+          }
+        }
+      }
+      // Random order, including going backwards
+      try (FixedBitMVForwardIndexReader.Context context = 
reader.createContext()) {
+        for (int docId : randomOrder) {
+          assertEquals(reader.getNumValuesMV(docId, context), 
valuesArray[docId].length, "docId: " + docId);
+          if (docId % 2 == 0) {
+            assertEquals(reader.getDictIdMV(docId, context), 
valuesArray[docId], "docId: " + docId);
+          }
+        }
+      }
+    }
+    FileUtils.forceDelete(INDEX_FILE);
+  }
+
   @AfterClass
   public void tearDown()
       throws IOException {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to