From 1c6fb63ee3dd34b3bf4d3247fa1ee5a7ce62e131 Mon Sep 17 00:00:00 2001 From: Balachandar Kesavan Date: Mon, 20 Jun 2016 11:13:49 -0700 Subject: [PATCH 01/10] Adding filters for TimeBoundary on backend Signed-off-by: Balachandar Kesavan --- .../src/main/java/io/druid/query/Druids.java | 9 +++ .../query/timeboundary/TimeBoundaryQuery.java | 22 ++++-- .../TimeBoundaryQueryRunnerFactory.java | 69 ++++++++++++++++--- .../io/druid/query/QueryRunnerTestHelper.java | 1 + .../TimeBoundaryQueryQueryToolChestTest.java | 4 ++ .../TimeBoundaryQueryRunnerTest.java | 30 +++++++- 6 files changed, 121 insertions(+), 14 deletions(-) diff --git a/processing/src/main/java/io/druid/query/Druids.java b/processing/src/main/java/io/druid/query/Druids.java index 1963592e80e3..cd3a7c597b66 100644 --- a/processing/src/main/java/io/druid/query/Druids.java +++ b/processing/src/main/java/io/druid/query/Druids.java @@ -775,6 +775,7 @@ public static class TimeBoundaryQueryBuilder private DataSource dataSource; private QuerySegmentSpec querySegmentSpec; private String bound; + private DimFilter dimFilter; private Map context; public TimeBoundaryQueryBuilder() @@ -782,6 +783,7 @@ public TimeBoundaryQueryBuilder() dataSource = null; querySegmentSpec = null; bound = null; + dimFilter = null; context = null; } @@ -791,6 +793,7 @@ public TimeBoundaryQuery build() dataSource, querySegmentSpec, bound, + dimFilter, context ); } @@ -840,6 +843,12 @@ public TimeBoundaryQueryBuilder bound(String b) return this; } + public TimeBoundaryQueryBuilder filters(String dimensionName, String value) + { + dimFilter = new SelectorDimFilter(dimensionName, value, null); + return this; + } + public TimeBoundaryQueryBuilder context(Map c) { context = c; diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java index 92770e69fb90..f80a56262044 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java @@ -31,6 +31,7 @@ import io.druid.query.Result; import io.druid.query.spec.MultipleIntervalSegmentSpec; import io.druid.query.spec.QuerySegmentSpec; +import io.druid.query.filter.DimFilter; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -52,6 +53,7 @@ public class TimeBoundaryQuery extends BaseQuery private static final byte CACHE_TYPE_ID = 0x0; + private final DimFilter dimFilter; private final String bound; @JsonCreator @@ -59,6 +61,7 @@ public TimeBoundaryQuery( @JsonProperty("dataSource") DataSource dataSource, @JsonProperty("intervals") QuerySegmentSpec querySegmentSpec, @JsonProperty("bound") String bound, + @JsonProperty("filter") DimFilter dimFilter, @JsonProperty("context") Map context ) { @@ -70,14 +73,12 @@ public TimeBoundaryQuery( context ); + this.dimFilter = dimFilter; this.bound = bound == null ? "" : bound; } @Override - public boolean hasFilters() - { - return false; - } + public boolean hasFilters() { return dimFilter != null; } @Override public String getType() @@ -85,6 +86,12 @@ public String getType() return Query.TIME_BOUNDARY; } + @JsonProperty("filter") + public DimFilter getDimensionsFilter() + { + return dimFilter; + } + @JsonProperty public String getBound() { @@ -98,6 +105,7 @@ public TimeBoundaryQuery withOverriddenContext(Map contextOverri getDataSource(), getQuerySegmentSpec(), bound, + dimFilter, computeOverridenContext(contextOverrides) ); } @@ -109,6 +117,7 @@ public TimeBoundaryQuery withQuerySegmentSpec(QuerySegmentSpec spec) getDataSource(), spec, bound, + dimFilter, getContext() ); } @@ -120,6 +129,7 @@ public Query> withDataSource(DataSource dataSour dataSource, getQuerySegmentSpec(), bound, + dimFilter, getContext() ); } @@ -211,6 +221,7 @@ public String toString() ", querySegmentSpec=" + getQuerySegmentSpec() + ", duration=" + getDuration() + ", bound=" + bound + + ", dimFilter=" + dimFilter.toString() + '}'; } @@ -233,6 +244,8 @@ public boolean equals(Object o) return false; } + if (dimFilter != null ? !dimFilter.equals(that.dimFilter) : that.dimFilter != null) return false; + return true; } @@ -241,6 +254,7 @@ public int hashCode() { int result = super.hashCode(); result = 31 * result + bound.hashCode(); + result = 31 * result + (dimFilter != null ? dimFilter.hashCode() : 0); return result; } } diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java index e7c9cde7ad02..c07e2934da8b 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java @@ -20,9 +20,12 @@ package io.druid.query.timeboundary; import com.google.inject.Inject; +import com.google.common.base.Function; +import com.google.common.collect.Lists; import com.metamx.common.ISE; import com.metamx.common.guava.BaseSequence; import com.metamx.common.guava.Sequence; +import com.metamx.common.guava.Sequences; import io.druid.query.ChainedExecutionQueryRunner; import io.druid.query.Query; import io.druid.query.QueryRunner; @@ -30,14 +33,22 @@ import io.druid.query.QueryToolChest; import io.druid.query.QueryWatcher; import io.druid.query.Result; +import io.druid.granularity.AllGranularity; import io.druid.segment.Segment; import io.druid.segment.StorageAdapter; +import io.druid.segment.filter.Filters; +import io.druid.query.QueryRunnerHelper; +import io.druid.segment.Cursor; +import io.druid.segment.LongColumnSelector; +import io.druid.segment.column.Column; import org.joda.time.DateTime; import java.util.Iterator; import java.util.Map; +import java.util.List; import java.util.concurrent.ExecutorService; + /** */ public class TimeBoundaryQueryRunnerFactory @@ -104,14 +115,56 @@ public Iterator> make() "Null storage adapter found. Probably trying to issue a query against a segment being memory unmapped." ); } - - final DateTime minTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MAX_TIME) - ? null - : adapter.getMinTime(); - final DateTime maxTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MIN_TIME) - ? null - : adapter.getMaxTime(); - + final DateTime minTime; + final DateTime maxTime; + + Function> extractionFunction = new Function>() + { + @Override + public Result apply(Cursor cursor) + { + if (cursor.isDone()) { return null; } + final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); + DateTime timestamp = new DateTime(timestampColumnSelector.get()); + return new Result<>(adapter.getInterval().getStart(), timestamp); + } + }; + + if (legacyQuery.getDimensionsFilter() != null) { + /* Should make a function? The only difference between these two parts is the descending boolean. */ + final Sequence> minResultSequence = QueryRunnerHelper.makeCursorBasedQuery( + adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), + Filters.toFilter(legacyQuery.getDimensionsFilter()), + false, new AllGranularity(), extractionFunction + ); + List> minResultList = Sequences.toList(minResultSequence, Lists.>newArrayList()); + if (minResultList.size() > 0) { + minTime = minResultList.get(0).getValue(); + } + else { + minTime = null; + } + + final Sequence> maxResultSequence = QueryRunnerHelper.makeCursorBasedQuery( + adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), + Filters.toFilter(legacyQuery.getDimensionsFilter()), + true, new AllGranularity(), extractionFunction + ); + List> maxResultList = Sequences.toList(maxResultSequence, Lists.>newArrayList()); + if (maxResultList.size() > 0) { + maxTime = maxResultList.get(0).getValue(); + } + else { + maxTime = null; + } + } else { + minTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MAX_TIME) + ? null + : adapter.getMinTime(); + maxTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MIN_TIME) + ? null + : adapter.getMaxTime(); + } return legacyQuery.buildResult( adapter.getInterval().getStart(), diff --git a/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java b/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java index c3b4b3d02dcf..a384265b0adf 100644 --- a/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java +++ b/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java @@ -82,6 +82,7 @@ public void registerQuery(Query query, ListenableFuture future) public static final String segmentId = "testSegment"; public static final String dataSource = "testing"; + public static final String truncatedDataSource = "testing2"; public static final UnionDataSource unionDataSource = new UnionDataSource( Lists.transform( Lists.newArrayList(dataSource, dataSource, dataSource, dataSource), new Function() diff --git a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java index 947984457005..2ba41a228919 100644 --- a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java +++ b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java @@ -45,6 +45,7 @@ public class TimeBoundaryQueryQueryToolChestTest new TableDataSource("test"), null, null, + null, null ); @@ -52,6 +53,7 @@ public class TimeBoundaryQueryQueryToolChestTest new TableDataSource("test"), null, TimeBoundaryQuery.MAX_TIME, + null, null ); @@ -59,6 +61,7 @@ public class TimeBoundaryQueryQueryToolChestTest new TableDataSource("test"), null, TimeBoundaryQuery.MIN_TIME, + null, null ); @@ -180,6 +183,7 @@ public void testCacheStrategy() throws Exception ) ), null, + null, null ) ); diff --git a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java index 3d484ddfb747..0842d3bd54db 100644 --- a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java +++ b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; +import com.google.common.collect.Iterables; import com.metamx.common.guava.Sequences; import io.druid.query.Druids; import io.druid.query.QueryRunner; @@ -64,6 +65,31 @@ public TimeBoundaryQueryRunnerTest( this.runner = runner; } + @Test + @SuppressWarnings("unchecked") + public void testFilteredTimeBoundaryQuery() + { + TimeBoundaryQuery timeBoundaryQuery = Druids.newTimeBoundaryQueryBuilder() + .dataSource("testing") + .filters("quality", "automotive") + .build(); + HashMap context = new HashMap(); + Iterable> results = Sequences.toList( + runner.run(timeBoundaryQuery, context), + Lists.>newArrayList() + ); + + /* If not, then no rows matched the filter--what is expected behavior in that case? */ + Assert.assertTrue(Iterables.size(results) > 0); + + TimeBoundaryResultValue val = results.iterator().next().getValue(); + DateTime minTime = val.getMinTime(); + DateTime maxTime = val.getMaxTime(); + + Assert.assertEquals(new DateTime("2011-01-12T00:00:00.000Z"), minTime); + Assert.assertEquals(new DateTime("2011-04-15T00:00:00.000Z"), maxTime); + } + @Test @SuppressWarnings("unchecked") public void testTimeBoundary() @@ -152,7 +178,7 @@ public void testMergeResults() throws Exception ) ); - TimeBoundaryQuery query = new TimeBoundaryQuery(new TableDataSource("test"), null, null, null); + TimeBoundaryQuery query = new TimeBoundaryQuery(new TableDataSource("test"), null, null, null, null); Iterable> actual = query.mergeResults(results); Assert.assertTrue(actual.iterator().next().getValue().getMaxTime().equals(new DateTime("2012-02-01"))); @@ -163,7 +189,7 @@ public void testMergeResultsEmptyResults() throws Exception { List> results = Lists.newArrayList(); - TimeBoundaryQuery query = new TimeBoundaryQuery(new TableDataSource("test"), null, null, null); + TimeBoundaryQuery query = new TimeBoundaryQuery(new TableDataSource("test"), null, null, null, null); Iterable> actual = query.mergeResults(results); Assert.assertFalse(actual.iterator().hasNext()); From c2c32928bc8de8b90a9e78896a791251d090edcf Mon Sep 17 00:00:00 2001 From: Balachandar Kesavan Date: Mon, 20 Jun 2016 12:39:59 -0700 Subject: [PATCH 02/10] updating TimeBoundaryQuery constructor in QueryHostFinderTest --- .../test/java/io/druid/server/router/QueryHostFinderTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/io/druid/server/router/QueryHostFinderTest.java b/server/src/test/java/io/druid/server/router/QueryHostFinderTest.java index 5237b4c20f2e..3963530d4bce 100644 --- a/server/src/test/java/io/druid/server/router/QueryHostFinderTest.java +++ b/server/src/test/java/io/druid/server/router/QueryHostFinderTest.java @@ -127,6 +127,7 @@ public void testFindServer() throws Exception new TableDataSource("test"), new MultipleIntervalSegmentSpec(Arrays.asList(new Interval("2011-08-31/2011-09-01"))), null, + null, null ) ); From 3fc2ea0f083b1afcc129ed585741a15c43397425 Mon Sep 17 00:00:00 2001 From: Balachandar Kesavan Date: Mon, 20 Jun 2016 15:04:22 -0700 Subject: [PATCH 03/10] add filter helpers --- processing/src/main/java/io/druid/query/Druids.java | 13 +++++++++++++ .../druid/query/timeboundary/TimeBoundaryQuery.java | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/processing/src/main/java/io/druid/query/Druids.java b/processing/src/main/java/io/druid/query/Druids.java index cd3a7c597b66..856dea4606fd 100644 --- a/processing/src/main/java/io/druid/query/Druids.java +++ b/processing/src/main/java/io/druid/query/Druids.java @@ -804,6 +804,7 @@ public TimeBoundaryQueryBuilder copy(TimeBoundaryQueryBuilder builder) .dataSource(builder.dataSource) .intervals(builder.querySegmentSpec) .bound(builder.bound) + .filters(builder.dimFilter) .context(builder.context); } @@ -849,6 +850,18 @@ public TimeBoundaryQueryBuilder filters(String dimensionName, String value) return this; } + public TimeBoundaryQueryBuilder filters(String dimensionName, String value, String... values) + { + dimFilter = new InDimFilter(dimensionName, Lists.asList(value, values), null); + return this; + } + + public TimeBoundaryQueryBuilder filters(DimFilter f) + { + dimFilter = f; + return this; + } + public TimeBoundaryQueryBuilder context(Map c) { context = c; diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java index f80a56262044..2feca52e0849 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java @@ -221,7 +221,7 @@ public String toString() ", querySegmentSpec=" + getQuerySegmentSpec() + ", duration=" + getDuration() + ", bound=" + bound + - ", dimFilter=" + dimFilter.toString() + + ", dimFilter=" + dimFilter + '}'; } From 7ea155fba3bf4a82573246395929bcbfc7094040 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 28 Jun 2016 12:23:12 -0700 Subject: [PATCH 04/10] update filterSegments + test --- .../TimeBoundaryQueryQueryToolChest.java | 26 ---- .../TimeBoundaryQueryRunnerFactory.java | 67 ++++------ .../io/druid/query/QueryRunnerTestHelper.java | 1 - .../TimeBoundaryQueryQueryToolChestTest.java | 85 +++--------- .../TimeBoundaryQueryRunnerTest.java | 126 +++++++++++++++++- 5 files changed, 166 insertions(+), 139 deletions(-) diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java index 3c74eabf24cd..d243fe11ae26 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java @@ -58,32 +58,6 @@ public class TimeBoundaryQueryQueryToolChest { }; - @Override - public List filterSegments(TimeBoundaryQuery query, List segments) - { - if (segments.size() <= 1) { - return segments; - } - - final T min = query.isMaxTime() ? null : segments.get(0); - final T max = query.isMinTime() ? null : segments.get(segments.size() - 1); - - return Lists.newArrayList( - Iterables.filter( - segments, - new Predicate() - { - @Override - public boolean apply(T input) - { - return (min != null && input.getInterval().overlaps(min.getInterval())) || - (max != null && input.getInterval().overlaps(max.getInterval())); - } - } - ) - ); - } - @Override public QueryRunner> mergeResults( final QueryRunner> runner diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java index c07e2934da8b..4855fd4170c4 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java @@ -92,6 +92,33 @@ public TimeBoundaryQueryRunner(Segment segment) this.adapter = segment.asStorageAdapter(); } + private Function> skipToFirstMatching = new Function>() + { + @Override + public Result apply(Cursor cursor) + { + if (cursor.isDone()) { return null; } + final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); + DateTime timestamp = new DateTime(timestampColumnSelector.get()); + return new Result<>(adapter.getInterval().getStart(), timestamp); + } + }; + + private DateTime getTimeBoundary(StorageAdapter adapter, TimeBoundaryQuery legacyQuery, boolean descending) + { + final Sequence> resultSequence = QueryRunnerHelper.makeCursorBasedQuery( + adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), + Filters.toFilter(legacyQuery.getDimensionsFilter()), + descending, new AllGranularity(), skipToFirstMatching + ); + List> resultList = Sequences.toList(resultSequence, Lists.>newArrayList()); + if (resultList.size() > 0) { + return resultList.get(0).getValue(); + } + + return null; + } + @Override public Sequence> run( final Query> input, @@ -118,45 +145,9 @@ public Iterator> make() final DateTime minTime; final DateTime maxTime; - Function> extractionFunction = new Function>() - { - @Override - public Result apply(Cursor cursor) - { - if (cursor.isDone()) { return null; } - final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); - DateTime timestamp = new DateTime(timestampColumnSelector.get()); - return new Result<>(adapter.getInterval().getStart(), timestamp); - } - }; - if (legacyQuery.getDimensionsFilter() != null) { - /* Should make a function? The only difference between these two parts is the descending boolean. */ - final Sequence> minResultSequence = QueryRunnerHelper.makeCursorBasedQuery( - adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), - Filters.toFilter(legacyQuery.getDimensionsFilter()), - false, new AllGranularity(), extractionFunction - ); - List> minResultList = Sequences.toList(minResultSequence, Lists.>newArrayList()); - if (minResultList.size() > 0) { - minTime = minResultList.get(0).getValue(); - } - else { - minTime = null; - } - - final Sequence> maxResultSequence = QueryRunnerHelper.makeCursorBasedQuery( - adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), - Filters.toFilter(legacyQuery.getDimensionsFilter()), - true, new AllGranularity(), extractionFunction - ); - List> maxResultList = Sequences.toList(maxResultSequence, Lists.>newArrayList()); - if (maxResultList.size() > 0) { - maxTime = maxResultList.get(0).getValue(); - } - else { - maxTime = null; - } + minTime = getTimeBoundary(adapter, legacyQuery, false); + maxTime = getTimeBoundary(adapter, legacyQuery, true); } else { minTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MAX_TIME) ? null diff --git a/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java b/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java index a384265b0adf..c3b4b3d02dcf 100644 --- a/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java +++ b/processing/src/test/java/io/druid/query/QueryRunnerTestHelper.java @@ -82,7 +82,6 @@ public void registerQuery(Query query, ListenableFuture future) public static final String segmentId = "testSegment"; public static final String dataSource = "testing"; - public static final String truncatedDataSource = "testing2"; public static final UnionDataSource unionDataSource = new UnionDataSource( Lists.transform( Lists.newArrayList(dataSource, dataSource, dataSource, dataSource), new Function() diff --git a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java index 2ba41a228919..91b446e68a15 100644 --- a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java +++ b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java @@ -78,94 +78,41 @@ public Interval getInterval() }; } - @Test - public void testFilterSegments() throws Exception + public void arbitraryFilterSegmentsTest(TimeBoundaryQuery query) throws Exception { - List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( - TIME_BOUNDARY_QUERY, - Arrays.asList( - createLogicalSegment(new Interval("2013-01-01/P1D")), - createLogicalSegment(new Interval("2013-01-01T01/PT1H")), - createLogicalSegment(new Interval("2013-01-01T02/PT1H")), - createLogicalSegment(new Interval("2013-01-02/P1D")), - createLogicalSegment(new Interval("2013-01-03T01/PT1H")), - createLogicalSegment(new Interval("2013-01-03T02/PT1H")), - createLogicalSegment(new Interval("2013-01-03/P1D")) - ) - ); - - Assert.assertEquals(6, segments.size()); - - List expected = Arrays.asList( + List inputSegments = Arrays.asList( createLogicalSegment(new Interval("2013-01-01/P1D")), createLogicalSegment(new Interval("2013-01-01T01/PT1H")), createLogicalSegment(new Interval("2013-01-01T02/PT1H")), + createLogicalSegment(new Interval("2013-01-02/P1D")), createLogicalSegment(new Interval("2013-01-03T01/PT1H")), createLogicalSegment(new Interval("2013-01-03T02/PT1H")), createLogicalSegment(new Interval("2013-01-03/P1D")) ); + List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( + query, inputSegments + ); - for (int i = 0; i < segments.size(); i++) { - Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); - } + Assert.assertEquals(7, segments.size()); + Assert.assertEquals(inputSegments, segments); } @Test - public void testMaxTimeFilterSegments() throws Exception + public void testFilterSegments() throws Exception { - List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( - MAXTIME_BOUNDARY_QUERY, - Arrays.asList( - createLogicalSegment(new Interval("2013-01-01/P1D")), - createLogicalSegment(new Interval("2013-01-01T01/PT1H")), - createLogicalSegment(new Interval("2013-01-01T02/PT1H")), - createLogicalSegment(new Interval("2013-01-02/P1D")), - createLogicalSegment(new Interval("2013-01-03T01/PT1H")), - createLogicalSegment(new Interval("2013-01-03T02/PT1H")), - createLogicalSegment(new Interval("2013-01-03/P1D")) - ) - ); - - Assert.assertEquals(3, segments.size()); - - List expected = Arrays.asList( - createLogicalSegment(new Interval("2013-01-03T01/PT1H")), - createLogicalSegment(new Interval("2013-01-03T02/PT1H")), - createLogicalSegment(new Interval("2013-01-03/P1D")) - ); + arbitraryFilterSegmentsTest(TIME_BOUNDARY_QUERY); + } - for (int i = 0; i < segments.size(); i++) { - Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); - } + @Test + public void testMaxTimeFilterSegments() throws Exception + { + arbitraryFilterSegmentsTest(MAXTIME_BOUNDARY_QUERY); } @Test public void testMinTimeFilterSegments() throws Exception { - List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( - MINTIME_BOUNDARY_QUERY, - Arrays.asList( - createLogicalSegment(new Interval("2013-01-01/P1D")), - createLogicalSegment(new Interval("2013-01-01T01/PT1H")), - createLogicalSegment(new Interval("2013-01-01T02/PT1H")), - createLogicalSegment(new Interval("2013-01-02/P1D")), - createLogicalSegment(new Interval("2013-01-03T01/PT1H")), - createLogicalSegment(new Interval("2013-01-03T02/PT1H")), - createLogicalSegment(new Interval("2013-01-03/P1D")) - ) - ); - - Assert.assertEquals(3, segments.size()); - - List expected = Arrays.asList( - createLogicalSegment(new Interval("2013-01-01/P1D")), - createLogicalSegment(new Interval("2013-01-01T01/PT1H")), - createLogicalSegment(new Interval("2013-01-01T02/PT1H")) - ); - - for (int i = 0; i < segments.size(); i++) { - Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); - } + arbitraryFilterSegmentsTest(MINTIME_BOUNDARY_QUERY); } @Test diff --git a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java index 0842d3bd54db..59c24270af6c 100644 --- a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java +++ b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerTest.java @@ -23,13 +23,30 @@ import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.google.common.collect.Iterables; +import com.google.common.io.CharSource; import com.metamx.common.guava.Sequences; +import io.druid.granularity.QueryGranularities; import io.druid.query.Druids; import io.druid.query.QueryRunner; +import io.druid.query.QueryRunnerFactory; import io.druid.query.QueryRunnerTestHelper; import io.druid.query.Result; import io.druid.query.TableDataSource; +import io.druid.query.ordering.StringComparators; +import io.druid.segment.IncrementalIndexSegment; +import io.druid.segment.incremental.IncrementalIndex; +import io.druid.segment.incremental.IncrementalIndexSchema; +import io.druid.segment.incremental.OnheapIncrementalIndex; +import io.druid.segment.Segment; +import io.druid.segment.TestIndex; +import io.druid.timeline.DataSegment; +import io.druid.timeline.TimelineObjectHolder; +import io.druid.timeline.VersionedIntervalTimeline; +import io.druid.timeline.partition.NoneShardSpec; +import io.druid.timeline.partition.SingleElementPartitionChunk; import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,6 +74,12 @@ public static Iterable constructorFeeder() throws IOException } private final QueryRunner runner; + private static final QueryRunnerFactory factory = new TimeBoundaryQueryRunnerFactory( + QueryRunnerTestHelper.NOOP_QUERYWATCHER + ); + private static Segment segment0; + private static Segment segment1; + private static List segmentIdentifiers; public TimeBoundaryQueryRunnerTest( QueryRunner runner @@ -65,29 +88,121 @@ public TimeBoundaryQueryRunnerTest( this.runner = runner; } + // Adapted from MultiSegmentSelectQueryTest, with modifications to make filtering meaningful + public static final String[] V_0112 = { + "2011-01-12T01:00:00.000Z spot business preferred bpreferred 100.000000", + "2011-01-12T02:00:00.000Z spot entertainment preferred epreferred 100.000000", + "2011-01-13T00:00:00.000Z spot automotive preferred apreferred 100.000000", + "2011-01-13T01:00:00.000Z spot business preferred bpreferred 100.000000", + }; + public static final String[] V_0113 = { + "2011-01-14T00:00:00.000Z spot automotive preferred apreferred 94.874713", + "2011-01-14T02:00:00.000Z spot entertainment preferred epreferred 110.087299", + "2011-01-15T00:00:00.000Z spot automotive preferred apreferred 94.874713", + "2011-01-15T01:00:00.000Z spot business preferred bpreferred 103.629399", + "2011-01-16T00:00:00.000Z spot automotive preferred apreferred 94.874713", + "2011-01-16T01:00:00.000Z spot business preferred bpreferred 103.629399", + "2011-01-16T02:00:00.000Z spot entertainment preferred epreferred 110.087299", + "2011-01-17T01:00:00.000Z spot business preferred bpreferred 103.629399", + "2011-01-17T02:00:00.000Z spot entertainment preferred epreferred 110.087299", + }; + + private static IncrementalIndex newIndex(String minTimeStamp) + { + return newIndex(minTimeStamp, 10000); + } + + private static IncrementalIndex newIndex(String minTimeStamp, int maxRowCount) + { + final IncrementalIndexSchema schema = new IncrementalIndexSchema.Builder() + .withMinTimestamp(new DateTime(minTimeStamp).getMillis()) + .withQueryGranularity(QueryGranularities.HOUR) + .withMetrics(TestIndex.METRIC_AGGS) + .build(); + return new OnheapIncrementalIndex(schema, true, maxRowCount); + } + + private static String makeIdentifier(IncrementalIndex index, String version) + { + return makeIdentifier(index.getInterval(), version); + } + + private static String makeIdentifier(Interval interval, String version) + { + return DataSegment.makeDataSegmentIdentifier( + QueryRunnerTestHelper.dataSource, + interval.getStart(), + interval.getEnd(), + version, + new NoneShardSpec() + ); + } + + private QueryRunner getCustomRunner() throws IOException { + CharSource v_0112 = CharSource.wrap(StringUtils.join(V_0112, "\n")); + CharSource v_0113 = CharSource.wrap(StringUtils.join(V_0113, "\n")); + + IncrementalIndex index0 = TestIndex.loadIncrementalIndex(newIndex("2011-01-12T00:00:00.000Z"), v_0112); + IncrementalIndex index1 = TestIndex.loadIncrementalIndex(newIndex("2011-01-14T00:00:00.000Z"), v_0113); + + segment0 = new IncrementalIndexSegment(index0, makeIdentifier(index0, "v1")); + segment1 = new IncrementalIndexSegment(index1, makeIdentifier(index1, "v1")); + + VersionedIntervalTimeline timeline = new VersionedIntervalTimeline(StringComparators.LEXICOGRAPHIC); + timeline.add(index0.getInterval(), "v1", new SingleElementPartitionChunk(segment0)); + timeline.add(index1.getInterval(), "v1", new SingleElementPartitionChunk(segment1)); + + segmentIdentifiers = Lists.newArrayList(); + for (TimelineObjectHolder holder : timeline.lookup(new Interval("2011-01-12/2011-01-17"))) { + segmentIdentifiers.add(makeIdentifier(holder.getInterval(), holder.getVersion())); + } + + return QueryRunnerTestHelper.makeFilteringQueryRunner(timeline, factory); + } + @Test @SuppressWarnings("unchecked") - public void testFilteredTimeBoundaryQuery() + public void testFilteredTimeBoundaryQuery() throws IOException { + QueryRunner customRunner = getCustomRunner(); TimeBoundaryQuery timeBoundaryQuery = Druids.newTimeBoundaryQueryBuilder() .dataSource("testing") .filters("quality", "automotive") .build(); + Assert.assertTrue(timeBoundaryQuery.hasFilters()); HashMap context = new HashMap(); Iterable> results = Sequences.toList( - runner.run(timeBoundaryQuery, context), + customRunner.run(timeBoundaryQuery, context), Lists.>newArrayList() ); - /* If not, then no rows matched the filter--what is expected behavior in that case? */ Assert.assertTrue(Iterables.size(results) > 0); TimeBoundaryResultValue val = results.iterator().next().getValue(); DateTime minTime = val.getMinTime(); DateTime maxTime = val.getMaxTime(); - Assert.assertEquals(new DateTime("2011-01-12T00:00:00.000Z"), minTime); - Assert.assertEquals(new DateTime("2011-04-15T00:00:00.000Z"), maxTime); + Assert.assertEquals(new DateTime("2011-01-13T00:00:00.000Z"), minTime); + Assert.assertEquals(new DateTime("2011-01-16T00:00:00.000Z"), maxTime); + } + + @Test + @SuppressWarnings("unchecked") + public void testFilteredTimeBoundaryQueryNoMatches() throws IOException + { + QueryRunner customRunner = getCustomRunner(); + TimeBoundaryQuery timeBoundaryQuery = Druids.newTimeBoundaryQueryBuilder() + .dataSource("testing") + .filters("quality", "foobar") // foobar dimension does not exist + .build(); + Assert.assertTrue(timeBoundaryQuery.hasFilters()); + HashMap context = new HashMap(); + Iterable> results = Sequences.toList( + customRunner.run(timeBoundaryQuery, context), + Lists.>newArrayList() + ); + + Assert.assertTrue(Iterables.size(results) == 0); } @Test @@ -97,6 +212,7 @@ public void testTimeBoundary() TimeBoundaryQuery timeBoundaryQuery = Druids.newTimeBoundaryQueryBuilder() .dataSource("testing") .build(); + Assert.assertFalse(timeBoundaryQuery.hasFilters()); HashMap context = new HashMap(); Iterable> results = Sequences.toList( runner.run(timeBoundaryQuery, context), From 8d014e55ff8451e57da79f81a9b99d067d356ff7 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 28 Jun 2016 14:13:13 -0700 Subject: [PATCH 05/10] Conditional filterSegment depending on whether a filter exists --- .../TimeBoundaryQueryQueryToolChest.java | 26 +++++ .../TimeBoundaryQueryQueryToolChestTest.java | 106 +++++++++++++++--- 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java index d243fe11ae26..133f27754a00 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChest.java @@ -58,6 +58,32 @@ public class TimeBoundaryQueryQueryToolChest { }; + @Override + public List filterSegments(TimeBoundaryQuery query, List segments) + { + if (segments.size() <= 1 || query.hasFilters()) { + return segments; + } + + final T min = query.isMaxTime() ? null : segments.get(0); + final T max = query.isMinTime() ? null : segments.get(segments.size() - 1); + + return Lists.newArrayList( + Iterables.filter( + segments, + new Predicate() + { + @Override + public boolean apply(T input) + { + return (min != null && input.getInterval().overlaps(min.getInterval())) || + (max != null && input.getInterval().overlaps(max.getInterval())); + } + } + ) + ); + } + @Override public QueryRunner> mergeResults( final QueryRunner> runner diff --git a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java index 91b446e68a15..b221e22b9d99 100644 --- a/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java +++ b/processing/src/test/java/io/druid/query/timeboundary/TimeBoundaryQueryQueryToolChestTest.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.druid.jackson.DefaultObjectMapper; +import io.druid.query.Druids; import io.druid.query.CacheStrategy; import io.druid.query.Result; import io.druid.query.TableDataSource; @@ -65,6 +66,10 @@ public class TimeBoundaryQueryQueryToolChestTest null ); + private static final TimeBoundaryQuery FILTERED_BOUNDARY_QUERY = Druids.newTimeBoundaryQueryBuilder() + .dataSource("testing") + .filters("foo", "bar") + .build(); private static LogicalSegment createLogicalSegment(final Interval interval) { @@ -78,43 +83,114 @@ public Interval getInterval() }; } - public void arbitraryFilterSegmentsTest(TimeBoundaryQuery query) throws Exception + @Test + public void testFilterSegments() throws Exception { - List inputSegments = Arrays.asList( + List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( + TIME_BOUNDARY_QUERY, + Arrays.asList( + createLogicalSegment(new Interval("2013-01-01/P1D")), + createLogicalSegment(new Interval("2013-01-01T01/PT1H")), + createLogicalSegment(new Interval("2013-01-01T02/PT1H")), + createLogicalSegment(new Interval("2013-01-02/P1D")), + createLogicalSegment(new Interval("2013-01-03T01/PT1H")), + createLogicalSegment(new Interval("2013-01-03T02/PT1H")), + createLogicalSegment(new Interval("2013-01-03/P1D")) + ) + ); + + Assert.assertEquals(6, segments.size()); + + List expected = Arrays.asList( createLogicalSegment(new Interval("2013-01-01/P1D")), createLogicalSegment(new Interval("2013-01-01T01/PT1H")), createLogicalSegment(new Interval("2013-01-01T02/PT1H")), - createLogicalSegment(new Interval("2013-01-02/P1D")), createLogicalSegment(new Interval("2013-01-03T01/PT1H")), createLogicalSegment(new Interval("2013-01-03T02/PT1H")), createLogicalSegment(new Interval("2013-01-03/P1D")) ); - List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( - query, inputSegments - ); - Assert.assertEquals(7, segments.size()); - Assert.assertEquals(inputSegments, segments); + for (int i = 0; i < segments.size(); i++) { + Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); + } } @Test - public void testFilterSegments() throws Exception + public void testMaxTimeFilterSegments() throws Exception { - arbitraryFilterSegmentsTest(TIME_BOUNDARY_QUERY); + List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( + MAXTIME_BOUNDARY_QUERY, + Arrays.asList( + createLogicalSegment(new Interval("2013-01-01/P1D")), + createLogicalSegment(new Interval("2013-01-01T01/PT1H")), + createLogicalSegment(new Interval("2013-01-01T02/PT1H")), + createLogicalSegment(new Interval("2013-01-02/P1D")), + createLogicalSegment(new Interval("2013-01-03T01/PT1H")), + createLogicalSegment(new Interval("2013-01-03T02/PT1H")), + createLogicalSegment(new Interval("2013-01-03/P1D")) + ) + ); + + Assert.assertEquals(3, segments.size()); + + List expected = Arrays.asList( + createLogicalSegment(new Interval("2013-01-03T01/PT1H")), + createLogicalSegment(new Interval("2013-01-03T02/PT1H")), + createLogicalSegment(new Interval("2013-01-03/P1D")) + ); + + for (int i = 0; i < segments.size(); i++) { + Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); + } } @Test - public void testMaxTimeFilterSegments() throws Exception + public void testMinTimeFilterSegments() throws Exception { - arbitraryFilterSegmentsTest(MAXTIME_BOUNDARY_QUERY); + List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( + MINTIME_BOUNDARY_QUERY, + Arrays.asList( + createLogicalSegment(new Interval("2013-01-01/P1D")), + createLogicalSegment(new Interval("2013-01-01T01/PT1H")), + createLogicalSegment(new Interval("2013-01-01T02/PT1H")), + createLogicalSegment(new Interval("2013-01-02/P1D")), + createLogicalSegment(new Interval("2013-01-03T01/PT1H")), + createLogicalSegment(new Interval("2013-01-03T02/PT1H")), + createLogicalSegment(new Interval("2013-01-03/P1D")) + ) + ); + + Assert.assertEquals(3, segments.size()); + + List expected = Arrays.asList( + createLogicalSegment(new Interval("2013-01-01/P1D")), + createLogicalSegment(new Interval("2013-01-01T01/PT1H")), + createLogicalSegment(new Interval("2013-01-01T02/PT1H")) + ); + + for (int i = 0; i < segments.size(); i++) { + Assert.assertEquals(segments.get(i).getInterval(), expected.get(i).getInterval()); + } } @Test - public void testMinTimeFilterSegments() throws Exception + public void testFilteredFilterSegments() throws Exception { - arbitraryFilterSegmentsTest(MINTIME_BOUNDARY_QUERY); - } + List segments = new TimeBoundaryQueryQueryToolChest().filterSegments( + FILTERED_BOUNDARY_QUERY, + Arrays.asList( + createLogicalSegment(new Interval("2013-01-01/P1D")), + createLogicalSegment(new Interval("2013-01-01T01/PT1H")), + createLogicalSegment(new Interval("2013-01-01T02/PT1H")), + createLogicalSegment(new Interval("2013-01-02/P1D")), + createLogicalSegment(new Interval("2013-01-03T01/PT1H")), + createLogicalSegment(new Interval("2013-01-03T02/PT1H")), + createLogicalSegment(new Interval("2013-01-03/P1D")) + ) + ); + Assert.assertEquals(7, segments.size()); + } @Test public void testCacheStrategy() throws Exception { From 2889bb5127af812a8edc8e21be8c79a1479fbc7d Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 5 Jul 2016 11:17:59 -0700 Subject: [PATCH 06/10] Style changes --- .../TimeBoundaryQueryRunnerFactory.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java index 4855fd4170c4..9a94f5db79fc 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java @@ -92,14 +92,16 @@ public TimeBoundaryQueryRunner(Segment segment) this.adapter = segment.asStorageAdapter(); } - private Function> skipToFirstMatching = new Function>() + private final Function> skipToFirstMatching = new Function>() { @Override public Result apply(Cursor cursor) { - if (cursor.isDone()) { return null; } + if (cursor.isDone()) { + return null; + } final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); - DateTime timestamp = new DateTime(timestampColumnSelector.get()); + final DateTime timestamp = new DateTime(timestampColumnSelector.get()); return new Result<>(adapter.getInterval().getStart(), timestamp); } }; @@ -111,7 +113,10 @@ private DateTime getTimeBoundary(StorageAdapter adapter, TimeBoundaryQuery legac Filters.toFilter(legacyQuery.getDimensionsFilter()), descending, new AllGranularity(), skipToFirstMatching ); - List> resultList = Sequences.toList(resultSequence, Lists.>newArrayList()); + final List> resultList = Sequences.toList( + Sequences.limit(resultSequence, 1), + Lists.>newArrayList() + ); if (resultList.size() > 0) { return resultList.get(0).getValue(); } From ae0a7b82caa3eefbe9f744665d67aa88a3933dfa Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 5 Jul 2016 11:55:05 -0700 Subject: [PATCH 07/10] Trigger rebuild From 25910979afe6469e7a593ed6b01ce3a0b2039d16 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Thu, 7 Jul 2016 11:18:50 -0700 Subject: [PATCH 08/10] Adding documentation for timeboundaryquery filtering --- docs/content/querying/timeboundaryquery.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/content/querying/timeboundaryquery.md b/docs/content/querying/timeboundaryquery.md index 1549c94571be..1f857b86eb5b 100644 --- a/docs/content/querying/timeboundaryquery.md +++ b/docs/content/querying/timeboundaryquery.md @@ -9,6 +9,7 @@ Time boundary queries return the earliest and latest data points of a data set. "queryType" : "timeBoundary", "dataSource": "sample_datasource", "bound" : < "maxTime" | "minTime" > # optional, defaults to returning both timestamps if not set + "filter" : { "type": "and", "fields": [, , ...] } # optional } ``` @@ -19,6 +20,7 @@ There are 3 main parts to a time boundary query: |queryType|This String should always be "timeBoundary"; this is the first thing Druid looks at to figure out how to interpret the query|yes| |dataSource|A String or Object defining the data source to query, very similar to a table in a relational database. See [DataSource](../querying/datasource.html) for more information.|yes| |bound | Optional, set to `maxTime` or `minTime` to return only the latest or earliest timestamp. Default to returning both if not set| no | +|filter|See [Filters](../querying/filters.html)|no| |context|See [Context](../querying/query-context.html)|no| The format of the result is: From aca3e2c762e3f98f50179c95f38506a1cb385a67 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 2 Aug 2016 14:32:52 -0700 Subject: [PATCH 09/10] added filter serialization to timeboundaryquery cache --- .../java/io/druid/query/timeboundary/TimeBoundaryQuery.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java index 2feca52e0849..2ae63c89bbaf 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java @@ -136,10 +136,12 @@ public Query> withDataSource(DataSource dataSour public byte[] getCacheKey() { + final byte[] filterBytes = dimFilter == null ? new byte[]{} : dimFilter.getCacheKey(); final byte[] boundBytes = StringUtils.toUtf8(bound); return ByteBuffer.allocate(1 + boundBytes.length) .put(CACHE_TYPE_ID) .put(boundBytes) + .put(filterBytes) .array(); } From 6e42b418ffb5b7189467b92c7bbaa3de0374af57 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Fri, 5 Aug 2016 13:32:12 -0700 Subject: [PATCH 10/10] code style changes --- .../query/timeboundary/TimeBoundaryQuery.java | 12 ++++-- .../TimeBoundaryQueryRunnerFactory.java | 39 +++++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java index 2ae63c89bbaf..9f2071e94586 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQuery.java @@ -78,7 +78,9 @@ public TimeBoundaryQuery( } @Override - public boolean hasFilters() { return dimFilter != null; } + public boolean hasFilters() { + return dimFilter != null; + } @Override public String getType() @@ -138,9 +140,11 @@ public byte[] getCacheKey() { final byte[] filterBytes = dimFilter == null ? new byte[]{} : dimFilter.getCacheKey(); final byte[] boundBytes = StringUtils.toUtf8(bound); - return ByteBuffer.allocate(1 + boundBytes.length) + final byte delimiter = (byte) 0xff; + return ByteBuffer.allocate(2 + boundBytes.length + filterBytes.length) .put(CACHE_TYPE_ID) .put(boundBytes) + .put(delimiter) .put(filterBytes) .array(); } @@ -246,7 +250,9 @@ public boolean equals(Object o) return false; } - if (dimFilter != null ? !dimFilter.equals(that.dimFilter) : that.dimFilter != null) return false; + if (dimFilter != null ? !dimFilter.equals(that.dimFilter) : that.dimFilter != null) { + return false; + } return true; } diff --git a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java index 9a94f5db79fc..3bd6bac63643 100644 --- a/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java +++ b/processing/src/main/java/io/druid/query/timeboundary/TimeBoundaryQueryRunnerFactory.java @@ -86,32 +86,35 @@ public QueryToolChest, TimeBoundaryQuery> getToo private static class TimeBoundaryQueryRunner implements QueryRunner> { private final StorageAdapter adapter; + private final Function> skipToFirstMatching; public TimeBoundaryQueryRunner(Segment segment) { this.adapter = segment.asStorageAdapter(); - } - - private final Function> skipToFirstMatching = new Function>() - { - @Override - public Result apply(Cursor cursor) + this.skipToFirstMatching = new Function>() { - if (cursor.isDone()) { - return null; + @Override + public Result apply(Cursor cursor) + { + if (cursor.isDone()) { + return null; + } + final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); + final DateTime timestamp = new DateTime(timestampColumnSelector.get()); + return new Result<>(adapter.getInterval().getStart(), timestamp); } - final LongColumnSelector timestampColumnSelector = cursor.makeLongColumnSelector(Column.TIME_COLUMN_NAME); - final DateTime timestamp = new DateTime(timestampColumnSelector.get()); - return new Result<>(adapter.getInterval().getStart(), timestamp); - } - }; + }; + } private DateTime getTimeBoundary(StorageAdapter adapter, TimeBoundaryQuery legacyQuery, boolean descending) { final Sequence> resultSequence = QueryRunnerHelper.makeCursorBasedQuery( - adapter, legacyQuery.getQuerySegmentSpec().getIntervals(), + adapter, + legacyQuery.getQuerySegmentSpec().getIntervals(), Filters.toFilter(legacyQuery.getDimensionsFilter()), - descending, new AllGranularity(), skipToFirstMatching + descending, + new AllGranularity(), + this.skipToFirstMatching ); final List> resultList = Sequences.toList( Sequences.limit(resultSequence, 1), @@ -152,7 +155,11 @@ public Iterator> make() if (legacyQuery.getDimensionsFilter() != null) { minTime = getTimeBoundary(adapter, legacyQuery, false); - maxTime = getTimeBoundary(adapter, legacyQuery, true); + if (minTime == null) { + maxTime = null; + } else { + maxTime = getTimeBoundary(adapter, legacyQuery, true); + } } else { minTime = legacyQuery.getBound().equalsIgnoreCase(TimeBoundaryQuery.MAX_TIME) ? null