You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by hi...@apache.org on 2020/04/06 05:29:54 UTC

[druid] branch master updated: Eliminate common subfilters when converting it to a CNF (#9608)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 40e84a1  Eliminate common subfilters when converting it to a CNF (#9608)
40e84a1 is described below

commit 40e84a171b8be8e87db20b0c5c189aef1b860f41
Author: Jihoon Son <ji...@apache.org>
AuthorDate: Sun Apr 5 22:29:41 2020 -0700

    Eliminate common subfilters when converting it to a CNF (#9608)
---
 .../druid/benchmark/FilterPartitionBenchmark.java  |   4 +-
 .../apache/druid/query/filter/BooleanFilter.java   |   5 +-
 .../apache/druid/query/filter/DimFilterUtils.java  |   4 +-
 .../apache/druid/query/filter/TrueDimFilter.java   |   8 +-
 .../segment/QueryableIndexStorageAdapter.java      |   9 +-
 .../org/apache/druid/segment/filter/AndFilter.java |  33 ++--
 .../org/apache/druid/segment/filter/Filters.java   |  56 +++---
 .../org/apache/druid/segment/filter/NotFilter.java |  32 +++-
 .../org/apache/druid/segment/filter/OrFilter.java  |  28 ++-
 .../apache/druid/segment/filter/TrueFilter.java    |   9 +-
 .../segment/join/filter/JoinFilterAnalyzer.java    |   8 +-
 .../druid/query/filter/AndDimFilterTest.java       |  29 ++-
 ...dDimFilterTest.java => DimFilterTestUtils.java} |  34 ++--
 .../apache/druid/query/filter/OrDimFilterTest.java |  53 +++++
 .../druid/segment/filter/BaseFilterTest.java       |   2 +-
 .../druid/segment/filter/FilterPartitionTest.java  |   4 +-
 .../filter/FilterTestUtils.java}                   |  38 ++--
 .../apache/druid/segment/filter/FiltersTest.java   | 213 ++++++++++++++++++++-
 ...tFilterTest.java => NotFilterEvaluateTest.java} |   6 +-
 .../apache/druid/segment/filter/NotFilterTest.java |  84 ++------
 20 files changed, 473 insertions(+), 186 deletions(-)

diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java
index b52cde1..547784e 100644
--- a/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java
+++ b/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java
@@ -377,7 +377,7 @@ public class FilterPartitionBenchmark
     Filter orFilter = new OrFilter(Arrays.asList(filter, filter2));
 
     StorageAdapter sa = new QueryableIndexStorageAdapter(qIndex);
-    Sequence<Cursor> cursors = makeCursors(sa, Filters.convertToCNF(orFilter));
+    Sequence<Cursor> cursors = makeCursors(sa, Filters.toCNF(orFilter));
     readCursors(cursors, blackhole);
   }
 
@@ -451,7 +451,7 @@ public class FilterPartitionBenchmark
     );
 
     StorageAdapter sa = new QueryableIndexStorageAdapter(qIndex);
-    Sequence<Cursor> cursors = makeCursors(sa, Filters.convertToCNF(dimFilter3.toFilter()));
+    Sequence<Cursor> cursors = makeCursors(sa, Filters.toCNF(dimFilter3.toFilter()));
     readCursors(cursors, blackhole);
   }
 
diff --git a/processing/src/main/java/org/apache/druid/query/filter/BooleanFilter.java b/processing/src/main/java/org/apache/druid/query/filter/BooleanFilter.java
index 9bbbdb6..e11153e 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/BooleanFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/BooleanFilter.java
@@ -23,12 +23,13 @@ import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.ColumnSelectorFactory;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 public interface BooleanFilter extends Filter
 {
-  List<Filter> getFilters();
+  ValueMatcher[] EMPTY_VALUE_MATCHER_ARRAY = new ValueMatcher[0];
+
+  Set<Filter> getFilters();
 
   /**
    * Get a ValueMatcher that applies this filter to row values.
diff --git a/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java b/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
index 00a84fc..c980adb 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
@@ -52,9 +52,9 @@ public class DimFilterUtils
   static final byte COLUMN_COMPARISON_CACHE_ID = 0xD;
   static final byte EXPRESSION_CACHE_ID = 0xE;
   static final byte TRUE_CACHE_ID = 0xF;
-  public static byte BLOOM_DIM_FILTER_CACHE_ID = 0x10;
-  public static final byte STRING_SEPARATOR = (byte) 0xFF;
+  public static final byte BLOOM_DIM_FILTER_CACHE_ID = 0x10;
 
+  public static final byte STRING_SEPARATOR = (byte) 0xFF;
 
   static byte[] computeCacheKey(byte cacheIdKey, List<DimFilter> filters)
   {
diff --git a/processing/src/main/java/org/apache/druid/query/filter/TrueDimFilter.java b/processing/src/main/java/org/apache/druid/query/filter/TrueDimFilter.java
index d10e6d9..2254358 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/TrueDimFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/TrueDimFilter.java
@@ -20,9 +20,9 @@
 package org.apache.druid.query.filter;
 
 import com.google.common.collect.RangeSet;
+import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.segment.filter.TrueFilter;
 
-import java.nio.ByteBuffer;
 import java.util.Collections;
 import java.util.Set;
 
@@ -32,8 +32,8 @@ public class TrueDimFilter implements DimFilter
 {
   @Override
   public byte[] getCacheKey()
-  {        
-    return ByteBuffer.allocate(1).put(DimFilterUtils.TRUE_CACHE_ID).array();
+  {
+    return new CacheKeyBuilder(DimFilterUtils.TRUE_CACHE_ID).build();
   }
 
   @Override
@@ -45,7 +45,7 @@ public class TrueDimFilter implements DimFilter
   @Override
   public Filter toFilter()
   {
-    return new TrueFilter();
+    return TrueFilter.instance();
   }
 
   @Override
diff --git a/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
index cbb6446..70f4c9c 100644
--- a/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
@@ -54,6 +54,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  *
@@ -384,13 +385,13 @@ public class QueryableIndexStorageAdapter implements StorageAdapter
      *
      * Any subfilters that cannot be processed entirely with bitmap indexes will be moved to the post-filtering stage.
      */
-    final List<Filter> preFilters;
+    final Set<Filter> preFilters;
     final List<Filter> postFilters = new ArrayList<>();
     int preFilteredRows = totalRows;
     if (filter == null) {
-      preFilters = Collections.emptyList();
+      preFilters = Collections.emptySet();
     } else {
-      preFilters = new ArrayList<>();
+      preFilters = new HashSet<>();
 
       if (filter instanceof AndFilter) {
         // If we get an AndFilter, we can split the subfilters across both filtering stages
@@ -432,7 +433,7 @@ public class QueryableIndexStorageAdapter implements StorageAdapter
     }
 
     if (queryMetrics != null) {
-      queryMetrics.preFilters(preFilters);
+      queryMetrics.preFilters(new ArrayList<>(preFilters));
       queryMetrics.postFilters(postFilters);
       queryMetrics.reportSegmentRows(totalRows);
       queryMetrics.reportPreFilteredRows(preFilteredRows);
diff --git a/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
index 93c890a..696bd8b 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
@@ -19,8 +19,10 @@
 
 package org.apache.druid.segment.filter;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.apache.druid.collections.bitmap.ImmutableBitmap;
 import org.apache.druid.java.util.common.StringUtils;
@@ -38,20 +40,27 @@ import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  */
 public class AndFilter implements BooleanFilter
 {
   private static final Joiner AND_JOINER = Joiner.on(" && ");
-  static final ValueMatcher[] EMPTY_VALUE_MATCHER_ARRAY = new ValueMatcher[0];
 
-  private final List<Filter> filters;
+  private final Set<Filter> filters;
 
+  @VisibleForTesting
   public AndFilter(List<Filter> filters)
   {
+    this(new HashSet<>(filters));
+  }
+
+  public AndFilter(Set<Filter> filters)
+  {
     Preconditions.checkArgument(filters.size() > 0, "Can't construct empty AndFilter");
     this.filters = filters;
   }
@@ -59,7 +68,7 @@ public class AndFilter implements BooleanFilter
   public static <T> ImmutableBitmap getBitmapIndex(
       BitmapIndexSelector selector,
       BitmapResultFactory<T> bitmapResultFactory,
-      List<Filter> filters
+      Set<Filter> filters
   )
   {
     return bitmapResultFactory.toImmutableBitmap(getBitmapResult(selector, bitmapResultFactory, filters));
@@ -68,11 +77,11 @@ public class AndFilter implements BooleanFilter
   private static <T> T getBitmapResult(
       BitmapIndexSelector selector,
       BitmapResultFactory<T> bitmapResultFactory,
-      List<Filter> filters
+      Set<Filter> filters
   )
   {
     if (filters.size() == 1) {
-      return filters.get(0).getBitmapResult(selector, bitmapResultFactory);
+      return Iterables.getOnlyElement(filters).getBitmapResult(selector, bitmapResultFactory);
     }
 
     final List<T> bitmapResults = Lists.newArrayListWithCapacity(filters.size());
@@ -102,8 +111,9 @@ public class AndFilter implements BooleanFilter
   {
     final ValueMatcher[] matchers = new ValueMatcher[filters.size()];
 
-    for (int i = 0; i < filters.size(); i++) {
-      matchers[i] = filters.get(i).makeMatcher(factory);
+    int i = 0;
+    for (Filter filter : filters) {
+      matchers[i++] = filter.makeMatcher(factory);
     }
     return makeMatcher(matchers);
   }
@@ -113,8 +123,9 @@ public class AndFilter implements BooleanFilter
   {
     final VectorValueMatcher[] matchers = new VectorValueMatcher[filters.size()];
 
-    for (int i = 0; i < filters.size(); i++) {
-      matchers[i] = filters.get(i).makeVectorMatcher(factory);
+    int i = 0;
+    for (Filter filter : filters) {
+      matchers[i++] = filter.makeVectorMatcher(factory);
     }
     return makeVectorMatcher(matchers);
   }
@@ -150,11 +161,11 @@ public class AndFilter implements BooleanFilter
       matchers.add(0, offsetMatcher);
     }
 
-    return makeMatcher(matchers.toArray(EMPTY_VALUE_MATCHER_ARRAY));
+    return makeMatcher(matchers.toArray(BooleanFilter.EMPTY_VALUE_MATCHER_ARRAY));
   }
 
   @Override
-  public List<Filter> getFilters()
+  public Set<Filter> getFilters()
   {
     return filters;
   }
diff --git a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java
index 4e865f6..2328dfb 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java
@@ -19,16 +19,14 @@
 
 package org.apache.druid.segment.filter;
 
-import com.google.common.base.Function;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import it.unimi.dsi.fastutil.ints.IntIterable;
 import it.unimi.dsi.fastutil.ints.IntIterator;
 import it.unimi.dsi.fastutil.ints.IntList;
 import org.apache.druid.collections.bitmap.ImmutableBitmap;
-import org.apache.druid.java.util.common.guava.FunctionalIterable;
 import org.apache.druid.query.BitmapResultFactory;
 import org.apache.druid.query.Query;
 import org.apache.druid.query.filter.BitmapIndexSelector;
@@ -51,9 +49,12 @@ import javax.annotation.Nullable;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  *
@@ -69,22 +70,9 @@ public class Filters
    *
    * @return list of Filters
    */
-  public static List<Filter> toFilters(List<DimFilter> dimFilters)
+  public static Set<Filter> toFilters(List<DimFilter> dimFilters)
   {
-    return ImmutableList.copyOf(
-        FunctionalIterable
-            .create(dimFilters)
-            .transform(
-                new Function<DimFilter, Filter>()
-                {
-                  @Override
-                  public Filter apply(DimFilter input)
-                  {
-                    return input.toFilter();
-                  }
-                }
-            )
-    );
+    return dimFilters.stream().map(DimFilter::toFilter).collect(Collectors.toSet());
   }
 
   /**
@@ -438,10 +426,10 @@ public class Filters
       return null;
     }
     boolean useCNF = query.getContextBoolean(CTX_KEY_USE_FILTER_CNF, false);
-    return useCNF ? convertToCNF(filter) : filter;
+    return useCNF ? toCNF(filter) : filter;
   }
 
-  public static Filter convertToCNF(Filter current)
+  public static Filter toCNF(Filter current)
   {
     current = pushDownNot(current);
     current = flatten(current);
@@ -452,7 +440,8 @@ public class Filters
 
   // CNF conversion functions were adapted from Apache Hive, see:
   // https://github.com/apache/hive/blob/branch-2.0/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java
-  private static Filter pushDownNot(Filter current)
+  @VisibleForTesting
+  static Filter pushDownNot(Filter current)
   {
     if (current instanceof NotFilter) {
       Filter child = ((NotFilter) current).getBaseFilter();
@@ -460,14 +449,14 @@ public class Filters
         return pushDownNot(((NotFilter) child).getBaseFilter());
       }
       if (child instanceof AndFilter) {
-        List<Filter> children = new ArrayList<>();
+        Set<Filter> children = new HashSet<>();
         for (Filter grandChild : ((AndFilter) child).getFilters()) {
           children.add(pushDownNot(new NotFilter(grandChild)));
         }
         return new OrFilter(children);
       }
       if (child instanceof OrFilter) {
-        List<Filter> children = new ArrayList<>();
+        Set<Filter> children = new HashSet<>();
         for (Filter grandChild : ((OrFilter) child).getFilters()) {
           children.add(pushDownNot(new NotFilter(grandChild)));
         }
@@ -477,7 +466,7 @@ public class Filters
 
 
     if (current instanceof AndFilter) {
-      List<Filter> children = new ArrayList<>();
+      Set<Filter> children = new HashSet<>();
       for (Filter child : ((AndFilter) current).getFilters()) {
         children.add(pushDownNot(child));
       }
@@ -486,7 +475,7 @@ public class Filters
 
 
     if (current instanceof OrFilter) {
-      List<Filter> children = new ArrayList<>();
+      Set<Filter> children = new HashSet<>();
       for (Filter child : ((OrFilter) current).getFilters()) {
         children.add(pushDownNot(child));
       }
@@ -503,7 +492,7 @@ public class Filters
       return new NotFilter(convertToCNFInternal(((NotFilter) current).getBaseFilter()));
     }
     if (current instanceof AndFilter) {
-      List<Filter> children = new ArrayList<>();
+      Set<Filter> children = new HashSet<>();
       for (Filter child : ((AndFilter) current).getFilters()) {
         children.add(convertToCNFInternal(child));
       }
@@ -525,7 +514,7 @@ public class Filters
         }
       }
       if (!andList.isEmpty()) {
-        List<Filter> result = new ArrayList<>();
+        Set<Filter> result = new HashSet<>();
         generateAllCombinations(result, andList, nonAndList);
         return new AndFilter(result);
       }
@@ -535,7 +524,8 @@ public class Filters
 
   // CNF conversion functions were adapted from Apache Hive, see:
   // https://github.com/apache/hive/blob/branch-2.0/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java
-  private static Filter flatten(Filter root)
+  @VisibleForTesting
+  static Filter flatten(Filter root)
   {
     if (root instanceof BooleanFilter) {
       List<Filter> children = new ArrayList<>(((BooleanFilter) root).getFilters());
@@ -546,7 +536,7 @@ public class Filters
         // do we need to flatten?
         if (child.getClass() == root.getClass() && !(child instanceof NotFilter)) {
           boolean first = true;
-          List<Filter> grandKids = ((BooleanFilter) child).getFilters();
+          Set<Filter> grandKids = ((BooleanFilter) child).getFilters();
           for (Filter grandkid : grandKids) {
             // for the first grandkid replace the original parent
             if (first) {
@@ -577,15 +567,15 @@ public class Filters
   // CNF conversion functions were adapted from Apache Hive, see:
   // https://github.com/apache/hive/blob/branch-2.0/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java
   private static void generateAllCombinations(
-      List<Filter> result,
+      Set<Filter> result,
       List<Filter> andList,
       List<Filter> nonAndList
   )
   {
-    List<Filter> children = ((AndFilter) andList.get(0)).getFilters();
+    Set<Filter> children = ((AndFilter) andList.get(0)).getFilters();
     if (result.isEmpty()) {
       for (Filter child : children) {
-        List<Filter> a = Lists.newArrayList(nonAndList);
+        Set<Filter> a = new HashSet<>(nonAndList);
         a.add(child);
         result.add(new OrFilter(a));
       }
@@ -594,7 +584,7 @@ public class Filters
       result.clear();
       for (Filter child : children) {
         for (Filter or : work) {
-          List<Filter> a = Lists.newArrayList((((OrFilter) or).getFilters()));
+          Set<Filter> a = new HashSet<>((((OrFilter) or).getFilters()));
           a.add(child);
           result.add(new OrFilter(a));
         }
diff --git a/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
index 3a39e2f..a82a57f 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.segment.filter;
 
+import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.query.BitmapResultFactory;
 import org.apache.druid.query.filter.BitmapIndexSelector;
 import org.apache.druid.query.filter.Filter;
@@ -32,6 +33,7 @@ import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -40,9 +42,7 @@ public class NotFilter implements Filter
 {
   private final Filter baseFilter;
 
-  public NotFilter(
-      Filter baseFilter
-  )
+  public NotFilter(Filter baseFilter)
   {
     this.baseFilter = baseFilter;
   }
@@ -135,6 +135,32 @@ public class NotFilter implements Filter
     return 1. - baseFilter.estimateSelectivity(indexSelector);
   }
 
+  @Override
+  public String toString()
+  {
+    return StringUtils.format("~(%s)", baseFilter);
+  }
+
+  @Override
+  public boolean equals(Object o)
+  {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    NotFilter notFilter = (NotFilter) o;
+    return Objects.equals(baseFilter, notFilter.baseFilter);
+  }
+
+  @Override
+  public int hashCode()
+  {
+    // to return a different hash from baseFilter
+    return Objects.hash(1, baseFilter);
+  }
+
   public Filter getBaseFilter()
   {
     return baseFilter;
diff --git a/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
index 9e5314b..46cd9e2 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
@@ -19,8 +19,10 @@
 
 package org.apache.druid.segment.filter;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import org.apache.druid.collections.bitmap.ImmutableBitmap;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.query.BitmapResultFactory;
@@ -38,8 +40,10 @@ import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  */
@@ -47,10 +51,16 @@ public class OrFilter implements BooleanFilter
 {
   private static final Joiner OR_JOINER = Joiner.on(" || ");
 
-  private final List<Filter> filters;
+  private final Set<Filter> filters;
 
+  @VisibleForTesting
   public OrFilter(List<Filter> filters)
   {
+    this(new HashSet<>(filters));
+  }
+
+  public OrFilter(Set<Filter> filters)
+  {
     Preconditions.checkArgument(filters.size() > 0, "Can't construct empty OrFilter (the universe does not exist)");
 
     this.filters = filters;
@@ -60,7 +70,7 @@ public class OrFilter implements BooleanFilter
   public <T> T getBitmapResult(BitmapIndexSelector selector, BitmapResultFactory<T> bitmapResultFactory)
   {
     if (filters.size() == 1) {
-      return filters.get(0).getBitmapResult(selector, bitmapResultFactory);
+      return Iterables.getOnlyElement(filters).getBitmapResult(selector, bitmapResultFactory);
     }
 
     List<T> bitmapResults = new ArrayList<>();
@@ -76,8 +86,9 @@ public class OrFilter implements BooleanFilter
   {
     final ValueMatcher[] matchers = new ValueMatcher[filters.size()];
 
-    for (int i = 0; i < filters.size(); i++) {
-      matchers[i] = filters.get(i).makeMatcher(factory);
+    int i = 0;
+    for (Filter filter : filters) {
+      matchers[i++] = filter.makeMatcher(factory);
     }
     return makeMatcher(matchers);
   }
@@ -87,8 +98,9 @@ public class OrFilter implements BooleanFilter
   {
     final VectorValueMatcher[] matchers = new VectorValueMatcher[filters.size()];
 
-    for (int i = 0; i < filters.size(); i++) {
-      matchers[i] = filters.get(i).makeVectorMatcher(factory);
+    int i = 0;
+    for (Filter filter : filters) {
+      matchers[i++] = filter.makeVectorMatcher(factory);
     }
     return makeVectorMatcher(matchers);
   }
@@ -124,11 +136,11 @@ public class OrFilter implements BooleanFilter
       matchers.add(0, offsetMatcher);
     }
 
-    return makeMatcher(matchers.toArray(AndFilter.EMPTY_VALUE_MATCHER_ARRAY));
+    return makeMatcher(matchers.toArray(BooleanFilter.EMPTY_VALUE_MATCHER_ARRAY));
   }
 
   @Override
-  public List<Filter> getFilters()
+  public Set<Filter> getFilters()
   {
     return filters;
   }
diff --git a/processing/src/main/java/org/apache/druid/segment/filter/TrueFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/TrueFilter.java
index 2ae0edf..58cda8c 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/TrueFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/TrueFilter.java
@@ -33,7 +33,14 @@ import java.util.Set;
  */
 public class TrueFilter implements Filter
 {
-  public TrueFilter()
+  private static final TrueFilter INSTANCE = new TrueFilter();
+
+  public static TrueFilter instance()
+  {
+    return INSTANCE;
+  }
+
+  private TrueFilter()
   {
   }
 
diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java
index d8cd4f6..892fcb3 100644
--- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java
+++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java
@@ -128,17 +128,17 @@ public class JoinFilterAnalyzer
       );
     }
 
-    Filter normalizedFilter = Filters.convertToCNF(originalFilter);
+    Filter normalizedFilter = Filters.toCNF(originalFilter);
 
     // List of candidates for pushdown
     // CNF normalization will generate either
     // - an AND filter with multiple subfilters
     // - or a single non-AND subfilter which cannot be split further
-    List<Filter> normalizedOrClauses;
+    Set<Filter> normalizedOrClauses;
     if (normalizedFilter instanceof AndFilter) {
       normalizedOrClauses = ((AndFilter) normalizedFilter).getFilters();
     } else {
-      normalizedOrClauses = Collections.singletonList(normalizedFilter);
+      normalizedOrClauses = Collections.singleton(normalizedFilter);
     }
 
     List<Filter> normalizedBaseTableClauses = new ArrayList<>();
@@ -422,7 +422,7 @@ public class JoinFilterAnalyzer
   )
   {
     boolean retainRhs = false;
-    List<Filter> newFilters = new ArrayList<>();
+    Set<Filter> newFilters = new HashSet<>();
     for (Filter filter : orFilter.getFilters()) {
       if (!areSomeColumnsFromJoin(joinFilterPreAnalysis.getJoinableClauses(), filter.getRequiredColumns())) {
         newFilters.add(filter);
diff --git a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java b/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
index f2d6c43..7aeae01 100644
--- a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
@@ -21,10 +21,12 @@ package org.apache.druid.query.filter;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import org.apache.druid.segment.filter.FilterTestUtils;
+import org.apache.druid.testing.InitializedNullHandlingTest;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class AndDimFilterTest
+public class AndDimFilterTest extends InitializedNullHandlingTest
 {
   @Test
   public void testGetRequiredColumns()
@@ -38,4 +40,29 @@ public class AndDimFilterTest
     );
     Assert.assertEquals(andDimFilter.getRequiredColumns(), Sets.newHashSet("a", "b", "c"));
   }
+
+  @Test
+  public void testToFilterWithDuplicateFilters()
+  {
+    DimFilter dimFilter = DimFilterTestUtils.and(
+        DimFilterTestUtils.or(
+            DimFilterTestUtils.selector("col1", "1"),
+            DimFilterTestUtils.selector("col2", "2")
+        ),
+        DimFilterTestUtils.or(
+            // duplicate but different order
+            DimFilterTestUtils.selector("col2", "2"),
+            DimFilterTestUtils.selector("col1", "1")
+        ),
+        DimFilterTestUtils.selector("col3", "3")
+    );
+    Filter expected = FilterTestUtils.and(
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "1"),
+            FilterTestUtils.selector("col2", "2")
+        ),
+        FilterTestUtils.selector("col3", "3")
+    );
+    Assert.assertEquals(expected, dimFilter.toFilter());
+  }
 }
diff --git a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java b/processing/src/test/java/org/apache/druid/query/filter/DimFilterTestUtils.java
similarity index 61%
copy from processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
copy to processing/src/test/java/org/apache/druid/query/filter/DimFilterTestUtils.java
index f2d6c43..87fa026 100644
--- a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/query/filter/DimFilterTestUtils.java
@@ -19,23 +19,27 @@
 
 package org.apache.druid.query.filter;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.junit.Assert;
-import org.junit.Test;
+import java.util.Arrays;
 
-public class AndDimFilterTest
+public class DimFilterTestUtils
 {
-  @Test
-  public void testGetRequiredColumns()
+  public static AndDimFilter and(DimFilter... filters)
   {
-    AndDimFilter andDimFilter = new AndDimFilter(
-        Lists.newArrayList(
-            new SelectorDimFilter("a", "d", null),
-            new SelectorDimFilter("b", "d", null),
-            new SelectorDimFilter("c", "d", null)
-        )
-    );
-    Assert.assertEquals(andDimFilter.getRequiredColumns(), Sets.newHashSet("a", "b", "c"));
+    return new AndDimFilter(Arrays.asList(filters));
+  }
+
+  public static OrDimFilter or(DimFilter... filters)
+  {
+    return new OrDimFilter(Arrays.asList(filters));
+  }
+
+  public static NotDimFilter not(DimFilter filter)
+  {
+    return new NotDimFilter(filter);
+  }
+
+  public static SelectorDimFilter selector(final String fieldName, final String value)
+  {
+    return new SelectorDimFilter(fieldName, value, null);
   }
 }
diff --git a/processing/src/test/java/org/apache/druid/query/filter/OrDimFilterTest.java b/processing/src/test/java/org/apache/druid/query/filter/OrDimFilterTest.java
new file mode 100644
index 0000000..9f02194
--- /dev/null
+++ b/processing/src/test/java/org/apache/druid/query/filter/OrDimFilterTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.query.filter;
+
+import org.apache.druid.segment.filter.FilterTestUtils;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class OrDimFilterTest extends InitializedNullHandlingTest
+{
+  @Test
+  public void testToFilterWithDuplicateFilters()
+  {
+    DimFilter dimFilter = DimFilterTestUtils.or(
+        DimFilterTestUtils.and(
+            DimFilterTestUtils.selector("col1", "1"),
+            DimFilterTestUtils.selector("col2", "2")
+        ),
+        DimFilterTestUtils.and(
+            // duplicate but different order
+            DimFilterTestUtils.selector("col2", "2"),
+            DimFilterTestUtils.selector("col1", "1")
+        ),
+        DimFilterTestUtils.selector("col3", "3")
+    );
+    Filter expected = FilterTestUtils.or(
+        FilterTestUtils.and(
+            FilterTestUtils.selector("col1", "1"),
+            FilterTestUtils.selector("col2", "2")
+        ),
+        FilterTestUtils.selector("col3", "3")
+    );
+    Assert.assertEquals(expected, dimFilter.toFilter());
+  }
+}
diff --git a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
index 8a704d2..5700458 100644
--- a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
@@ -336,7 +336,7 @@ public abstract class BaseFilterTest extends InitializedNullHandlingTest
 
     final DimFilter maybeOptimized = optimize ? dimFilter.optimize() : dimFilter;
     final Filter filter = maybeOptimized.toFilter();
-    return cnf ? Filters.convertToCNF(filter) : filter;
+    return cnf ? Filters.toCNF(filter) : filter;
   }
 
   private DimFilter maybeOptimize(final DimFilter dimFilter)
diff --git a/processing/src/test/java/org/apache/druid/segment/filter/FilterPartitionTest.java b/processing/src/test/java/org/apache/druid/segment/filter/FilterPartitionTest.java
index bb76c54..5ffd167 100644
--- a/processing/src/test/java/org/apache/druid/segment/filter/FilterPartitionTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/FilterPartitionTest.java
@@ -621,7 +621,7 @@ public class FilterPartitionTest extends BaseFilterTest
     );
 
     Filter filter1 = dimFilter1.toFilter();
-    Filter filter1CNF = Filters.convertToCNF(filter1);
+    Filter filter1CNF = Filters.toCNF(filter1);
 
     Assert.assertEquals(AndFilter.class, filter1CNF.getClass());
     Assert.assertEquals(2, ((AndFilter) filter1CNF).getFilters().size());
@@ -675,7 +675,7 @@ public class FilterPartitionTest extends BaseFilterTest
     );
 
     Filter filter1 = dimFilter1.toFilter();
-    Filter filter1CNF = Filters.convertToCNF(filter1);
+    Filter filter1CNF = Filters.toCNF(filter1);
 
     Assert.assertEquals(AndFilter.class, filter1CNF.getClass());
     Assert.assertEquals(2, ((AndFilter) filter1CNF).getFilters().size());
diff --git a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java b/processing/src/test/java/org/apache/druid/segment/filter/FilterTestUtils.java
similarity index 58%
copy from processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
copy to processing/src/test/java/org/apache/druid/segment/filter/FilterTestUtils.java
index f2d6c43..003d8d8 100644
--- a/processing/src/test/java/org/apache/druid/query/filter/AndDimFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/FilterTestUtils.java
@@ -17,25 +17,31 @@
  * under the License.
  */
 
-package org.apache.druid.query.filter;
+package org.apache.druid.segment.filter;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.druid.query.filter.Filter;
 
-public class AndDimFilterTest
+import java.util.Arrays;
+
+public class FilterTestUtils
 {
-  @Test
-  public void testGetRequiredColumns()
+  public static AndFilter and(Filter... filters)
+  {
+    return new AndFilter(Arrays.asList(filters));
+  }
+
+  public static OrFilter or(Filter... filters)
+  {
+    return new OrFilter(Arrays.asList(filters));
+  }
+
+  public static NotFilter not(Filter filter)
+  {
+    return new NotFilter(filter);
+  }
+
+  public static SelectorFilter selector(final String fieldName, final String value)
   {
-    AndDimFilter andDimFilter = new AndDimFilter(
-        Lists.newArrayList(
-            new SelectorDimFilter("a", "d", null),
-            new SelectorDimFilter("b", "d", null),
-            new SelectorDimFilter("c", "d", null)
-        )
-    );
-    Assert.assertEquals(andDimFilter.getRequiredColumns(), Sets.newHashSet("a", "b", "c"));
+    return new SelectorFilter(fieldName, value, null);
   }
 }
diff --git a/processing/src/test/java/org/apache/druid/segment/filter/FiltersTest.java b/processing/src/test/java/org/apache/druid/segment/filter/FiltersTest.java
index 56b993c..70b527d 100644
--- a/processing/src/test/java/org/apache/druid/segment/filter/FiltersTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/FiltersTest.java
@@ -25,14 +25,16 @@ import org.apache.druid.collections.bitmap.BitmapFactory;
 import org.apache.druid.collections.bitmap.ConciseBitmapFactory;
 import org.apache.druid.collections.bitmap.ImmutableBitmap;
 import org.apache.druid.collections.bitmap.MutableBitmap;
+import org.apache.druid.query.filter.Filter;
 import org.apache.druid.segment.IntIteratorUtils;
 import org.apache.druid.segment.column.BitmapIndex;
+import org.apache.druid.testing.InitializedNullHandlingTest;
 import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.List;
 
-public class FiltersTest
+public class FiltersTest extends InitializedNullHandlingTest
 {
   @Test
   public void testEstimateSelectivityOfBitmapList()
@@ -50,6 +52,215 @@ public class FiltersTest
     Assert.assertEquals(expected, estimated, 0.00001);
   }
 
+  @Test
+  public void testPushDownNot()
+  {
+    final Filter filter = FilterTestUtils.not(
+        FilterTestUtils.and(
+            FilterTestUtils.selector("col1", "1"),
+            FilterTestUtils.selector("col2", "2"),
+            FilterTestUtils.not(FilterTestUtils.selector("col3", "3"))
+        )
+    );
+    final Filter expected = FilterTestUtils.or(
+        FilterTestUtils.not(FilterTestUtils.selector("col1", "1")),
+        FilterTestUtils.not(FilterTestUtils.selector("col2", "2")),
+        FilterTestUtils.selector("col3", "3")
+    );
+    Assert.assertEquals(expected, Filters.pushDownNot(filter));
+  }
+
+  @Test
+  public void testPushDownNotLeafNot()
+  {
+    final Filter filter = FilterTestUtils.and(
+        FilterTestUtils.selector("col1", "1"),
+        FilterTestUtils.selector("col2", "2"),
+        FilterTestUtils.not(FilterTestUtils.selector("col3", "3"))
+    );
+    Assert.assertEquals(filter, Filters.pushDownNot(filter));
+  }
+
+  @Test
+  public void testFlatten()
+  {
+    final Filter filter = FilterTestUtils.and(
+        FilterTestUtils.and(
+            FilterTestUtils.and(
+                FilterTestUtils.selector("col1", "1"),
+                FilterTestUtils.selector("col2", "2")
+            )
+        ),
+        FilterTestUtils.selector("col3", "3")
+    );
+    final Filter expected = FilterTestUtils.and(
+        FilterTestUtils.selector("col1", "1"),
+        FilterTestUtils.selector("col2", "2"),
+        FilterTestUtils.selector("col3", "3")
+    );
+    Assert.assertEquals(expected, Filters.flatten(filter));
+  }
+
+  @Test
+  public void testFlattenUnflattenable()
+  {
+    final Filter filter = FilterTestUtils.and(
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "1"),
+            FilterTestUtils.selector("col2", "2")
+        ),
+        FilterTestUtils.selector("col3", "3")
+    );
+    Assert.assertEquals(filter, Filters.flatten(filter));
+  }
+
+  @Test
+  public void testToCNFWithMuchReducibleFilter()
+  {
+    final Filter muchReducible = FilterTestUtils.and(
+        // should be flattened
+        FilterTestUtils.and(
+            FilterTestUtils.and(
+                FilterTestUtils.and(FilterTestUtils.selector("col1", "val1"))
+            )
+        ),
+        // should be flattened
+        FilterTestUtils.and(
+            FilterTestUtils.or(
+                FilterTestUtils.and(FilterTestUtils.selector("col1", "val1"))
+            )
+        ),
+        // should be flattened
+        FilterTestUtils.or(
+            FilterTestUtils.and(
+                FilterTestUtils.or(FilterTestUtils.selector("col1", "val1"))
+            )
+        ),
+        // should eliminate duplicate filters
+        FilterTestUtils.selector("col1", "val1"),
+        FilterTestUtils.selector("col2", "val2"),
+        FilterTestUtils.and(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.selector("col2", "val2")
+        ),
+        FilterTestUtils.and(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.and(
+                FilterTestUtils.selector("col2", "val2"),
+                FilterTestUtils.selector("col1", "val1")
+            )
+        )
+    );
+    final Filter expected = FilterTestUtils.and(
+        FilterTestUtils.selector("col1", "val1"),
+        FilterTestUtils.selector("col2", "val2")
+    );
+    Assert.assertEquals(expected, Filters.toCNF(muchReducible));
+  }
+
+  @Test
+  public void testToCNFWithComplexFilterIncludingNotAndOr()
+  {
+    final Filter filter = FilterTestUtils.and(
+        FilterTestUtils.or(
+            FilterTestUtils.and(
+                FilterTestUtils.selector("col1", "val1"),
+                FilterTestUtils.selector("col2", "val2")
+            ),
+            FilterTestUtils.not(
+                FilterTestUtils.and(
+                    FilterTestUtils.selector("col4", "val4"),
+                    FilterTestUtils.selector("col5", "val5")
+                )
+            )
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.not(
+                FilterTestUtils.or(
+                    FilterTestUtils.selector("col2", "val2"),
+                    FilterTestUtils.selector("col4", "val4"),
+                    FilterTestUtils.selector("col5", "val5")
+                )
+            ),
+            FilterTestUtils.and(
+                FilterTestUtils.selector("col1", "val1"),
+                FilterTestUtils.selector("col3", "val3")
+            )
+        ),
+        FilterTestUtils.and(
+            FilterTestUtils.or(
+                FilterTestUtils.selector("col1", "val1"),
+                FilterTestUtils.selector("col2", "val22"), // selecting different value
+                FilterTestUtils.selector("col3", "val3")
+            ),
+            FilterTestUtils.not(
+                FilterTestUtils.selector("col1", "val11")
+            )
+        ),
+        FilterTestUtils.and(
+            FilterTestUtils.or(
+                FilterTestUtils.selector("col1", "val1"),
+                FilterTestUtils.selector("col2", "val22"),
+                FilterTestUtils.selector("col3", "val3")
+            ),
+            FilterTestUtils.not(
+                FilterTestUtils.selector("col1", "val11") // selecting different value
+            )
+        )
+    );
+    final Filter expected = FilterTestUtils.and(
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.selector("col2", "val22"),
+            FilterTestUtils.selector("col3", "val3")
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.not(FilterTestUtils.selector("col2", "val2"))
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.not(FilterTestUtils.selector("col2", "val2")),
+            FilterTestUtils.selector("col3", "val3")
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.not(FilterTestUtils.selector("col4", "val4"))
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col3", "val3"),
+            FilterTestUtils.not(FilterTestUtils.selector("col4", "val4"))
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.not(FilterTestUtils.selector("col5", "val5"))
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col3", "val3"),
+            FilterTestUtils.not(FilterTestUtils.selector("col5", "val5"))
+        ),
+        FilterTestUtils.not(FilterTestUtils.selector("col1", "val11")),
+        // The below OR filter could be eliminated because this filter also has
+        // (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)).
+        // The reduction process would be
+        // (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)) && (col1 = val1 || ~(col4 = val4) || ~(col5 = val5))
+        // => (col1 = val1 && ~(col4 = val4) || ~(col5 = val5)) && (col1 = val1 || ~(col4 = val4) || ~(col5 = val5))
+        // => (col1 = val1 && ~(col4 = val4) || ~(col5 = val5))
+        // => (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)).
+        // However, we don't have this reduction now, so we have a filter in a suboptimized CNF.
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col1", "val1"),
+            FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")),
+            FilterTestUtils.not(FilterTestUtils.selector("col5", "val5"))
+        ),
+        FilterTestUtils.or(
+            FilterTestUtils.selector("col2", "val2"),
+            FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")),
+            FilterTestUtils.not(FilterTestUtils.selector("col5", "val5"))
+        )
+    );
+    Assert.assertEquals(expected, Filters.toCNF(filter));
+  }
+
   private static BitmapIndex getBitmapIndex(final List<ImmutableBitmap> bitmapList)
   {
     return new BitmapIndex()
diff --git a/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java b/processing/src/test/java/org/apache/druid/segment/filter/NotFilterEvaluateTest.java
similarity index 95%
copy from processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java
copy to processing/src/test/java/org/apache/druid/segment/filter/NotFilterEvaluateTest.java
index f512d70..a2f073c 100644
--- a/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/NotFilterEvaluateTest.java
@@ -44,7 +44,7 @@ import java.util.List;
 import java.util.Map;
 
 @RunWith(Parameterized.class)
-public class NotFilterTest extends BaseFilterTest
+public class NotFilterEvaluateTest extends BaseFilterTest
 {
   private static final String TIMESTAMP_COLUMN = "timestamp";
 
@@ -64,7 +64,7 @@ public class NotFilterTest extends BaseFilterTest
       PARSER.parseBatch(ImmutableMap.of("dim0", "5")).get(0)
   );
 
-  public NotFilterTest(
+  public NotFilterEvaluateTest(
       String testName,
       IndexBuilder indexBuilder,
       Function<IndexBuilder, Pair<StorageAdapter, Closeable>> finisher,
@@ -78,7 +78,7 @@ public class NotFilterTest extends BaseFilterTest
   @AfterClass
   public static void tearDown() throws Exception
   {
-    BaseFilterTest.tearDown(NotFilterTest.class.getName());
+    BaseFilterTest.tearDown(NotFilterEvaluateTest.class.getName());
   }
 
   @Test
diff --git a/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java b/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java
index f512d70..d3f34e1 100644
--- a/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/filter/NotFilterTest.java
@@ -19,86 +19,24 @@
 
 package org.apache.druid.segment.filter;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import org.apache.druid.data.input.InputRow;
-import org.apache.druid.data.input.impl.DimensionsSpec;
-import org.apache.druid.data.input.impl.InputRowParser;
-import org.apache.druid.data.input.impl.MapInputRowParser;
-import org.apache.druid.data.input.impl.TimeAndDimsParseSpec;
-import org.apache.druid.data.input.impl.TimestampSpec;
-import org.apache.druid.java.util.common.DateTimes;
-import org.apache.druid.java.util.common.Pair;
-import org.apache.druid.query.filter.NotDimFilter;
-import org.apache.druid.query.filter.SelectorDimFilter;
-import org.apache.druid.segment.IndexBuilder;
-import org.apache.druid.segment.StorageAdapter;
-import org.junit.AfterClass;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.apache.druid.query.filter.Filter;
+import org.junit.Assert;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-import java.io.Closeable;
-import java.util.List;
-import java.util.Map;
-
-@RunWith(Parameterized.class)
-public class NotFilterTest extends BaseFilterTest
+public class NotFilterTest
 {
-  private static final String TIMESTAMP_COLUMN = "timestamp";
-
-  private static final InputRowParser<Map<String, Object>> PARSER = new MapInputRowParser(
-      new TimeAndDimsParseSpec(
-          new TimestampSpec(TIMESTAMP_COLUMN, "iso", DateTimes.of("2000")),
-          new DimensionsSpec(null, null, null)
-      )
-  );
-
-  private static final List<InputRow> ROWS = ImmutableList.of(
-      PARSER.parseBatch(ImmutableMap.of("dim0", "0")).get(0),
-      PARSER.parseBatch(ImmutableMap.of("dim0", "1")).get(0),
-      PARSER.parseBatch(ImmutableMap.of("dim0", "2")).get(0),
-      PARSER.parseBatch(ImmutableMap.of("dim0", "3")).get(0),
-      PARSER.parseBatch(ImmutableMap.of("dim0", "4")).get(0),
-      PARSER.parseBatch(ImmutableMap.of("dim0", "5")).get(0)
-  );
-
-  public NotFilterTest(
-      String testName,
-      IndexBuilder indexBuilder,
-      Function<IndexBuilder, Pair<StorageAdapter, Closeable>> finisher,
-      boolean cnf,
-      boolean optimize
-  )
-  {
-    super(testName, ROWS, indexBuilder, finisher, cnf, optimize);
-  }
-
-  @AfterClass
-  public static void tearDown() throws Exception
+  @Test
+  public void testEquals()
   {
-    BaseFilterTest.tearDown(NotFilterTest.class.getName());
+    EqualsVerifier.forClass(NotFilter.class).usingGetClass().withNonnullFields("baseFilter").verify();
   }
 
   @Test
-  public void testNotSelector()
+  public void testHashCodeCompareWithBaseFilter()
   {
-    assertFilterMatches(
-        new NotDimFilter(new SelectorDimFilter("dim0", null, null)),
-        ImmutableList.of("0", "1", "2", "3", "4", "5")
-    );
-    assertFilterMatches(
-        new NotDimFilter(new SelectorDimFilter("dim0", "", null)),
-        ImmutableList.of("0", "1", "2", "3", "4", "5")
-    );
-    assertFilterMatches(
-        new NotDimFilter(new SelectorDimFilter("dim0", "0", null)),
-        ImmutableList.of("1", "2", "3", "4", "5")
-    );
-    assertFilterMatches(
-        new NotDimFilter(new SelectorDimFilter("dim0", "1", null)),
-        ImmutableList.of("0", "2", "3", "4", "5")
-    );
+    final Filter baseFilter = FilterTestUtils.selector("col1", "1");
+    final Filter notFilter = FilterTestUtils.not(baseFilter);
+    Assert.assertNotEquals(notFilter.hashCode(), baseFilter.hashCode());
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org