This is an automated email from the ASF dual-hosted git repository.
Jackie-Jiang 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 d34dbf6e296 Fix set-mode FUNNELCOUNT merge with an empty segment's
step sets (#20201)
d34dbf6e296 is described below
commit d34dbf6e29654ae4a2d9ad975540fe74cb0ac499
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Tue Nov 10 20:42:00 2026 -0800
Fix set-mode FUNNELCOUNT merge with an empty segment's step sets (#20201)
---
.../function/funnel/SetMergeStrategy.java | 21 ++++++++++++++++++---
.../function/funnel/FunnelCountSerDeTest.java | 17 +++++++++++++++++
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/SetMergeStrategy.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/SetMergeStrategy.java
index de8809a053d..e47f6b632bd 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/SetMergeStrategy.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/SetMergeStrategy.java
@@ -56,15 +56,30 @@ class SetMergeStrategy implements MergeStrategy<List<Set>> {
@Override
public List<Set> merge(List<Set> intermediateResult1, List<Set>
intermediateResult2) {
- // TODO: Fix merging typed step sets into empty ones. A segment without
rows contributes empty `ObjectOpenHashSet`s.
- // When those are the first operand, the int, long, float or double
values of the other side are boxed into
- // them, `setType` maps the result to the string set type, and
serialization throws `ClassCastException`.
+ // A segment without rows contributes empty string sets whatever the
stored type of the correlation column. Take the
+ // other side as a whole when one side has no values, so that the step
sets of one list keep a single set type.
+ if (hasNoValues(intermediateResult2)) {
+ return intermediateResult1;
+ }
+ if (hasNoValues(intermediateResult1)) {
+ return intermediateResult2;
+ }
for (int i = 0; i < _numSteps; i++) {
intermediateResult1.get(i).addAll(intermediateResult2.get(i));
}
return intermediateResult1;
}
+ /// Returns whether all step sets are empty, as they are for a segment
without rows.
+ private static boolean hasNoValues(List<Set> stepsSets) {
+ for (Set stepSet : stepsSets) {
+ if (!stepSet.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public long[] extractFinalResult(List<Set> stepsSets) {
long[] result = new long[_numSteps];
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountSerDeTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountSerDeTest.java
index fb41ca92b09..dc7d6bc5e86 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountSerDeTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountSerDeTest.java
@@ -30,6 +30,7 @@ import org.apache.pinot.common.CustomObject;
import org.apache.pinot.common.request.Literal;
import org.apache.pinot.common.request.context.ExpressionContext;
import org.apache.pinot.common.request.context.FunctionContext;
+import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
import org.apache.pinot.core.query.aggregation.function.AggregationFunction;
import
org.apache.pinot.core.query.aggregation.function.AggregationFunction.SerializedResult;
import org.roaringbitmap.RoaringBitmap;
@@ -83,6 +84,22 @@ public class FunnelCountSerDeTest {
new long[]{1, 0});
}
+ /// A segment without rows contributes empty step sets whatever the stored
type of the correlation column. Merged on
+ /// either side of typed step sets, they leave the typed sets, which go out
with the set type of that stored type.
+ @Test
+ public void testMergeSetStepsWithEmptySegment() {
+ AggregationFunction function = funnelCount("set");
+ AggregationResultHolder emptySegment =
function.createAggregationResultHolder();
+ List intSteps = List.of(new IntOpenHashSet(new int[]{7}), new
IntOpenHashSet());
+ byte[] intSets = HexFormat.of().parseHex("00000002" + "00000009"
+ + "00000008" + "0000000100000007"
+ + "00000004" + "00000000");
+ assertListSteps(function, (List)
function.merge(function.extractAggregationResult(emptySegment), intSteps),
+ intSets, new long[]{1, 0});
+ assertListSteps(function, (List) function.merge(intSteps,
function.extractAggregationResult(emptySegment)),
+ intSets, new long[]{1, 0});
+ }
+
/// Step sketches go out as `[int size][int DATA_SKETCH_TYPE]`, then `[int
length]` and the compact sketch bytes per
/// step.
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]