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

kfaraz pushed a commit to branch 25.0.0
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/25.0.0 by this push:
     new 1b009edcf8 Fix cool nested column bug caused by not properly 
validating that global id is present in global dictionary before lookup up 
local id (#13561) (#13572)
1b009edcf8 is described below

commit 1b009edcf8ace3365cfff6de5caebb0e3bc863a4
Author: Kashif Faraz <[email protected]>
AuthorDate: Fri Dec 16 08:26:07 2022 +0530

    Fix cool nested column bug caused by not properly validating that global id 
is present in global dictionary before lookup up local id (#13561) (#13572)
    
    This commit fixes a bug with nested column "value set" indexes caused by 
not properly
    validating that the globalId looked up for value is present in the global 
dictionary prior to
    looking it up in the local dictionary, which when "adjusting" the global 
ids for value type
    can cause incorrect selection of value indexes.
    
    To use an example of a variant typed nested column with 3 values `["1", 
null, -2]`.
    The string dictionary is `[null, "1"]`, the long dictionary is `[-2]` and 
our local dictionary is `[0, 1, 2]`.
    
    The code for variant typed indexes checks if the value is present in all 
global dictionaries
    and returns indexes for all matches. So in this case, we first lookup "1" 
in the string dictionary,
    find it at global id 1, all is good. Now, we check the long dictionary for 
`1`, which due to
    `-(insertionpoint + 1)` gives us `-(1 + 2) = -2`. Since the global id space 
is actually stacked
    dictionaries, global ids for long and double values must be "adjusted" by 
the size of string
    dictionary, and size of string + size of long for doubles.
    
    Prior to this patch we were not checking that the globalId is 0 or larger, 
we then immediately
    looked up the `localDictionary.indexOf(-2 + adjustLong) = 
localDictionary.indexOf(-2 + 2) = localDictionary.indexOf(0)` ... which is an 
actual value contained in the dictionary! The fix is
    to skip the longs completely since there were no global matches.
    
    On to doubles, `-(insertionPoint + 1)` gives us `-(0 + 1) = -1`. The double 
adjust value is '3'
    since 2 strings and 1 long, so `localDictionary.indexOf(-1 + 3)` = 
`localDictionary.indexOf(2)`
    which is also a real value in our local dictionary that is definitely not 
'1'.
    
    So in this one case, looking for '1' incorrectly ended up matching every 
row.
    
    Co-authored-by: Clint Wylie <[email protected]>
---
 .../NestedFieldLiteralColumnIndexSupplier.java     |  98 +++++---
 .../NestedFieldLiteralDictionaryEncodedColumn.java | 145 ++++++++++-
 .../druid/query/scan/NestedDataScanQueryTest.java  |   9 +-
 .../nested/NestedDataColumnSupplierTest.java       | 134 +++++++---
 .../NestedFieldLiteralColumnIndexSupplierTest.java | 274 +++++++++++++++++++++
 5 files changed, 579 insertions(+), 81 deletions(-)

diff --git 
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplier.java
 
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplier.java
index 36c39f251b..22ba59835e 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplier.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplier.java
@@ -336,17 +336,21 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
         @Override
         public double estimateSelectivity(int totalRows)
         {
-          return (double) getBitmap(
-              
localDictionary.indexOf(stringDictionary.indexOf(StringUtils.toUtf8ByteBuffer(value)))
-          ).size() / totalRows;
+          final int globalId = 
stringDictionary.indexOf(StringUtils.toUtf8ByteBuffer(value));
+          if (globalId < 0) {
+            return 0.0;
+          }
+          return (double) getBitmap(localDictionary.indexOf(globalId)).size() 
/ totalRows;
         }
 
         @Override
         public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory)
         {
-          return bitmapResultFactory.wrapDimensionValue(
-              
getBitmap(localDictionary.indexOf(stringDictionary.indexOf(StringUtils.toUtf8ByteBuffer(value))))
-          );
+          final int globalId = 
stringDictionary.indexOf(StringUtils.toUtf8ByteBuffer(value));
+          if (globalId < 0) {
+            return 
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
+          }
+          return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(globalId)));
         }
       };
     }
@@ -564,6 +568,7 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
     @Override
     public BitmapColumnIndex forValue(@Nullable String value)
     {
+      final boolean inputNull = value == null;
       final Long longValue = GuavaUtils.tryParseLong(value);
       return new SimpleBitmapColumnIndex()
       {
@@ -574,22 +579,34 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
         public double estimateSelectivity(int totalRows)
         {
           if (longValue == null) {
-            return (double) getBitmap(localDictionary.indexOf(0)).size() / 
totalRows;
+            if (inputNull) {
+              return (double) getBitmap(localDictionary.indexOf(0)).size() / 
totalRows;
+            } else {
+              return 0.0;
+            }
+          }
+          final int globalId = longDictionary.indexOf(longValue);
+          if (globalId < 0) {
+            return 0.0;
           }
-          return (double) getBitmap(
-              localDictionary.indexOf(longDictionary.indexOf(longValue) + 
adjustLongId)
-          ).size() / totalRows;
+          return (double) getBitmap(localDictionary.indexOf(globalId + 
adjustLongId)).size() / totalRows;
         }
 
         @Override
         public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory)
         {
           if (longValue == null) {
-            return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(0)));
+            if (inputNull) {
+              return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(0)));
+            } else {
+              return 
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
+            }
           }
-          return bitmapResultFactory.wrapDimensionValue(
-              
getBitmap(localDictionary.indexOf(longDictionary.indexOf(longValue) + 
adjustLongId))
-          );
+          final int globalId = longDictionary.indexOf(longValue);
+          if (globalId < 0) {
+            return 
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
+          }
+          return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(globalId
 + adjustLongId)));
         }
       };
     }
@@ -759,6 +776,7 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
     @Override
     public BitmapColumnIndex forValue(@Nullable String value)
     {
+      final boolean inputNull = value == null;
       final Double doubleValue = Strings.isNullOrEmpty(value) ? null : 
Doubles.tryParse(value);
       return new SimpleBitmapColumnIndex()
       {
@@ -768,22 +786,34 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
         public double estimateSelectivity(int totalRows)
         {
           if (doubleValue == null) {
-            return (double) getBitmap(localDictionary.indexOf(0)).size() / 
totalRows;
+            if (inputNull) {
+              return (double) getBitmap(localDictionary.indexOf(0)).size() / 
totalRows;
+            } else {
+              return 0.0;
+            }
+          }
+          final int globalId = doubleDictionary.indexOf(doubleValue);
+          if (globalId < 0) {
+            return 0.0;
           }
-          return (double) getBitmap(
-              localDictionary.indexOf(doubleDictionary.indexOf(doubleValue) + 
adjustDoubleId)
-          ).size() / totalRows;
+          return (double) getBitmap(localDictionary.indexOf(globalId + 
adjustDoubleId)).size() / totalRows;
         }
 
         @Override
         public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory)
         {
           if (doubleValue == null) {
-            return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(0)));
+            if (inputNull) {
+              return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(0)));
+            } else {
+              return 
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
+            }
           }
-          return bitmapResultFactory.wrapDimensionValue(
-              
getBitmap(localDictionary.indexOf(doubleDictionary.indexOf(doubleValue) + 
adjustDoubleId))
-          );
+          final int globalId = doubleDictionary.indexOf(doubleValue);
+          if (globalId < 0) {
+            return 
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
+          }
+          return 
bitmapResultFactory.wrapDimensionValue(getBitmap(localDictionary.indexOf(globalId
 + adjustDoubleId)));
         }
       };
     }
@@ -964,25 +994,31 @@ public class 
NestedFieldLiteralColumnIndexSupplier<TStringDictionary extends Ind
 
       // multi-type, return all that match
       int globalId = 
stringDictionary.indexOf(StringUtils.toUtf8ByteBuffer(value));
-      int localId = localDictionary.indexOf(globalId);
-      if (localId >= 0) {
-        intList.add(localId);
+      if (globalId >= 0) {
+        int localId = localDictionary.indexOf(globalId);
+        if (localId >= 0) {
+          intList.add(localId);
+        }
       }
       Long someLong = GuavaUtils.tryParseLong(value);
       if (someLong != null) {
         globalId = longDictionary.indexOf(someLong);
-        localId = localDictionary.indexOf(globalId + adjustLongId);
-        if (localId >= 0) {
-          intList.add(localId);
+        if (globalId >= 0) {
+          int localId = localDictionary.indexOf(globalId + adjustLongId);
+          if (localId >= 0) {
+            intList.add(localId);
+          }
         }
       }
 
       Double someDouble = Doubles.tryParse(value);
       if (someDouble != null) {
         globalId = doubleDictionary.indexOf(someDouble);
-        localId = localDictionary.indexOf(globalId + adjustDoubleId);
-        if (localId >= 0) {
-          intList.add(localId);
+        if (globalId >= 0) {
+          int localId = localDictionary.indexOf(globalId + adjustDoubleId);
+          if (localId >= 0) {
+            intList.add(localId);
+          }
         }
       }
       return intList;
diff --git 
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralDictionaryEncodedColumn.java
 
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralDictionaryEncodedColumn.java
index 855112e6b4..6f794b61db 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralDictionaryEncodedColumn.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldLiteralDictionaryEncodedColumn.java
@@ -25,7 +25,6 @@ import com.google.common.base.Predicates;
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Floats;
 import org.apache.druid.collections.bitmap.ImmutableBitmap;
-import org.apache.druid.common.config.NullHandling;
 import org.apache.druid.common.guava.GuavaUtils;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.UOE;
@@ -146,7 +145,7 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
     final int globalId = dictionary.get(id);
     if (globalId < globalDictionary.size()) {
       return StringUtils.fromUtf8Nullable(globalDictionary.get(globalId));
-    } else if (globalId < adjustLongId + globalLongDictionary.size()) {
+    } else if (globalId < globalDictionary.size() + 
globalLongDictionary.size()) {
       return String.valueOf(globalLongDictionary.get(globalId - adjustLongId));
     } else {
       return String.valueOf(globalDoubleDictionary.get(globalId - 
adjustDoubleId));
@@ -156,7 +155,11 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
   @Override
   public int lookupId(String name)
   {
-    return dictionary.indexOf(getIdFromGlobalDictionary(name));
+    final int globalId = getIdFromGlobalDictionary(name);
+    if (globalId < 0) {
+      return -1;
+    }
+    return dictionary.indexOf(globalId);
   }
 
   @Override
@@ -179,9 +182,17 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
     if (singleType != null) {
       switch (singleType.getType()) {
         case LONG:
-          return globalLongDictionary.indexOf(GuavaUtils.tryParseLong(val));
+          final int globalLong = 
globalLongDictionary.indexOf(GuavaUtils.tryParseLong(val));
+          if (globalLong < 0) {
+            return -1;
+          }
+          return globalLong + adjustLongId;
         case DOUBLE:
-          return globalDoubleDictionary.indexOf(Doubles.tryParse(val));
+          final int globalDouble = 
globalDoubleDictionary.indexOf(Doubles.tryParse(val));
+          if (globalDouble < 0) {
+            return -1;
+          }
+          return globalDouble + adjustDoubleId;
         default:
           return globalDictionary.indexOf(StringUtils.toUtf8ByteBuffer(val));
       }
@@ -189,9 +200,15 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
       int candidate = 
globalDictionary.indexOf(StringUtils.toUtf8ByteBuffer(val));
       if (candidate < 0) {
         candidate = globalLongDictionary.indexOf(GuavaUtils.tryParseLong(val));
+        if (candidate >= 0) {
+          candidate += adjustLongId;
+        }
       }
       if (candidate < 0) {
         candidate = globalDoubleDictionary.indexOf(Doubles.tryParse(val));
+        if (candidate >= 0) {
+          candidate += adjustDoubleId;
+        }
       }
       return candidate;
     }
@@ -228,7 +245,6 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
         final int globalId = dictionary.get(localId);
         if (globalId == 0) {
           // zero
-          assert NullHandling.replaceWithDefault();
           return 0f;
         } else if (globalId < adjustLongId) {
           // try to convert string to float
@@ -248,7 +264,6 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
         final int globalId = dictionary.get(localId);
         if (globalId == 0) {
           // zero
-          assert NullHandling.replaceWithDefault();
           return 0.0;
         } else if (globalId < adjustLongId) {
           // try to convert string to double
@@ -268,7 +283,6 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
         final int globalId = dictionary.get(localId);
         if (globalId == 0) {
           // zero
-          assert NullHandling.replaceWithDefault();
           return 0L;
         } else if (globalId < adjustLongId) {
           // try to convert string to long
@@ -509,6 +523,121 @@ public class 
NestedFieldLiteralDictionaryEncodedColumn<TStringDictionary extends
         };
       }
     }
+    if (singleType == null) {
+      return new ColumnValueSelector<Object>()
+      {
+
+        private PeekableIntIterator nullIterator = 
nullBitmap.peekableIterator();
+        private int nullMark = -1;
+        private int offsetMark = -1;
+
+        @Nullable
+        @Override
+        public Object getObject()
+        {
+          final int localId = column.get(offset.getOffset());
+          final int globalId = dictionary.get(localId);
+          if (globalId < adjustLongId) {
+            return 
StringUtils.fromUtf8Nullable(globalDictionary.get(globalId));
+          } else if (globalId < adjustDoubleId) {
+            return globalLongDictionary.get(globalId - adjustLongId);
+          } else {
+            return globalDoubleDictionary.get(globalId - adjustDoubleId);
+          }
+        }
+
+        @Override
+        public float getFloat()
+        {
+          final int localId = column.get(offset.getOffset());
+          final int globalId = dictionary.get(localId);
+          if (globalId == 0) {
+            // zero
+            return 0f;
+          } else if (globalId < adjustLongId) {
+            // try to convert string to float
+            Float f = 
Floats.tryParse(StringUtils.fromUtf8(globalDictionary.get(globalId)));
+            return f == null ? 0f : f;
+          } else if (globalId < adjustDoubleId) {
+            return globalLongDictionary.get(globalId - 
adjustLongId).floatValue();
+          } else {
+            return globalDoubleDictionary.get(globalId - 
adjustDoubleId).floatValue();
+          }
+        }
+
+        @Override
+        public double getDouble()
+        {
+          final int localId = column.get(offset.getOffset());
+          final int globalId = dictionary.get(localId);
+          if (globalId == 0) {
+            // zero
+            return 0.0;
+          } else if (globalId < adjustLongId) {
+            // try to convert string to double
+            Double d = 
Doubles.tryParse(StringUtils.fromUtf8(globalDictionary.get(globalId)));
+            return d == null ? 0.0 : d;
+          } else if (globalId < adjustDoubleId) {
+            return globalLongDictionary.get(globalId - 
adjustLongId).doubleValue();
+          } else {
+            return globalDoubleDictionary.get(globalId - adjustDoubleId);
+          }
+        }
+
+        @Override
+        public long getLong()
+        {
+          final int localId = column.get(offset.getOffset());
+          final int globalId = dictionary.get(localId);
+          if (globalId == 0) {
+            // zero
+            return 0L;
+          } else if (globalId < adjustLongId) {
+            // try to convert string to long
+            Long l = 
GuavaUtils.tryParseLong(StringUtils.fromUtf8(globalDictionary.get(globalId)));
+            return l == null ? 0L : l;
+          } else if (globalId < adjustDoubleId) {
+            return globalLongDictionary.get(globalId - adjustLongId);
+          } else {
+            return globalDoubleDictionary.get(globalId - 
adjustDoubleId).longValue();
+          }
+        }
+
+        @Override
+        public boolean isNull()
+        {
+          final int i = offset.getOffset();
+          if (i < offsetMark) {
+            // offset was reset, reset iterator state
+            nullMark = -1;
+            nullIterator = nullBitmap.peekableIterator();
+          }
+          offsetMark = i;
+          if (nullMark < i) {
+            nullIterator.advanceIfNeeded(offsetMark);
+            if (nullIterator.hasNext()) {
+              nullMark = nullIterator.next();
+            }
+          }
+          return nullMark == offsetMark;
+        }
+
+        @Override
+        public Class<?> classOfObject()
+        {
+          return Object.class;
+        }
+
+        @Override
+        public void inspectRuntimeShape(RuntimeShapeInspector inspector)
+        {
+          inspector.visit("longColumn", longsColumn);
+          inspector.visit("nullBitmap", nullBitmap);
+        }
+      };
+    }
+
+    // single type strings, use a dimension selector
     return makeDimensionSelector(offset, null);
   }
 
diff --git 
a/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
 
b/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
index 4436d9c95c..defca9cb17 100644
--- 
a/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
+++ 
b/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
@@ -43,7 +43,6 @@ import 
org.apache.druid.query.spec.MultipleIntervalSegmentSpec;
 import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.ColumnValueSelector;
 import org.apache.druid.segment.Cursor;
-import org.apache.druid.segment.DimensionDictionarySelector;
 import org.apache.druid.segment.DoubleColumnSelector;
 import org.apache.druid.segment.LongColumnSelector;
 import org.apache.druid.segment.Segment;
@@ -438,13 +437,13 @@ public class NestedDataScanQueryTest extends 
InitializedNullHandlingTest
         NESTED_MIXED_NUMERIC_FIELD
     );
     Assert.assertNotNull(mixedNumericValueSelector);
-    Assert.assertTrue(mixedNumericValueSelector instanceof 
DimensionDictionarySelector);
+    Assert.assertTrue(mixedNumericValueSelector instanceof 
ColumnValueSelector);
 
     ColumnValueSelector mixedValueSelector = 
columnSelectorFactory.makeColumnValueSelector(
         NESTED_MIXED_FIELD
     );
     Assert.assertNotNull(mixedValueSelector);
-    Assert.assertTrue(mixedValueSelector instanceof 
DimensionDictionarySelector);
+    Assert.assertTrue(mixedValueSelector instanceof ColumnValueSelector);
 
 
     ColumnValueSelector sparseLongValueSelector = 
columnSelectorFactory.makeColumnValueSelector(
@@ -463,13 +462,13 @@ public class NestedDataScanQueryTest extends 
InitializedNullHandlingTest
         NESTED_SPARSE_MIXED_NUMERIC_FIELD
     );
     Assert.assertNotNull(sparseMixedNumericValueSelector);
-    Assert.assertTrue(sparseMixedNumericValueSelector instanceof 
DimensionDictionarySelector);
+    Assert.assertTrue(sparseMixedNumericValueSelector instanceof 
ColumnValueSelector);
 
     ColumnValueSelector sparseMixedValueSelector = 
columnSelectorFactory.makeColumnValueSelector(
         NESTED_SPARSE_MIXED_FIELD
     );
     Assert.assertNotNull(sparseMixedValueSelector);
-    Assert.assertTrue(sparseMixedValueSelector instanceof 
DimensionDictionarySelector);
+    Assert.assertTrue(sparseMixedValueSelector instanceof ColumnValueSelector);
     //CHECKSTYLE.ON: Regexp
   }
 
diff --git 
a/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
 
b/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
index 98dc97731f..774ac51b58 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
@@ -33,9 +33,11 @@ import 
org.apache.druid.java.util.common.io.smoosh.FileSmoosher;
 import org.apache.druid.java.util.common.io.smoosh.SmooshedFileMapper;
 import org.apache.druid.java.util.common.io.smoosh.SmooshedWriter;
 import org.apache.druid.query.DefaultBitmapResultFactory;
+import org.apache.druid.query.filter.SelectorPredicateFactory;
 import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
 import org.apache.druid.segment.BaseProgressIndicator;
 import org.apache.druid.segment.ColumnValueSelector;
+import org.apache.druid.segment.DimensionSelector;
 import org.apache.druid.segment.IndexSpec;
 import org.apache.druid.segment.NestedDataColumnIndexer;
 import org.apache.druid.segment.ObjectColumnSelector;
@@ -44,6 +46,7 @@ import org.apache.druid.segment.TestHelper;
 import org.apache.druid.segment.column.ColumnBuilder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.column.DruidPredicateIndex;
 import org.apache.druid.segment.column.NullValueIndex;
 import org.apache.druid.segment.column.StringValueSetIndex;
 import org.apache.druid.segment.column.TypeStrategy;
@@ -64,8 +67,10 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.SortedMap;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
@@ -75,18 +80,20 @@ public class NestedDataColumnSupplierTest extends 
InitializedNullHandlingTest
 {
   private static final ObjectMapper JSON_MAPPER = TestHelper.makeJsonMapper();
 
+  private static final String NO_MATCH = "no";
+
   @Rule
   public final TemporaryFolder tempFolder = new TemporaryFolder();
 
   DefaultBitmapResultFactory resultFactory = new 
DefaultBitmapResultFactory(new RoaringBitmapFactory());
 
   List<Map<String, Object>> data = ImmutableList.of(
-      ImmutableMap.of("x", 1L, "y", 1.0, "z", "a"),
-      ImmutableMap.of("y", 3.0, "z", "d"),
+      ImmutableMap.of("x", 1L, "y", 1.0, "z", "a", "v", "100"),
+      ImmutableMap.of("y", 3.0, "z", "d", "v", 1000L),
       ImmutableMap.of("x", 5L, "y", 5.0, "z", "b"),
-      ImmutableMap.of("x", 3L, "y", 4.0, "z", "c"),
-      ImmutableMap.of("x", 2L),
-      ImmutableMap.of("x", 4L, "y", 2.0, "z", "e")
+      ImmutableMap.of("x", 3L, "y", 4.0, "z", "c", "v", 3000.333),
+      ImmutableMap.of("x", 2L, "v", "40000"),
+      ImmutableMap.of("x", 4L, "y", 2.0, "z", "e", "v", 11111L)
   );
 
   Closer closer = Closer.create();
@@ -216,30 +223,47 @@ public class NestedDataColumnSupplierTest extends 
InitializedNullHandlingTest
     Assert.assertEquals(ImmutableSet.of(ColumnType.LONG), 
column.getColumnTypes(xPath));
     Assert.assertEquals(ColumnType.LONG, 
column.getColumnHolder(xPath).getCapabilities().toColumnType());
     ColumnValueSelector<?> xSelector = column.makeColumnValueSelector(xPath, 
offset);
+    DimensionSelector xDimSelector = column.makeDimensionSelector(xPath, 
offset, null);
     ColumnIndexSupplier xIndexSupplier = column.getColumnIndexSupplier(xPath);
     Assert.assertNotNull(xIndexSupplier);
     StringValueSetIndex xValueIndex = 
xIndexSupplier.as(StringValueSetIndex.class);
+    DruidPredicateIndex xPredicateIndex = 
xIndexSupplier.as(DruidPredicateIndex.class);
     NullValueIndex xNulls = xIndexSupplier.as(NullValueIndex.class);
 
     final List<NestedPathPart> yPath = NestedPathFinder.parseJsonPath("$.y");
     Assert.assertEquals(ImmutableSet.of(ColumnType.DOUBLE), 
column.getColumnTypes(yPath));
     Assert.assertEquals(ColumnType.DOUBLE, 
column.getColumnHolder(yPath).getCapabilities().toColumnType());
     ColumnValueSelector<?> ySelector = column.makeColumnValueSelector(yPath, 
offset);
+    DimensionSelector yDimSelector = column.makeDimensionSelector(yPath, 
offset, null);
     ColumnIndexSupplier yIndexSupplier = column.getColumnIndexSupplier(yPath);
     Assert.assertNotNull(yIndexSupplier);
     StringValueSetIndex yValueIndex = 
yIndexSupplier.as(StringValueSetIndex.class);
+    DruidPredicateIndex yPredicateIndex = 
yIndexSupplier.as(DruidPredicateIndex.class);
     NullValueIndex yNulls = yIndexSupplier.as(NullValueIndex.class);
 
     final List<NestedPathPart> zPath = NestedPathFinder.parseJsonPath("$.z");
     Assert.assertEquals(ImmutableSet.of(ColumnType.STRING), 
column.getColumnTypes(zPath));
     Assert.assertEquals(ColumnType.STRING, 
column.getColumnHolder(zPath).getCapabilities().toColumnType());
     ColumnValueSelector<?> zSelector = column.makeColumnValueSelector(zPath, 
offset);
+    DimensionSelector zDimSelector = column.makeDimensionSelector(zPath, 
offset, null);
     ColumnIndexSupplier zIndexSupplier = column.getColumnIndexSupplier(zPath);
     Assert.assertNotNull(zIndexSupplier);
     StringValueSetIndex zValueIndex = 
zIndexSupplier.as(StringValueSetIndex.class);
+    DruidPredicateIndex zPredicateIndex = 
zIndexSupplier.as(DruidPredicateIndex.class);
     NullValueIndex zNulls = zIndexSupplier.as(NullValueIndex.class);
 
-    Assert.assertEquals(ImmutableList.of(xPath, yPath, zPath), 
column.getNestedFields());
+    final List<NestedPathPart> vPath = NestedPathFinder.parseJsonPath("$.v");
+    Assert.assertEquals(ImmutableSet.of(ColumnType.STRING, ColumnType.LONG, 
ColumnType.DOUBLE), column.getColumnTypes(vPath));
+    Assert.assertEquals(ColumnType.STRING, 
column.getColumnHolder(vPath).getCapabilities().toColumnType());
+    ColumnValueSelector<?> vSelector = column.makeColumnValueSelector(vPath, 
offset);
+    DimensionSelector vDimSelector = column.makeDimensionSelector(vPath, 
offset, null);
+    ColumnIndexSupplier vIndexSupplier = column.getColumnIndexSupplier(vPath);
+    Assert.assertNotNull(vIndexSupplier);
+    StringValueSetIndex vValueIndex = 
vIndexSupplier.as(StringValueSetIndex.class);
+    DruidPredicateIndex vPredicateIndex = 
vIndexSupplier.as(DruidPredicateIndex.class);
+    NullValueIndex vNulls = vIndexSupplier.as(NullValueIndex.class);
+
+    Assert.assertEquals(ImmutableList.of(vPath, xPath, yPath, zPath), 
column.getNestedFields());
 
     for (int i = 0; i < data.size(); i++) {
       Map row = data.get(i);
@@ -247,41 +271,77 @@ public class NestedDataColumnSupplierTest extends 
InitializedNullHandlingTest
           JSON_MAPPER.writeValueAsString(row),
           
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(rawSelector.getObject()))
       );
-      if (row.containsKey("x")) {
-        Assert.assertEquals(row.get("x"), xSelector.getObject());
-        Assert.assertEquals(row.get("x"), xSelector.getLong());
-        
Assert.assertTrue(xValueIndex.forValue(String.valueOf(row.get("x"))).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertFalse(xNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      } else {
-        Assert.assertNull(xSelector.getObject());
-        Assert.assertTrue(xSelector.isNull());
-        
Assert.assertTrue(xValueIndex.forValue(null).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertTrue(xNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      }
-      if (row.containsKey("y")) {
-        Assert.assertEquals(row.get("y"), ySelector.getObject());
-        Assert.assertEquals(row.get("y"), ySelector.getDouble());
-        
Assert.assertTrue(yValueIndex.forValue(String.valueOf(row.get("y"))).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertFalse(yNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      } else {
-        Assert.assertNull(ySelector.getObject());
-        Assert.assertTrue(ySelector.isNull());
-        
Assert.assertTrue(yValueIndex.forValue(null).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertTrue(yNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      }
-      if (row.containsKey("z")) {
-        Assert.assertEquals(row.get("z"), zSelector.getObject());
-        Assert.assertTrue(zValueIndex.forValue((String) 
row.get("z")).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertFalse(zNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      } else {
-        Assert.assertNull(zSelector.getObject());
-        
Assert.assertTrue(zValueIndex.forValue(null).computeBitmapResult(resultFactory).get(i));
-        
Assert.assertTrue(zNulls.forNull().computeBitmapResult(resultFactory).get(i));
-      }
+
+      testPath(row, i, "v", vSelector, vDimSelector, vValueIndex, 
vPredicateIndex, vNulls, null);
+      testPath(row, i, "x", xSelector, xDimSelector, xValueIndex, 
xPredicateIndex, xNulls, ColumnType.LONG);
+      testPath(row, i, "y", ySelector, yDimSelector, yValueIndex, 
yPredicateIndex, yNulls, ColumnType.DOUBLE);
+      testPath(row, i, "z", zSelector, zDimSelector, zValueIndex, 
zPredicateIndex, zNulls, ColumnType.STRING);
+
       offset.increment();
     }
   }
 
+  private void testPath(
+      Map row,
+      int rowNumber,
+      String path,
+      ColumnValueSelector<?> valueSelector,
+      DimensionSelector dimSelector,
+      StringValueSetIndex valueSetIndex,
+      DruidPredicateIndex predicateIndex,
+      NullValueIndex nullValueIndex,
+      @Nullable ColumnType singleType
+  )
+  {
+    if (row.containsKey(path) && row.get(path) != null) {
+      Assert.assertEquals(row.get(path), valueSelector.getObject());
+      if (ColumnType.LONG.equals(singleType)) {
+        Assert.assertEquals(row.get(path), valueSelector.getLong());
+      } else if (ColumnType.DOUBLE.equals(singleType)) {
+        Assert.assertEquals((double) row.get(path), valueSelector.getDouble(), 
0.0);
+      }
+      Assert.assertFalse(valueSelector.isNull());
+
+      final String theString = String.valueOf(row.get(path));
+      Assert.assertEquals(theString, dimSelector.getObject());
+      String dimSelectorLookupVal = 
dimSelector.lookupName(dimSelector.getRow().get(0));
+      Assert.assertEquals(theString, dimSelectorLookupVal);
+      
Assert.assertEquals(dimSelector.idLookup().lookupId(dimSelectorLookupVal), 
dimSelector.getRow().get(0));
+
+      
Assert.assertTrue(valueSetIndex.forValue(theString).computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertTrue(valueSetIndex.forSortedValues(new 
TreeSet<>(ImmutableSet.of(theString))).computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertTrue(predicateIndex.forPredicate(new 
SelectorPredicateFactory(theString)).computeBitmapResult(resultFactory).get(rowNumber));
+      
Assert.assertFalse(valueSetIndex.forValue(NO_MATCH).computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertFalse(valueSetIndex.forSortedValues(new 
TreeSet<>(ImmutableSet.of(NO_MATCH))).computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertFalse(predicateIndex.forPredicate(new 
SelectorPredicateFactory(NO_MATCH)).computeBitmapResult(resultFactory).get(rowNumber));
+      
Assert.assertFalse(nullValueIndex.forNull().computeBitmapResult(resultFactory).get(rowNumber));
+
+      Assert.assertTrue(dimSelector.makeValueMatcher(theString).matches());
+      Assert.assertFalse(dimSelector.makeValueMatcher(NO_MATCH).matches());
+      Assert.assertTrue(dimSelector.makeValueMatcher(x -> Objects.equals(x, 
theString)).matches());
+      Assert.assertFalse(dimSelector.makeValueMatcher(x -> Objects.equals(x, 
NO_MATCH)).matches());
+    } else {
+      Assert.assertNull(valueSelector.getObject());
+      Assert.assertTrue(valueSelector.isNull());
+
+      Assert.assertEquals(0, dimSelector.getRow().get(0));
+      Assert.assertNull(dimSelector.getObject());
+      Assert.assertNull(dimSelector.lookupName(dimSelector.getRow().get(0)));
+
+      
Assert.assertTrue(valueSetIndex.forValue(null).computeBitmapResult(resultFactory).get(rowNumber));
+      
Assert.assertFalse(valueSetIndex.forValue(NO_MATCH).computeBitmapResult(resultFactory).get(rowNumber));
+      
Assert.assertTrue(nullValueIndex.forNull().computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertTrue(predicateIndex.forPredicate(new 
SelectorPredicateFactory(null)).computeBitmapResult(resultFactory).get(rowNumber));
+      
Assert.assertFalse(valueSetIndex.forValue(NO_MATCH).computeBitmapResult(resultFactory).get(rowNumber));
+      Assert.assertFalse(predicateIndex.forPredicate(new 
SelectorPredicateFactory(NO_MATCH)).computeBitmapResult(resultFactory).get(rowNumber));
+
+      Assert.assertTrue(dimSelector.makeValueMatcher((String) null).matches());
+      Assert.assertFalse(dimSelector.makeValueMatcher(NO_MATCH).matches());
+      Assert.assertTrue(dimSelector.makeValueMatcher(x -> x == 
null).matches());
+      Assert.assertFalse(dimSelector.makeValueMatcher(x -> Objects.equals(x, 
NO_MATCH)).matches());
+    }
+  }
+
   private static class SettableSelector extends 
ObjectColumnSelector<StructuredData>
   {
     private StructuredData data;
diff --git 
a/processing/src/test/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplierTest.java
 
b/processing/src/test/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplierTest.java
index 25fec68bb8..a4dc7bf741 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplierTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/nested/NestedFieldLiteralColumnIndexSupplierTest.java
@@ -210,12 +210,37 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     ImmutableBitmap bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap);
 
+    forRange = rangeIndex.forRange(null, true, "a", true);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(null, false, "b", true);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
     forRange = rangeIndex.forRange(null, false, "b", false);
     Assert.assertNotNull(forRange);
     Assert.assertEquals(0.4, forRange.estimateSelectivity(10), 0.0);
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 1, 3, 7, 8);
 
+
+    forRange = rangeIndex.forRange("a", false, "b", true);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange("a", true, "b", false);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.4, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 1, 3, 7, 8);
+
     forRange = rangeIndex.forRange("b", false, "fon", false);
     Assert.assertNotNull(forRange);
     Assert.assertEquals(0.4, forRange.estimateSelectivity(10), 0.0);
@@ -491,6 +516,24 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 0, 2, 3, 5, 9);
 
+    forRange = rangeIndex.forRange(null, false, "a", true);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(null, false, "b", true);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(null, false, "b", false);
+    Assert.assertNotNull(forRange);
+    Assert.assertEquals(0.1, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 3);
+
     forRange = rangeIndex.forRange("f", false, null, true);
     Assert.assertNotNull(forRange);
     Assert.assertEquals(0.6, forRange.estimateSelectivity(10), 0.0);
@@ -609,6 +652,45 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     ImmutableBitmap bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 0, 2, 6, 7, 8);
 
+    forRange = rangeIndex.forRange(1, true, 3, true);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(1, false, 3, true);
+    Assert.assertEquals(0.3, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 1, 3, 9);
+
+    forRange = rangeIndex.forRange(1, false, 3, false);
+    Assert.assertEquals(0.5, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 1, 3, 4, 5, 9);
+
+
+    forRange = rangeIndex.forRange(100L, true, 300L, true);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+
+    forRange = rangeIndex.forRange(100L, true, 300L, false);
+    Assert.assertEquals(0.3, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 2, 7, 8);
+
+
+    forRange = rangeIndex.forRange(100L, false, 300L, true);
+    Assert.assertEquals(0.2, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0, 6);
+
+
+    forRange = rangeIndex.forRange(100L, false, 300L, false);
+    Assert.assertEquals(0.5, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0, 2, 6, 7, 8);
+
     forRange = rangeIndex.forRange(null, true, null, true);
     Assert.assertEquals(1.0, forRange.estimateSelectivity(10), 0.0);
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
@@ -726,6 +808,26 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     ImmutableBitmap bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 0, 6, 7);
 
+    forRange = rangeIndex.forRange(100, true, 300, true);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(100, false, 300, true);
+    Assert.assertEquals(0.2, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0, 6);
+
+    forRange = rangeIndex.forRange(100, true, 300, false);
+    Assert.assertEquals(0.1, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 7);
+
+    forRange = rangeIndex.forRange(100, false, 300, false);
+    Assert.assertEquals(0.3, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0, 6, 7);
+
     forRange = rangeIndex.forRange(null, true, null, true);
     Assert.assertEquals(0.7, forRange.estimateSelectivity(10), 0.0);
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
@@ -735,6 +837,21 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     Assert.assertEquals(0.7, forRange.estimateSelectivity(10), 0.0);
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 0, 1, 3, 4, 6, 7, 9);
+
+    forRange = rangeIndex.forRange(null, false, 0, false);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(null, false, 1, false);
+    Assert.assertEquals(0.3, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 1, 3, 9);
+
+    forRange = rangeIndex.forRange(null, false, 1, true);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
   }
 
   @Test
@@ -997,6 +1114,26 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     Assert.assertEquals(0.7, forRange.estimateSelectivity(10), 0.0);
     bitmap = forRange.computeBitmapResult(bitmapResultFactory);
     checkBitmap(bitmap, 0, 2, 4, 5, 7, 8, 9);
+
+    forRange = rangeIndex.forRange(null, true, 1.0, true);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+
+    forRange = rangeIndex.forRange(null, true, 1.1, false);
+    Assert.assertEquals(0.2, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0, 8);
+
+    forRange = rangeIndex.forRange(6.6, false, null, false);
+    Assert.assertEquals(0.1, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 5);
+
+    forRange = rangeIndex.forRange(6.6, true, null, false);
+    Assert.assertEquals(0.0, forRange.estimateSelectivity(10), 0.0);
+    bitmap = forRange.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
   }
 
   @Test
@@ -1164,6 +1301,143 @@ public class NestedFieldLiteralColumnIndexSupplierTest 
extends InitializedNullHa
     checkBitmap(lowLevelIndex.getBitmap(-1));
   }
 
+  @Test
+  public void testEnsureNoImproperSelectionFromAdjustedGlobals() throws 
IOException
+  {
+    // make sure we only pick matching values, not "matching" values from not 
validating that
+    // globalId actually exists before looking it up in local dictionary
+    ByteBuffer localDictionaryBuffer = ByteBuffer.allocate(1 << 
10).order(ByteOrder.nativeOrder());
+    ByteBuffer bitmapsBuffer = ByteBuffer.allocate(1 << 10);
+
+    ByteBuffer stringBuffer = ByteBuffer.allocate(1 << 10);
+    ByteBuffer longBuffer = ByteBuffer.allocate(1 << 
10).order(ByteOrder.nativeOrder());
+    ByteBuffer doubleBuffer = ByteBuffer.allocate(1 << 
10).order(ByteOrder.nativeOrder());
+
+    GenericIndexedWriter<String> stringWriter = new GenericIndexedWriter<>(
+        new OnHeapMemorySegmentWriteOutMedium(),
+        "strings",
+        GenericIndexed.STRING_STRATEGY
+    );
+    stringWriter.open();
+    stringWriter.write(null);
+    stringWriter.write("1");
+    writeToBuffer(stringBuffer, stringWriter);
+
+    FixedIndexedWriter<Long> longWriter = new FixedIndexedWriter<>(
+        new OnHeapMemorySegmentWriteOutMedium(),
+        TypeStrategies.LONG,
+        ByteOrder.nativeOrder(),
+        Long.BYTES,
+        true
+    );
+    longWriter.open();
+    longWriter.write(-2L);
+    writeToBuffer(longBuffer, longWriter);
+
+    FixedIndexedWriter<Double> doubleWriter = new FixedIndexedWriter<>(
+        new OnHeapMemorySegmentWriteOutMedium(),
+        TypeStrategies.DOUBLE,
+        ByteOrder.nativeOrder(),
+        Double.BYTES,
+        true
+    );
+    doubleWriter.open();
+    writeToBuffer(doubleBuffer, doubleWriter);
+
+    GenericIndexed<ByteBuffer> strings = GenericIndexed.read(stringBuffer, 
GenericIndexed.UTF8_STRATEGY);
+    Supplier<Indexed<ByteBuffer>> stringIndexed = () -> 
strings.singleThreaded();
+    Supplier<FixedIndexed<Long>> longIndexed = FixedIndexed.read(longBuffer, 
TypeStrategies.LONG, ByteOrder.nativeOrder(), Long.BYTES);
+    Supplier<FixedIndexed<Double>> doubleIndexed = 
FixedIndexed.read(doubleBuffer, TypeStrategies.DOUBLE, ByteOrder.nativeOrder(), 
Double.BYTES);
+
+    FixedIndexedWriter<Integer> localDictionaryWriter = new 
FixedIndexedWriter<>(
+        new OnHeapMemorySegmentWriteOutMedium(),
+        NestedDataColumnSerializer.INT_TYPE_STRATEGY,
+        ByteOrder.nativeOrder(),
+        Integer.BYTES,
+        true
+    );
+    localDictionaryWriter.open();
+    GenericIndexedWriter<ImmutableBitmap> bitmapWriter = new 
GenericIndexedWriter<>(
+        new OnHeapMemorySegmentWriteOutMedium(),
+        "bitmaps",
+        roaringFactory.getObjectStrategy()
+    );
+    bitmapWriter.setObjectsNotSorted();
+    bitmapWriter.open();
+
+    // 10 rows
+    // globals: [
+    //    [null, '1'],
+    //    [-2],
+    //    []
+    // ]
+    // local: [null, '1', -2]
+    // column: ['1', null, -2]
+
+    // null
+    localDictionaryWriter.write(0);
+    bitmapWriter.write(fillBitmap(1));
+
+    // '1'
+    localDictionaryWriter.write(1);
+    bitmapWriter.write(fillBitmap(0));
+
+    // -2
+    localDictionaryWriter.write(2);
+    bitmapWriter.write(fillBitmap(2));
+
+    writeToBuffer(localDictionaryBuffer, localDictionaryWriter);
+    writeToBuffer(bitmapsBuffer, bitmapWriter);
+
+    Supplier<FixedIndexed<Integer>> dictionarySupplier = FixedIndexed.read(
+        localDictionaryBuffer,
+        NestedDataColumnSerializer.INT_TYPE_STRATEGY,
+        ByteOrder.nativeOrder(),
+        Integer.BYTES
+    );
+
+    GenericIndexed<ImmutableBitmap> bitmaps = 
GenericIndexed.read(bitmapsBuffer, roaringFactory.getObjectStrategy());
+
+    NestedFieldLiteralColumnIndexSupplier<?> indexSupplier = new 
NestedFieldLiteralColumnIndexSupplier<>(
+        new NestedLiteralTypeInfo.TypeSet(
+            new NestedLiteralTypeInfo.MutableTypeSet().add(ColumnType.STRING)
+                                                      .add(ColumnType.LONG)
+                                                      .getByteValue()
+        ),
+        roaringFactory.getBitmapFactory(),
+        bitmaps,
+        dictionarySupplier,
+        stringIndexed,
+        longIndexed,
+        doubleIndexed
+    );
+
+    StringValueSetIndex valueSetIndex = 
indexSupplier.as(StringValueSetIndex.class);
+    Assert.assertNotNull(valueSetIndex);
+
+    // 3 rows
+    // local: [null, '1', -2]
+    // column: ['1', null, -2]
+
+    BitmapColumnIndex columnIndex = valueSetIndex.forValue("1");
+    Assert.assertNotNull(columnIndex);
+    Assert.assertEquals(0.3333, columnIndex.estimateSelectivity(3), 0.001);
+    ImmutableBitmap bitmap = 
columnIndex.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 0);
+
+    columnIndex = valueSetIndex.forValue("-2");
+    Assert.assertNotNull(columnIndex);
+    Assert.assertEquals(0.3333, columnIndex.estimateSelectivity(3), 0.001);
+    bitmap = columnIndex.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap, 2);
+
+    columnIndex = valueSetIndex.forValue("2");
+    Assert.assertNotNull(columnIndex);
+    Assert.assertEquals(0.0, columnIndex.estimateSelectivity(3), 0.0);
+    bitmap = columnIndex.computeBitmapResult(bitmapResultFactory);
+    checkBitmap(bitmap);
+  }
+
   private NestedFieldLiteralColumnIndexSupplier<?> 
makeSingleTypeStringSupplier() throws IOException
   {
     ByteBuffer localDictionaryBuffer = ByteBuffer.allocate(1 << 
12).order(ByteOrder.nativeOrder());


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

Reply via email to