diff --git a/docs/content/querying/aggregations.md b/docs/content/querying/aggregations.md index 673961404473..ec2b64a46735 100644 --- a/docs/content/querying/aggregations.md +++ b/docs/content/querying/aggregations.md @@ -204,8 +204,6 @@ A filtered aggregator wraps any given aggregator, but only aggregates the values This makes it possible to compute the results of a filtered and an unfiltered aggregation simultaneously, without having to issue multiple queries, and use both results as part of post-aggregations. -*Limitations:* The filtered aggregator currently only supports 'or', 'and', 'selector', 'not' and 'Extraction' filters, i.e. matching one or multiple dimensions against a single value. - *Note:* If only the filtered results are required, consider putting the filter on the query itself, which will be much faster since it does not require scanning all the data. ```json diff --git a/processing/src/main/java/io/druid/query/aggregation/FilteredAggregatorFactory.java b/processing/src/main/java/io/druid/query/aggregation/FilteredAggregatorFactory.java index 8ee215677c30..98bd33b2bbc2 100644 --- a/processing/src/main/java/io/druid/query/aggregation/FilteredAggregatorFactory.java +++ b/processing/src/main/java/io/druid/query/aggregation/FilteredAggregatorFactory.java @@ -21,12 +21,19 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import io.druid.query.dimension.DefaultDimensionSpec; import io.druid.query.filter.DimFilter; import io.druid.query.filter.ValueMatcher; +import io.druid.query.filter.ValueMatcherFactory; import io.druid.segment.ColumnSelectorFactory; +import io.druid.segment.DimensionSelector; +import io.druid.segment.data.IndexedInts; +import io.druid.segment.filter.BooleanValueMatcher; import io.druid.segment.filter.Filters; import java.nio.ByteBuffer; +import java.util.BitSet; import java.util.Comparator; import java.util.List; @@ -52,18 +59,19 @@ public FilteredAggregatorFactory( @Override public Aggregator factorize(ColumnSelectorFactory columnSelectorFactory) { - final ValueMatcher valueMatcher = Filters.convertDimensionFilters(filter).makeMatcher(columnSelectorFactory); - return new FilteredAggregator( - valueMatcher, - delegate.factorize(columnSelectorFactory) - ); - + final ValueMatcherFactory valueMatcherFactory = new FilteredAggregatorValueMatcherFactory(columnSelectorFactory); + final ValueMatcher valueMatcher = Filters.convertDimensionFilters(filter).makeMatcher(valueMatcherFactory); + return new FilteredAggregator( + valueMatcher, + delegate.factorize(columnSelectorFactory) + ); } @Override public BufferAggregator factorizeBuffered(ColumnSelectorFactory columnSelectorFactory) { - final ValueMatcher valueMatcher = Filters.convertDimensionFilters(filter).makeMatcher(columnSelectorFactory); + final ValueMatcherFactory valueMatcherFactory = new FilteredAggregatorValueMatcherFactory(columnSelectorFactory); + final ValueMatcher valueMatcher = Filters.convertDimensionFilters(filter).makeMatcher(valueMatcherFactory); return new FilteredBufferAggregator( valueMatcher, delegate.factorizeBuffered(columnSelectorFactory) @@ -199,4 +207,85 @@ public int hashCode() result = 31 * result + (filter != null ? filter.hashCode() : 0); return result; } + + private static class FilteredAggregatorValueMatcherFactory implements ValueMatcherFactory + { + private final ColumnSelectorFactory columnSelectorFactory; + + public FilteredAggregatorValueMatcherFactory(ColumnSelectorFactory columnSelectorFactory) + { + this.columnSelectorFactory = columnSelectorFactory; + } + + @Override + public ValueMatcher makeValueMatcher(final String dimension, final Comparable value) + { + final DimensionSelector selector = columnSelectorFactory.makeDimensionSelector( + new DefaultDimensionSpec(dimension, dimension) + ); + + // Compare "value" as a String. + final String valueString = value == null ? null : value.toString(); + final boolean isNullOrEmpty = valueString == null || valueString.isEmpty(); + + // Missing columns match a null or empty string value, and don't match anything else. + if (selector == null) { + return new BooleanValueMatcher(isNullOrEmpty); + } + + final int valueId = selector.lookupId(valueString); + return new ValueMatcher() + { + @Override + public boolean matches() + { + final IndexedInts row = selector.getRow(); + final int size = row.size(); + for (int i = 0; i < size; ++i) { + if (row.get(i) == valueId) { + return true; + } + } + return false; + } + }; + } + + @Override + public ValueMatcher makeValueMatcher(final String dimension, final Predicate predicate) + { + final DimensionSelector selector = columnSelectorFactory.makeDimensionSelector( + new DefaultDimensionSpec(dimension, dimension) + ); + + if (selector == null) { + return new BooleanValueMatcher(predicate.apply(null)); + } + + // Check every value in the dimension, as a String. + final int cardinality = selector.getValueCardinality(); + final BitSet valueIds = new BitSet(cardinality); + for (int i = 0; i < cardinality; i++) { + if (predicate.apply(selector.lookupName(i))) { + valueIds.set(i); + } + } + + return new ValueMatcher() + { + @Override + public boolean matches() + { + final IndexedInts row = selector.getRow(); + final int size = row.size(); + for (int i = 0; i < size; ++i) { + if (valueIds.get(row.get(i))) { + return true; + } + } + return false; + } + }; + } + } } diff --git a/processing/src/main/java/io/druid/query/filter/Filter.java b/processing/src/main/java/io/druid/query/filter/Filter.java index 557418f2f151..e63f9b22667a 100644 --- a/processing/src/main/java/io/druid/query/filter/Filter.java +++ b/processing/src/main/java/io/druid/query/filter/Filter.java @@ -20,7 +20,6 @@ package io.druid.query.filter; import com.metamx.collections.bitmap.ImmutableBitmap; -import io.druid.segment.ColumnSelectorFactory; /** */ @@ -28,5 +27,4 @@ public interface Filter { public ImmutableBitmap getBitmapIndex(BitmapIndexSelector selector); public ValueMatcher makeMatcher(ValueMatcherFactory factory); - public ValueMatcher makeMatcher(ColumnSelectorFactory columnSelectorFactory); } diff --git a/processing/src/main/java/io/druid/query/filter/ValueMatcherFactory.java b/processing/src/main/java/io/druid/query/filter/ValueMatcherFactory.java index 0bfda5de72f6..3da61be99b1f 100644 --- a/processing/src/main/java/io/druid/query/filter/ValueMatcherFactory.java +++ b/processing/src/main/java/io/druid/query/filter/ValueMatcherFactory.java @@ -20,13 +20,11 @@ package io.druid.query.filter; import com.google.common.base.Predicate; -import com.metamx.collections.spatial.search.Bound; /** */ public interface ValueMatcherFactory { public ValueMatcher makeValueMatcher(String dimension, Comparable value); - public ValueMatcher makeValueMatcher(String dimension, Predicate value); - public ValueMatcher makeValueMatcher(String dimension, Bound bound); + public ValueMatcher makeValueMatcher(String dimension, Predicate predicate); } diff --git a/processing/src/main/java/io/druid/segment/filter/AndFilter.java b/processing/src/main/java/io/druid/segment/filter/AndFilter.java index d13bcc67521f..085f86d1c21e 100644 --- a/processing/src/main/java/io/druid/segment/filter/AndFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/AndFilter.java @@ -25,7 +25,6 @@ import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; import java.util.List; @@ -72,16 +71,6 @@ public ValueMatcher makeMatcher(ValueMatcherFactory factory) return makeMatcher(matchers); } - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - final ValueMatcher[] matchers = new ValueMatcher[filters.size()]; - - for (int i = 0; i < filters.size(); i++) { - matchers[i] = filters.get(i).makeMatcher(factory); - } - return makeMatcher(matchers); - } - private ValueMatcher makeMatcher(final ValueMatcher[] baseMatchers) { if (baseMatchers.length == 1) { diff --git a/processing/src/main/java/io/druid/segment/filter/DimensionPredicateFilter.java b/processing/src/main/java/io/druid/segment/filter/DimensionPredicateFilter.java index d578ef6227ad..72d48af16a38 100644 --- a/processing/src/main/java/io/druid/segment/filter/DimensionPredicateFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/DimensionPredicateFilter.java @@ -27,7 +27,6 @@ import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; import io.druid.segment.data.Indexed; import javax.annotation.Nullable; @@ -77,10 +76,4 @@ public ValueMatcher makeMatcher(ValueMatcherFactory factory) { return factory.makeValueMatcher(dimension, predicate); } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - throw new UnsupportedOperationException(); - } } diff --git a/processing/src/main/java/io/druid/segment/filter/ExtractionFilter.java b/processing/src/main/java/io/druid/segment/filter/ExtractionFilter.java index 7be2e97da449..144b56326b63 100644 --- a/processing/src/main/java/io/druid/segment/filter/ExtractionFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/ExtractionFilter.java @@ -23,18 +23,13 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.metamx.collections.bitmap.ImmutableBitmap; -import io.druid.query.dimension.DefaultDimensionSpec; import io.druid.query.extraction.ExtractionFn; import io.druid.query.filter.BitmapIndexSelector; import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; -import io.druid.segment.DimensionSelector; import io.druid.segment.data.Indexed; -import io.druid.segment.data.IndexedInts; -import java.util.BitSet; import java.util.Iterator; import java.util.List; @@ -121,38 +116,4 @@ public boolean apply(String input) } ); } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory columnSelectorFactory) - { - final DimensionSelector dimensionSelector = columnSelectorFactory.makeDimensionSelector( - new DefaultDimensionSpec(dimension, dimension) - ); - if (dimensionSelector == null) { - return new BooleanValueMatcher(value.equals(Strings.nullToEmpty(fn.apply(null)))); - } else { - final BitSet bitSetOfIds = new BitSet(dimensionSelector.getValueCardinality()); - for (int i = 0; i < dimensionSelector.getValueCardinality(); i++) { - if (value.equals(Strings.nullToEmpty(fn.apply(dimensionSelector.lookupName(i))))) { - bitSetOfIds.set(i); - } - } - return new ValueMatcher() - { - @Override - public boolean matches() - { - final IndexedInts row = dimensionSelector.getRow(); - final int size = row.size(); - for (int i = 0; i < size; ++i) { - if (bitSetOfIds.get(row.get(i))) { - return true; - } - } - return false; - } - }; - } - } - } diff --git a/processing/src/main/java/io/druid/segment/filter/JavaScriptFilter.java b/processing/src/main/java/io/druid/segment/filter/JavaScriptFilter.java index 4ea9c4d04aa4..0703bc71d13c 100644 --- a/processing/src/main/java/io/druid/segment/filter/JavaScriptFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/JavaScriptFilter.java @@ -27,7 +27,6 @@ import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; import io.druid.segment.data.Indexed; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; @@ -161,11 +160,4 @@ public int hashCode() return script.hashCode(); } } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - throw new UnsupportedOperationException(); - } - } diff --git a/processing/src/main/java/io/druid/segment/filter/NotFilter.java b/processing/src/main/java/io/druid/segment/filter/NotFilter.java index 3f3579bcd057..970e19eacb99 100644 --- a/processing/src/main/java/io/druid/segment/filter/NotFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/NotFilter.java @@ -24,7 +24,6 @@ import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; /** */ @@ -62,19 +61,4 @@ public boolean matches() } }; } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - final ValueMatcher baseMatcher = baseFilter.makeMatcher(factory); - - return new ValueMatcher() - { - @Override - public boolean matches() - { - return !baseMatcher.matches(); - } - }; - } } diff --git a/processing/src/main/java/io/druid/segment/filter/OrFilter.java b/processing/src/main/java/io/druid/segment/filter/OrFilter.java index 9611f0fc7f87..84a7aa6034df 100644 --- a/processing/src/main/java/io/druid/segment/filter/OrFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/OrFilter.java @@ -25,7 +25,6 @@ import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; import java.util.List; @@ -72,16 +71,6 @@ public ValueMatcher makeMatcher(ValueMatcherFactory factory) return makeMatcher(matchers); } - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - final ValueMatcher[] matchers = new ValueMatcher[filters.size()]; - - for (int i = 0; i < filters.size(); i++) { - matchers[i] = filters.get(i).makeMatcher(factory); - } - return makeMatcher(matchers); - } - private ValueMatcher makeMatcher(final ValueMatcher[] baseMatchers){ if (baseMatchers.length == 1) { return baseMatchers[0]; diff --git a/processing/src/main/java/io/druid/segment/filter/SelectorFilter.java b/processing/src/main/java/io/druid/segment/filter/SelectorFilter.java index 861eb35e245f..604dce9a45dd 100644 --- a/processing/src/main/java/io/druid/segment/filter/SelectorFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/SelectorFilter.java @@ -19,16 +19,11 @@ package io.druid.segment.filter; -import com.google.common.base.Strings; import com.metamx.collections.bitmap.ImmutableBitmap; -import io.druid.query.dimension.DefaultDimensionSpec; import io.druid.query.filter.BitmapIndexSelector; import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; -import io.druid.segment.DimensionSelector; -import io.druid.segment.data.IndexedInts; /** */ @@ -57,36 +52,4 @@ public ValueMatcher makeMatcher(ValueMatcherFactory factory) { return factory.makeValueMatcher(dimension, value); } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory columnSelectorFactory) - { - final DimensionSelector dimensionSelector = columnSelectorFactory.makeDimensionSelector( - new DefaultDimensionSpec(dimension, dimension) - ); - - // Missing columns match a null or empty string value and don't match anything else - if (dimensionSelector == null) { - return new BooleanValueMatcher(Strings.isNullOrEmpty(value)); - } else { - final int valueId = dimensionSelector.lookupId(value); - return new ValueMatcher() - { - @Override - public boolean matches() - { - final IndexedInts row = dimensionSelector.getRow(); - final int size = row.size(); - for (int i = 0; i < size; ++i) { - if (row.get(i) == valueId) { - return true; - } - } - return false; - } - }; - } - } - - } diff --git a/processing/src/main/java/io/druid/segment/filter/SpatialFilter.java b/processing/src/main/java/io/druid/segment/filter/SpatialFilter.java index 2ca221560d0f..d9bc9fe2cf5f 100644 --- a/processing/src/main/java/io/druid/segment/filter/SpatialFilter.java +++ b/processing/src/main/java/io/druid/segment/filter/SpatialFilter.java @@ -18,13 +18,17 @@ */ package io.druid.segment.filter; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; import com.metamx.collections.bitmap.ImmutableBitmap; import com.metamx.collections.spatial.search.Bound; import io.druid.query.filter.BitmapIndexSelector; import io.druid.query.filter.Filter; import io.druid.query.filter.ValueMatcher; import io.druid.query.filter.ValueMatcherFactory; -import io.druid.segment.ColumnSelectorFactory; +import io.druid.segment.incremental.SpatialDimensionRowTransformer; + +import java.util.Arrays; /** */ @@ -38,8 +42,8 @@ public SpatialFilter( Bound bound ) { - this.dimension = dimension; - this.bound = bound; + this.dimension = Preconditions.checkNotNull(dimension, "dimension"); + this.bound = Preconditions.checkNotNull(bound, "bound"); } @Override @@ -54,14 +58,19 @@ public ValueMatcher makeMatcher(ValueMatcherFactory factory) { return factory.makeValueMatcher( dimension, - bound + new Predicate() + { + @Override + public boolean apply(Object input) + { + if (input instanceof String) { + final float[] coordinate = SpatialDimensionRowTransformer.decode((String) input); + return bound.contains(coordinate); + } else { + return false; + } + } + } ); } - - @Override - public ValueMatcher makeMatcher(ColumnSelectorFactory factory) - { - throw new UnsupportedOperationException(); - } - } diff --git a/processing/src/main/java/io/druid/segment/incremental/IncrementalIndexStorageAdapter.java b/processing/src/main/java/io/druid/segment/incremental/IncrementalIndexStorageAdapter.java index 3e736a625a29..56a0e103dd00 100644 --- a/processing/src/main/java/io/druid/segment/incremental/IncrementalIndexStorageAdapter.java +++ b/processing/src/main/java/io/druid/segment/incremental/IncrementalIndexStorageAdapter.java @@ -731,46 +731,6 @@ public boolean matches() } }; } - - @Override - public ValueMatcher makeValueMatcher(final String dimension, final Bound bound) - { - IncrementalIndex.DimensionDesc dimensionDesc = index.getDimension(dimension); - if (dimensionDesc == null) { - return new BooleanValueMatcher(false); - } - final int dimIndex = dimensionDesc.getIndex(); - final IncrementalIndex.DimDim dimDim = dimensionDesc.getValues(); - - return new ValueMatcher() - { - @Override - public boolean matches() - { - int[][] dims = holder.getKey().getDims(); - if (dimIndex >= dims.length || dims[dimIndex] == null) { - return false; - } - - for (int dimVal : dims[dimIndex]) { - Comparable fullDimVal = dimDim.getValue(dimVal); - // TODO: decide what to do for non-String spatial dims, skip for now - if (!(fullDimVal instanceof String)) { - return false; - } - List stringCoords = Lists.newArrayList(SPLITTER.split((String) fullDimVal)); - float[] coords = new float[stringCoords.size()]; - for (int j = 0; j < coords.length; j++) { - coords[j] = Float.valueOf(stringCoords.get(j)); - } - if (bound.contains(coords)) { - return true; - } - } - return false; - } - }; - } } @Override diff --git a/processing/src/main/java/io/druid/segment/incremental/SpatialDimensionRowTransformer.java b/processing/src/main/java/io/druid/segment/incremental/SpatialDimensionRowTransformer.java index fc03a738a8dc..c42823ca61de 100644 --- a/processing/src/main/java/io/druid/segment/incremental/SpatialDimensionRowTransformer.java +++ b/processing/src/main/java/io/druid/segment/incremental/SpatialDimensionRowTransformer.java @@ -24,6 +24,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -224,11 +225,41 @@ private boolean isJoinedSpatialDimValValid(String dimVal) return true; } - private static Float tryParseFloat(String val) { + private static Float tryParseFloat(String val) + { try { return Float.parseFloat(val); - } catch (NullPointerException | NumberFormatException e) { + } + catch (NullPointerException | NumberFormatException e) { + return null; + } + } + + /** + * Decodes encodedCoordinate. + * + * @param encodedCoordinate encoded coordinate + * + * @return decoded coordinate, or null if it could not be decoded + */ + public static float[] decode(final String encodedCoordinate) + { + if (encodedCoordinate == null) { return null; } + + final ImmutableList parts = ImmutableList.copyOf(SPLITTER.split(encodedCoordinate)); + final float[] coordinate = new float[parts.size()]; + + for (int i = 0; i < coordinate.length; i++) { + final Float floatPart = tryParseFloat(parts.get(i)); + if (floatPart == null) { + return null; + } else { + coordinate[i] = floatPart; + } + } + + return coordinate; } } diff --git a/processing/src/test/java/io/druid/segment/filter/SpatialFilterBonusTest.java b/processing/src/test/java/io/druid/segment/filter/SpatialFilterBonusTest.java index 4fe65daedcaa..b855bc6dcf9e 100644 --- a/processing/src/test/java/io/druid/segment/filter/SpatialFilterBonusTest.java +++ b/processing/src/test/java/io/druid/segment/filter/SpatialFilterBonusTest.java @@ -22,6 +22,7 @@ import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.metamx.collections.spatial.search.RadiusBound; import com.metamx.collections.spatial.search.RectangularBound; import io.druid.data.input.MapBasedInputRow; @@ -35,6 +36,7 @@ import io.druid.query.Result; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.aggregation.CountAggregatorFactory; +import io.druid.query.aggregation.FilteredAggregatorFactory; import io.druid.query.aggregation.LongSumAggregatorFactory; import io.druid.query.filter.SpatialDimFilter; import io.druid.query.timeseries.TimeseriesQuery; @@ -66,6 +68,7 @@ import java.util.HashMap; import java.util.List; import java.util.Random; +import java.util.Set; /** */ @@ -205,9 +208,21 @@ private static IncrementalIndex makeIncrementalIndex() throws IOException ) ); - // Add a bunch of random points + // Add a bunch of random points, without replacement + Set alreadyChosen = Sets.newHashSet(); Random rand = new Random(); for (int i = 6; i < NUM_POINTS; i++) { + String coord = null; + while (coord == null) { + coord = String.format( + "%s,%s", + (float) (rand.nextFloat() * 10 + 10.0), + (float) (rand.nextFloat() * 10 + 10.0) + ); + if (!alreadyChosen.add(coord)) { + coord = null; + } + } theIndex.add( new MapBasedInputRow( new DateTime("2013-01-01").getMillis(), @@ -215,11 +230,7 @@ private static IncrementalIndex makeIncrementalIndex() throws IOException ImmutableMap.of( "timestamp", new DateTime("2013-01-01").toString(), "dim", "boo", - "dim.geo", String.format( - "%s,%s", - (float) (rand.nextFloat() * 10 + 10.0), - (float) (rand.nextFloat() * 10 + 10.0) - ), + "dim.geo", coord, "val", i ) ) @@ -389,10 +400,10 @@ private static QueryableIndex makeMergedQueryableIndex(final IndexSpec indexSpec "timestamp", new DateTime("2013-01-01").toString(), "dim", "boo", "dim.geo", String.format( - "%s,%s", - (float) (rand.nextFloat() * 10 + 10.0), - (float) (rand.nextFloat() * 10 + 10.0) - ), + "%s,%s", + (float) (rand.nextFloat() * 10 + 10.0), + (float) (rand.nextFloat() * 10 + 10.0) + ), "val", i ) ) @@ -467,9 +478,9 @@ public void testSpatialQuery() new DateTime("2013-01-01T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 3L) - .put("val", 59L) - .build() + .put("rows", 3L) + .put("val", 59L) + .build() ) ) ); @@ -485,7 +496,7 @@ public void testSpatialQuery() factory.createRunner(segment), factory.getToolchest() ); - HashMap context = new HashMap(); + HashMap context = new HashMap(); TestHelper.assertExpectedResults(expectedResults, runner.run(query, context)); } catch (Exception e) { @@ -519,45 +530,139 @@ public void testSpatialQueryMorePoints() new DateTime("2013-01-01T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 1L) - .put("val", 17L) - .build() + .put("rows", 1L) + .put("val", 17L) + .build() ) ), new Result( new DateTime("2013-01-02T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 1L) - .put("val", 29L) - .build() + .put("rows", 1L) + .put("val", 29L) + .build() ) ), new Result( new DateTime("2013-01-03T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 1L) - .put("val", 13L) - .build() + .put("rows", 1L) + .put("val", 13L) + .build() ) ), new Result( new DateTime("2013-01-04T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 1L) - .put("val", 91L) - .build() + .put("rows", 1L) + .put("val", 91L) + .build() ) ), new Result( new DateTime("2013-01-05T00:00:00.000Z"), new TimeseriesResultValue( ImmutableMap.builder() - .put("rows", 1L) - .put("val", 47L) - .build() + .put("rows", 1L) + .put("val", 47L) + .build() + ) + ) + ); + try { + TimeseriesQueryRunnerFactory factory = new TimeseriesQueryRunnerFactory( + new TimeseriesQueryQueryToolChest( + QueryRunnerTestHelper.NoopIntervalChunkingQueryRunnerDecorator()), + new TimeseriesQueryEngine(), + QueryRunnerTestHelper.NOOP_QUERYWATCHER + ); + + QueryRunner runner = new FinalizeResultsQueryRunner( + factory.createRunner(segment), + factory.getToolchest() + ); + HashMap context = new HashMap(); + TestHelper.assertExpectedResults(expectedResults, runner.run(query, context)); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Test + public void testSpatialQueryFilteredAggregator() + { + TimeseriesQuery query = Druids.newTimeseriesQueryBuilder() + .dataSource("test") + .granularity(QueryGranularity.DAY) + .intervals(Arrays.asList(new Interval("2013-01-01/2013-01-07"))) + .aggregators( + Arrays.asList( + new CountAggregatorFactory("rows"), + new FilteredAggregatorFactory( + new LongSumAggregatorFactory("valFiltered", "val"), + new SpatialDimFilter( + "dim.geo", + new RectangularBound(new float[]{0.0f, 0.0f}, new float[]{9.0f, 9.0f}) + ) + ), + new LongSumAggregatorFactory("val", "val") + ) + ) + .build(); + + List> expectedResults = Arrays.asList( + new Result<>( + new DateTime("2013-01-01T00:00:00.000Z"), + new TimeseriesResultValue( + ImmutableMap.builder() + .put("rows", 4995L) + .put("val", 12497502L) + .put("valFiltered", 17L) + .build() + ) + ), + new Result<>( + new DateTime("2013-01-02T00:00:00.000Z"), + new TimeseriesResultValue( + ImmutableMap.builder() + .put("rows", 1L) + .put("val", 29L) + .put("valFiltered", 29L) + .build() + ) + ), + new Result<>( + new DateTime("2013-01-03T00:00:00.000Z"), + new TimeseriesResultValue( + ImmutableMap.builder() + .put("rows", 1L) + .put("val", 13L) + .put("valFiltered", 13L) + .build() + ) + ), + new Result<>( + new DateTime("2013-01-04T00:00:00.000Z"), + new TimeseriesResultValue( + ImmutableMap.builder() + .put("rows", 1L) + .put("val", 91L) + .put("valFiltered", 91L) + .build() + ) + ), + new Result<>( + new DateTime("2013-01-05T00:00:00.000Z"), + new TimeseriesResultValue( + ImmutableMap.builder() + .put("rows", 2L) + .put("val", 548L) + .put("valFiltered", 47L) + .build() ) ) ); @@ -573,7 +678,7 @@ public void testSpatialQueryMorePoints() factory.createRunner(segment), factory.getToolchest() ); - HashMap context = new HashMap(); + HashMap context = new HashMap(); TestHelper.assertExpectedResults(expectedResults, runner.run(query, context)); } catch (Exception e) {