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

cwylie 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 36e38b319b add virtual column support to search query (#12720)
36e38b319b is described below

commit 36e38b319b9dfe5314932e6b938c01ecddfc7456
Author: Clint Wylie <[email protected]>
AuthorDate: Mon Jul 4 21:58:10 2022 -0700

    add virtual column support to search query (#12720)
---
 .../main/java/org/apache/druid/query/Druids.java   | 16 +++++++
 .../apache/druid/query/search/AutoStrategy.java    |  3 +-
 .../druid/query/search/CursorOnlyStrategy.java     |  3 +-
 .../org/apache/druid/query/search/SearchQuery.java | 55 ++++++++++++----------
 .../druid/query/search/SearchQueryRunner.java      | 14 ++----
 .../druid/query/search/UseIndexesStrategy.java     | 36 ++++++++++----
 .../search/SearchQueryQueryToolChestTest.java      |  2 +
 .../druid/query/search/SearchQueryRunnerTest.java  | 40 ++++++++++++++++
 8 files changed, 121 insertions(+), 48 deletions(-)

diff --git a/processing/src/main/java/org/apache/druid/query/Druids.java 
b/processing/src/main/java/org/apache/druid/query/Druids.java
index a760d1745d..10dfde91b5 100644
--- a/processing/src/main/java/org/apache/druid/query/Druids.java
+++ b/processing/src/main/java/org/apache/druid/query/Druids.java
@@ -318,6 +318,7 @@ public class Druids
     private int limit;
     private QuerySegmentSpec querySegmentSpec;
     private List<DimensionSpec> dimensions;
+    private VirtualColumns virtualColumns;
     private SearchQuerySpec querySpec;
     private SearchSortSpec sortSpec;
     private Map<String, Object> context;
@@ -330,6 +331,7 @@ public class Druids
       limit = 0;
       querySegmentSpec = null;
       dimensions = null;
+      virtualColumns = null;
       querySpec = null;
       sortSpec = null;
       context = null;
@@ -344,6 +346,7 @@ public class Druids
           limit,
           querySegmentSpec,
           dimensions,
+          virtualColumns,
           querySpec,
           sortSpec,
           context
@@ -359,6 +362,7 @@ public class Druids
           .limit(query.getLimit())
           .intervals(query.getQuerySegmentSpec())
           .dimensions(query.getDimensions())
+          .virtualColumns(query.getVirtualColumns())
           .query(query.getQuery())
           .sortSpec(query.getSort())
           .context(query.getContext());
@@ -436,6 +440,18 @@ public class Druids
       return this;
     }
 
+    public SearchQueryBuilder virtualColumns(VirtualColumn... vc)
+    {
+      virtualColumns = VirtualColumns.create(Arrays.asList(vc));
+      return this;
+    }
+
+    public SearchQueryBuilder virtualColumns(VirtualColumns vc)
+    {
+      virtualColumns = vc;
+      return this;
+    }
+
     public SearchQueryBuilder dimensions(List<DimensionSpec> d)
     {
       dimensions = d;
diff --git 
a/processing/src/main/java/org/apache/druid/query/search/AutoStrategy.java 
b/processing/src/main/java/org/apache/druid/query/search/AutoStrategy.java
index 89d7d47e3f..36ddcc3ad9 100644
--- a/processing/src/main/java/org/apache/druid/query/search/AutoStrategy.java
+++ b/processing/src/main/java/org/apache/druid/query/search/AutoStrategy.java
@@ -27,7 +27,6 @@ import 
org.apache.druid.segment.ColumnSelectorColumnIndexSelector;
 import org.apache.druid.segment.DeprecatedQueryableIndexColumnSelector;
 import org.apache.druid.segment.QueryableIndex;
 import org.apache.druid.segment.Segment;
-import org.apache.druid.segment.VirtualColumns;
 import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.DictionaryEncodedStringValueIndex;
@@ -59,7 +58,7 @@ public class AutoStrategy extends SearchStrategy
       final ColumnSelector columnSelector = new 
DeprecatedQueryableIndexColumnSelector(index);
       final ColumnIndexSelector selector = new 
ColumnSelectorColumnIndexSelector(
           index.getBitmapFactoryForDimensions(),
-          VirtualColumns.EMPTY,
+          query.getVirtualColumns(),
           columnSelector
       );
 
diff --git 
a/processing/src/main/java/org/apache/druid/query/search/CursorOnlyStrategy.java
 
b/processing/src/main/java/org/apache/druid/query/search/CursorOnlyStrategy.java
index b4483fdd49..77325c5823 100644
--- 
a/processing/src/main/java/org/apache/druid/query/search/CursorOnlyStrategy.java
+++ 
b/processing/src/main/java/org/apache/druid/query/search/CursorOnlyStrategy.java
@@ -30,7 +30,6 @@ import org.apache.druid.segment.Cursor;
 import org.apache.druid.segment.DimensionHandlerUtils;
 import org.apache.druid.segment.Segment;
 import org.apache.druid.segment.StorageAdapter;
-import org.apache.druid.segment.VirtualColumns;
 import org.joda.time.Interval;
 
 import java.util.List;
@@ -90,7 +89,7 @@ public class CursorOnlyStrategy extends SearchStrategy
       final Sequence<Cursor> cursors = adapter.makeCursors(
           filter,
           interval,
-          VirtualColumns.EMPTY,
+          query.getVirtualColumns(),
           query.getGranularity(),
           query.isDescending(),
           null
diff --git 
a/processing/src/main/java/org/apache/druid/query/search/SearchQuery.java 
b/processing/src/main/java/org/apache/druid/query/search/SearchQuery.java
index c2c2fb99de..7424bbf1fb 100644
--- a/processing/src/main/java/org/apache/druid/query/search/SearchQuery.java
+++ b/processing/src/main/java/org/apache/druid/query/search/SearchQuery.java
@@ -20,6 +20,7 @@
 package org.apache.druid.query.search;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Preconditions;
 import org.apache.druid.java.util.common.granularity.Granularities;
@@ -33,9 +34,11 @@ import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.query.ordering.StringComparators;
 import org.apache.druid.query.spec.QuerySegmentSpec;
+import org.apache.druid.segment.VirtualColumns;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  */
@@ -46,6 +49,8 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
   private final DimFilter dimFilter;
   private final SearchSortSpec sortSpec;
   private final List<DimensionSpec> dimensions;
+
+  private final VirtualColumns virtualColumns;
   private final SearchQuerySpec querySpec;
   private final int limit;
 
@@ -57,6 +62,7 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
       @JsonProperty("limit") int limit,
       @JsonProperty("intervals") QuerySegmentSpec querySegmentSpec,
       @JsonProperty("searchDimensions") List<DimensionSpec> dimensions,
+      @JsonProperty("virtualColumns") VirtualColumns virtualColumns,
       @JsonProperty("query") SearchQuerySpec querySpec,
       @JsonProperty("sort") SearchSortSpec sortSpec,
       @JsonProperty("context") Map<String, Object> context
@@ -69,6 +75,7 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
     this.sortSpec = sortSpec == null ? DEFAULT_SORT_SPEC : sortSpec;
     this.limit = (limit == 0) ? 1000 : limit;
     this.dimensions = dimensions;
+    this.virtualColumns = VirtualColumns.nullToEmpty(virtualColumns);
     this.querySpec = querySpec == null ? new AllSearchQuerySpec() : querySpec;
   }
 
@@ -127,6 +134,14 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
     return dimensions;
   }
 
+  @JsonProperty
+  @Override
+  @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = 
VirtualColumns.JsonIncludeFilter.class)
+  public VirtualColumns getVirtualColumns()
+  {
+    return virtualColumns;
+  }
+
   @JsonProperty("query")
   public SearchQuerySpec getQuery()
   {
@@ -152,6 +167,7 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
            ", dimFilter=" + dimFilter +
            ", granularity='" + getGranularity() + '\'' +
            ", dimensions=" + dimensions +
+           ", virtualColumns=" + virtualColumns +
            ", querySpec=" + querySpec +
            ", querySegmentSpec=" + getQuerySegmentSpec() +
            ", limit=" + limit +
@@ -173,34 +189,25 @@ public class SearchQuery extends 
BaseQuery<Result<SearchResultValue>>
 
     SearchQuery that = (SearchQuery) o;
 
-    if (limit != that.limit) {
-      return false;
-    }
-    if (dimFilter != null ? !dimFilter.equals(that.dimFilter) : that.dimFilter 
!= null) {
-      return false;
-    }
-    if (dimensions != null ? !dimensions.equals(that.dimensions) : 
that.dimensions != null) {
-      return false;
-    }
-    if (querySpec != null ? !querySpec.equals(that.querySpec) : that.querySpec 
!= null) {
-      return false;
-    }
-    if (sortSpec != null ? !sortSpec.equals(that.sortSpec) : that.sortSpec != 
null) {
-      return false;
-    }
-
-    return true;
+    return limit == that.limit &&
+           Objects.equals(dimFilter, that.dimFilter) &&
+           Objects.equals(dimensions, that.dimensions) &&
+           Objects.equals(virtualColumns, that.virtualColumns) &&
+           Objects.equals(querySpec, that.querySpec) &&
+           Objects.equals(sortSpec, that.sortSpec);
   }
 
   @Override
   public int hashCode()
   {
-    int result = super.hashCode();
-    result = 31 * result + (dimFilter != null ? dimFilter.hashCode() : 0);
-    result = 31 * result + (sortSpec != null ? sortSpec.hashCode() : 0);
-    result = 31 * result + (dimensions != null ? dimensions.hashCode() : 0);
-    result = 31 * result + (querySpec != null ? querySpec.hashCode() : 0);
-    result = 31 * result + limit;
-    return result;
+    return Objects.hash(
+        super.hashCode(),
+        dimFilter,
+        sortSpec,
+        dimensions,
+        virtualColumns,
+        querySpec,
+        limit
+    );
   }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/query/search/SearchQueryRunner.java 
b/processing/src/main/java/org/apache/druid/query/search/SearchQueryRunner.java
index 7ba1e1ab54..776c115408 100644
--- 
a/processing/src/main/java/org/apache/druid/query/search/SearchQueryRunner.java
+++ 
b/processing/src/main/java/org/apache/druid/query/search/SearchQueryRunner.java
@@ -19,11 +19,9 @@
 
 package org.apache.druid.query.search;
 
-import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Object2IntRBTreeMap;
 import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.ISE;
@@ -234,14 +232,10 @@ public class SearchQueryRunner implements 
QueryRunner<Result<SearchResultValue>>
   )
   {
     Iterable<SearchHit> source = Iterables.transform(
-        retVal.object2IntEntrySet(), new 
Function<Object2IntMap.Entry<SearchHit>, SearchHit>()
-        {
-          @Override
-          public SearchHit apply(Object2IntMap.Entry<SearchHit> input)
-          {
-            SearchHit hit = input.getKey();
-            return new SearchHit(hit.getDimension(), hit.getValue(), 
input.getIntValue());
-          }
+        retVal.object2IntEntrySet(),
+        input -> {
+          SearchHit hit = input.getKey();
+          return new SearchHit(hit.getDimension(), hit.getValue(), 
input.getIntValue());
         }
     );
 
diff --git 
a/processing/src/main/java/org/apache/druid/query/search/UseIndexesStrategy.java
 
b/processing/src/main/java/org/apache/druid/query/search/UseIndexesStrategy.java
index a08ee56985..60d3b17ba6 100644
--- 
a/processing/src/main/java/org/apache/druid/query/search/UseIndexesStrategy.java
+++ 
b/processing/src/main/java/org/apache/druid/query/search/UseIndexesStrategy.java
@@ -45,6 +45,7 @@ import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.DictionaryEncodedStringValueIndex;
 import org.apache.druid.segment.column.NumericColumn;
+import org.apache.druid.segment.virtual.VirtualizedColumnInspector;
 import org.joda.time.Interval;
 
 import java.util.ArrayList;
@@ -77,14 +78,18 @@ public class UseIndexesStrategy extends SearchStrategy
 
     if (index != null) {
       // pair of bitmap dims and non-bitmap dims
-      final Pair<List<DimensionSpec>, List<DimensionSpec>> pair = 
partitionDimensionList(adapter, searchDims);
+      final Pair<List<DimensionSpec>, List<DimensionSpec>> pair = 
partitionDimensionList(
+          adapter,
+          query.getVirtualColumns(),
+          searchDims
+      );
       final List<DimensionSpec> bitmapSuppDims = pair.lhs;
       final List<DimensionSpec> nonBitmapSuppDims = pair.rhs;
 
       if (bitmapSuppDims.size() > 0) {
         final ColumnIndexSelector selector = new 
ColumnSelectorColumnIndexSelector(
             index.getBitmapFactoryForDimensions(),
-            VirtualColumns.EMPTY,
+            query.getVirtualColumns(),
             new DeprecatedQueryableIndexColumnSelector(index)
         );
 
@@ -94,7 +99,13 @@ public class UseIndexesStrategy extends SearchStrategy
         // the cursor-based plan. This can be more optimized. One possible 
optimization is generating a bitmap index
         // from the non-bitmap-support filter, and then use it to compute the 
filtered result by intersecting bitmaps.
         if (filter == null || filter.getBitmapColumnIndex(selector) != null) {
-          final ImmutableBitmap timeFilteredBitmap = 
makeTimeFilteredBitmap(index, segment, filter, interval);
+          final ImmutableBitmap timeFilteredBitmap = makeTimeFilteredBitmap(
+              index,
+              segment,
+              query.getVirtualColumns(),
+              filter,
+              interval
+          );
           builder.add(new IndexOnlyExecutor(query, segment, 
timeFilteredBitmap, bitmapSuppDims));
         } else {
           // Fall back to cursor-based execution strategy
@@ -118,6 +129,7 @@ public class UseIndexesStrategy extends SearchStrategy
    */
   private static Pair<List<DimensionSpec>, List<DimensionSpec>> 
partitionDimensionList(
       StorageAdapter adapter,
+      VirtualColumns virtualColumns,
       List<DimensionSpec> dimensions
   )
   {
@@ -127,9 +139,10 @@ public class UseIndexesStrategy extends SearchStrategy
         adapter.getAvailableDimensions(),
         dimensions
     );
+    VirtualizedColumnInspector columnInspector = new 
VirtualizedColumnInspector(adapter, virtualColumns);
 
     for (DimensionSpec spec : dimsToSearch) {
-      ColumnCapabilities capabilities = 
adapter.getColumnCapabilities(spec.getDimension());
+      ColumnCapabilities capabilities = 
columnInspector.getColumnCapabilities(spec.getDimension());
       if (capabilities == null) {
         continue;
       }
@@ -147,6 +160,7 @@ public class UseIndexesStrategy extends SearchStrategy
   static ImmutableBitmap makeTimeFilteredBitmap(
       final QueryableIndex index,
       final Segment segment,
+      final VirtualColumns virtualColumns,
       final Filter filter,
       final Interval interval
   )
@@ -158,7 +172,7 @@ public class UseIndexesStrategy extends SearchStrategy
     } else {
       final ColumnIndexSelector selector = new 
ColumnSelectorColumnIndexSelector(
           index.getBitmapFactoryForDimensions(),
-          VirtualColumns.EMPTY,
+          virtualColumns,
           new DeprecatedQueryableIndexColumnSelector(index)
       );
       final BitmapColumnIndex columnIndex = 
filter.getBitmapColumnIndex(selector);
@@ -249,18 +263,20 @@ public class UseIndexesStrategy extends SearchStrategy
       final QueryableIndex index = segment.asQueryableIndex();
       Preconditions.checkArgument(index != null, "Index should not be null");
 
+      ColumnSelectorColumnIndexSelector indexSelector = new 
ColumnSelectorColumnIndexSelector(
+          index.getBitmapFactoryForDimensions(),
+          query.getVirtualColumns(),
+          new DeprecatedQueryableIndexColumnSelector(index)
+      );
+
       final Object2IntRBTreeMap<SearchHit> retVal = new 
Object2IntRBTreeMap<>(query.getSort().getComparator());
       retVal.defaultReturnValue(0);
 
       final BitmapFactory bitmapFactory = 
index.getBitmapFactoryForDimensions();
 
       for (DimensionSpec dimension : dimsToSearch) {
-        final ColumnHolder columnHolder = 
index.getColumnHolder(dimension.getDimension());
-        if (columnHolder == null) {
-          continue;
-        }
 
-        final ColumnIndexSupplier indexSupplier = 
columnHolder.getIndexSupplier();
+        final ColumnIndexSupplier indexSupplier = 
indexSelector.getIndexSupplier(dimension.getDimension());
 
         ExtractionFn extractionFn = dimension.getExtractionFn();
         if (extractionFn == null) {
diff --git 
a/processing/src/test/java/org/apache/druid/query/search/SearchQueryQueryToolChestTest.java
 
b/processing/src/test/java/org/apache/druid/query/search/SearchQueryQueryToolChestTest.java
index add20d3956..8ef0ff3548 100644
--- 
a/processing/src/test/java/org/apache/druid/query/search/SearchQueryQueryToolChestTest.java
+++ 
b/processing/src/test/java/org/apache/druid/query/search/SearchQueryQueryToolChestTest.java
@@ -30,6 +30,7 @@ import org.apache.druid.query.Druids;
 import org.apache.druid.query.Result;
 import org.apache.druid.query.TableDataSource;
 import org.apache.druid.query.spec.MultipleIntervalSegmentSpec;
+import org.apache.druid.segment.VirtualColumns;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -48,6 +49,7 @@ public class SearchQueryQueryToolChestTest
                 1,
                 new 
MultipleIntervalSegmentSpec(ImmutableList.of(Intervals.of("2015-01-01/2015-01-02"))),
                 ImmutableList.of(Druids.DIMENSION_IDENTITY.apply("dim1")),
+                VirtualColumns.EMPTY,
                 new FragmentSearchQuerySpec(ImmutableList.of("a", "b")),
                 null,
                 null
diff --git 
a/processing/src/test/java/org/apache/druid/query/search/SearchQueryRunnerTest.java
 
b/processing/src/test/java/org/apache/druid/query/search/SearchQueryRunnerTest.java
index f204a8fe2b..1ec7d41967 100644
--- 
a/processing/src/test/java/org/apache/druid/query/search/SearchQueryRunnerTest.java
+++ 
b/processing/src/test/java/org/apache/druid/query/search/SearchQueryRunnerTest.java
@@ -21,6 +21,7 @@ package org.apache.druid.query.search;
 
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import org.apache.druid.common.config.NullHandling;
 import org.apache.druid.data.input.MapBasedInputRow;
 import org.apache.druid.java.util.common.DateTimes;
@@ -59,6 +60,7 @@ import org.apache.druid.segment.column.ColumnType;
 import org.apache.druid.segment.incremental.IncrementalIndex;
 import org.apache.druid.segment.incremental.IncrementalIndexSchema;
 import org.apache.druid.segment.incremental.OnheapIncrementalIndex;
+import org.apache.druid.segment.virtual.ListFilteredVirtualColumn;
 import org.apache.druid.testing.InitializedNullHandlingTest;
 import org.apache.druid.timeline.SegmentId;
 import org.junit.Assert;
@@ -784,6 +786,44 @@ public class SearchQueryRunnerTest extends 
InitializedNullHandlingTest
     checkSearchQuery(searchQuery, noHit);
   }
 
+  @Test
+  public void testSearchSameValueInMultiDimsVirtualColumns()
+  {
+    SearchQuery searchQuery = Druids.newSearchQueryBuilder()
+                                    
.dataSource(QueryRunnerTestHelper.DATA_SOURCE)
+                                    
.granularity(QueryRunnerTestHelper.ALL_GRAN)
+                                    
.intervals(QueryRunnerTestHelper.FULL_ON_INTERVAL_SPEC)
+                                    .dimensions(
+                                        Arrays.asList(
+                                            "v0",
+                                            "v1"
+                                        )
+                                    )
+                                    .virtualColumns(
+                                        new ListFilteredVirtualColumn(
+                                            "v0",
+                                            
DefaultDimensionSpec.of(QueryRunnerTestHelper.PLACEMENT_DIMENSION),
+                                            ImmutableSet.of("preferred"),
+                                            true
+                                        ),
+                                        new ListFilteredVirtualColumn(
+                                            "v1",
+                                            
DefaultDimensionSpec.of(QueryRunnerTestHelper.PLACEMENTISH_DIMENSION),
+                                            ImmutableSet.of("e"),
+                                            true
+                                        )
+                                    )
+                                    .query("e")
+                                    .build();
+
+    List<SearchHit> expectedHits = new ArrayList<>();
+    // same results as testSearchSameValueInMultiDims except v1 is missing a 
'preferred' since is filtered to just e
+    expectedHits.add(new SearchHit("v0", "preferred", 1209));
+    expectedHits.add(new SearchHit("v1", "e", 93));
+
+    checkSearchQuery(searchQuery, expectedHits);
+  }
+
   private void checkSearchQuery(Query searchQuery, List<SearchHit> 
expectedResults)
   {
     checkSearchQuery(searchQuery, runner, expectedResults);


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

Reply via email to