Repository: calcite Updated Branches: refs/heads/master be78de942 -> 159bcab20
[CALCITE-2077] Druid adapter: Use 'scan' query rather than 'select' query (Nishant Bangarwa) Close apache/calcite#577 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/159bcab2 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/159bcab2 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/159bcab2 Branch: refs/heads/master Commit: 159bcab20d2024b938cdc52ca9152631279d63df Parents: be78de9 Author: Nishant Bangarwa <[email protected]> Authored: Thu Dec 21 12:16:10 2017 -0800 Committer: Jesus Camacho Rodriguez <[email protected]> Committed: Thu Dec 21 12:16:18 2017 -0800 ---------------------------------------------------------------------- .../adapter/druid/DruidConnectionImpl.java | 57 ++++++++-- .../calcite/adapter/druid/DruidQuery.java | 33 +++++- .../apache/calcite/adapter/druid/QueryType.java | 3 +- .../org/apache/calcite/test/DruidAdapterIT.java | 104 +++++++++---------- 4 files changed, 130 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/159bcab2/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java index 3c52fce..4f65dff 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java @@ -37,7 +37,6 @@ import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; - import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -249,6 +248,39 @@ class DruidConnectionImpl implements DruidConnection { expect(parser, JsonToken.END_OBJECT); } } + break; + + case SCAN: + if (parser.nextToken() == JsonToken.START_ARRAY) { + while (parser.nextToken() == JsonToken.START_OBJECT) { + expectScalarField(parser, "segmentId"); + + expect(parser, JsonToken.FIELD_NAME); + if (parser.getCurrentName().equals("columns")) { + expect(parser, JsonToken.START_ARRAY); + while (parser.nextToken() != JsonToken.END_ARRAY) { + // Skip the columns list + } + } + if (parser.nextToken() == JsonToken.FIELD_NAME + && parser.getCurrentName().equals("events") + && parser.nextToken() == JsonToken.START_ARRAY) { + // Events is Array of Arrays where each array is a row + while (parser.nextToken() == JsonToken.START_ARRAY) { + for (String field : fieldNames) { + parseFieldForName(fieldNames, fieldTypes, posTimestampField, rowBuilder, parser, + field); + } + expect(parser, JsonToken.END_ARRAY); + Row row = rowBuilder.build(); + sink.send(row); + rowBuilder.reset(); + page.totalRowCount += 1; + } + } + expect(parser, JsonToken.END_OBJECT); + } + } } } catch (IOException | InterruptedException e) { throw new RuntimeException(e); @@ -270,7 +302,12 @@ class DruidConnectionImpl implements DruidConnection { private void parseField(List<String> fieldNames, List<ColumnMetaData.Rep> fieldTypes, int posTimestampField, Row.RowBuilder rowBuilder, JsonParser parser) throws IOException { final String fieldName = parser.getCurrentName(); + parseFieldForName(fieldNames, fieldTypes, posTimestampField, rowBuilder, parser, fieldName); + } + private void parseFieldForName(List<String> fieldNames, List<ColumnMetaData.Rep> fieldTypes, + int posTimestampField, Row.RowBuilder rowBuilder, JsonParser parser, String fieldName) + throws IOException { // Move to next token, which is name's value JsonToken token = parser.nextToken(); @@ -288,13 +325,18 @@ class DruidConnectionImpl implements DruidConnection { if (isTimestampColumn || ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP == type) { try { - final Date parse; - // synchronized block to avoid race condition - synchronized (UTC_TIMESTAMP_FORMAT) { - parse = UTC_TIMESTAMP_FORMAT.parse(parser.getText()); + final long timeInMillis; + + if (token == JsonToken.VALUE_NUMBER_INT) { + timeInMillis = parser.getLongValue(); + } else { + // synchronized block to avoid race condition + synchronized (UTC_TIMESTAMP_FORMAT) { + timeInMillis = UTC_TIMESTAMP_FORMAT.parse(parser.getText()).getTime(); + } } if (posTimestampField != -1) { - rowBuilder.set(posTimestampField, parse.getTime()); + rowBuilder.set(posTimestampField, timeInMillis); } } catch (ParseException e) { // ignore bad value @@ -423,7 +465,7 @@ class DruidConnectionImpl implements DruidConnection { } expect(parser, JsonToken.START_OBJECT); while (parser.nextToken() != JsonToken.END_OBJECT) { - // empty + // empty } } @@ -653,7 +695,6 @@ class DruidConnectionImpl implements DruidConnection { } } - /** Result of a "segmentMetadata" call, populated by Jackson. */ @SuppressWarnings({ "WeakerAccess", "unused" }) private static class JsonSegmentMetadata { http://git-wip-us.apache.org/repos/asf/calcite/blob/159bcab2/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java index 01cf440..db7d4ff 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java @@ -68,9 +68,11 @@ import org.apache.calcite.util.Util; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.joda.time.Interval; @@ -340,7 +342,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { } else if (rel instanceof Filter) { pw.item("filter", ((Filter) rel).getCondition()); } else if (rel instanceof Project) { - if (((Project) rel).getInput() instanceof Aggregate) { + if (((Project) rel).getInput() instanceof Aggregate) { pw.item("post_projects", ((Project) rel).getProjects()); } else { pw.item("projects", ((Project) rel).getProjects()); @@ -468,7 +470,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { final Sort sort = (Sort) rels.get(i++); collationIndexes = new ArrayList<>(); collationDirections = new ArrayList<>(); - for (RelFieldCollation fCol: sort.collation.getFieldCollations()) { + for (RelFieldCollation fCol : sort.collation.getFieldCollations()) { collationIndexes.add(fCol.getFieldIndex()); collationDirections.add(fCol.getDirection()); if (sort.getRowType().getFieldList().get(fCol.getFieldIndex()).getType().getFamily() @@ -505,7 +507,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { List<Integer> collationIndexes, List<Direction> collationDirections, ImmutableBitSet numericCollationIndexes, Integer fetch, Project postProject) { final CalciteConnectionConfig config = getConnectionConfig(); - QueryType queryType = QueryType.SELECT; + QueryType queryType = QueryType.SCAN; final Translator translator = new Translator(druidTable, rowType, config.timeZone()); List<String> fieldNames = rowType.getFieldNames(); Set<String> usedFieldNames = Sets.newHashSet(fieldNames); @@ -657,7 +659,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { // all check has been done to ensure all RexCall rexNode can be pushed in. if (rex instanceof RexCall) { DruidQuery.JsonPostAggregation jsonPost = getJsonPostAggregation(fieldName, rex, - postProject.getInput()); + postProject.getInput()); postAggs.add(jsonPost); } } @@ -801,6 +803,29 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { generator.writeEndObject(); break; + case SCAN: + generator.writeStartObject(); + + generator.writeStringField("queryType", "scan"); + generator.writeStringField("dataSource", druidTable.dataSource); + writeField(generator, "intervals", intervals); + writeFieldIf(generator, "filter", jsonFilter); + writeField(generator, "columns", + Lists.transform(fieldNames, new Function<String, String>() { + @Override public String apply(String s) { + return s.equals(druidTable.timestampFieldName) + ? DruidTable.DEFAULT_TIMESTAMP_COLUMN : s; + } + })); + generator.writeStringField("granularity", finalGranularity.value); + generator.writeStringField("resultFormat", "compactedList"); + if (fetch != null) { + generator.writeNumberField("limit", fetch); + } + + generator.writeEndObject(); + break; + default: throw new AssertionError("unknown query type " + queryType); } http://git-wip-us.apache.org/repos/asf/calcite/blob/159bcab2/druid/src/main/java/org/apache/calcite/adapter/druid/QueryType.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/QueryType.java b/druid/src/main/java/org/apache/calcite/adapter/druid/QueryType.java index a460e6a..a262eb9 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/QueryType.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/QueryType.java @@ -21,7 +21,8 @@ public enum QueryType { SELECT("select"), TOP_N("topN"), GROUP_BY("groupBy"), - TIMESERIES("timeseries"); + TIMESERIES("timeseries"), + SCAN("scan"); private final String queryName; http://git-wip-us.apache.org/repos/asf/calcite/blob/159bcab2/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java ---------------------------------------------------------------------- diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java index cc33100..913e30c 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java @@ -240,11 +240,12 @@ public class DruidAdapterIT { + "limit 1\n"; final String explain = "DruidQuery(table=[[wiki, wikiticker]], intervals=[[1900-01-01T00:00:00.000Z/3000-01-01T00:00:00.000Z]], projects=[[$0]], fetch=[1])\n"; - final String druidQuery = "{'queryType':'select'," - + "'dataSource':'wikiticker','descending':false," + final String druidQuery = "{'queryType':'scan'," + + "'dataSource':'wikiticker'," + "'intervals':['1900-01-01T00:00:00.000Z/3000-01-01T00:00:00.000Z']," - + "'dimensions':[],'metrics':[],'granularity':'all','pagingSpec':{'threshold':1,'fromNext':true}," - + "'context':{'druid.query.fetch':true}}"; + + "'columns':['__time'],'granularity':'all'," + + "'resultFormat':'compactedList','limit':1}"; + sql(sql, WIKI_AUTO2) .returnsUnordered("__time=2015-09-12 00:46:58") .explainContains(explain) @@ -340,12 +341,11 @@ public class DruidAdapterIT { final String explain = "\n DruidQuery(table=[[wiki, wikiticker]], " + "intervals=[[1900-01-01T00:00:00.000Z/2015-10-12T00:00:00.000Z]], " + "projects=[[$0]])\n"; - final String druidQuery = "{'queryType':'select'," - + "'dataSource':'wikiticker','descending':false," + final String druidQuery = "{'queryType':'scan'," + + "'dataSource':'wikiticker'," + "'intervals':['1900-01-01T00:00:00.000Z/2015-10-12T00:00:00.000Z']," - + "'dimensions':[],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true}," - + "'context':{'druid.query.fetch':false}}"; + + "'columns':['__time'],'granularity':'all'," + + "'resultFormat':'compactedList'"; sql(sql, WIKI_AUTO2) .limit(2) .returnsUnordered("__time=2015-09-12 00:46:58", @@ -564,10 +564,10 @@ public class DruidAdapterIT { final String sql = "select \"state_province\", \"product_name\"\n" + "from \"foodmart\"\n" + "offset 2 fetch next 3 rows only"; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," - + "'dimensions':['state_province','product_name'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + + "'columns':['state_province','product_name'],'granularity':'all'," + + "'resultFormat':'compactedList'}"; sql(sql) .runs() .queryContains(druidChecker(druidQuery)); @@ -576,10 +576,10 @@ public class DruidAdapterIT { @Test public void testLimit() { final String sql = "select \"gender\", \"state_province\"\n" + "from \"foodmart\" fetch next 3 rows only"; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," - + "'dimensions':['gender','state_province'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':3,'fromNext':true},'context':{'druid.query.fetch':true}}"; + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + + "'columns':['gender','state_province'],'granularity':'all'," + + "'resultFormat':'compactedList','limit':3"; sql(sql) .runs() .queryContains(druidChecker(druidQuery)); @@ -782,13 +782,13 @@ public class DruidAdapterIT { final String sql = "select \"product_name\" from \"foodmart\"\n" + "where \"product_id\" BETWEEN '1500' AND '1502'\n" + "order by \"state_province\" desc, \"product_id\""; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + "'filter':{'type':'and','fields':[" + "{'type':'bound','dimension':'product_id','lower':'1500','lowerStrict':false,'ordering':'lexicographic'}," + "{'type':'bound','dimension':'product_id','upper':'1502','upperStrict':false,'ordering':'lexicographic'}]}," - + "'dimensions':['product_name','state_province','product_id'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + + "'columns':['product_name','state_province','product_id'],'granularity':'all'," + + "'resultFormat':'compactedList'"; sql(sql) .limit(4) .returns( @@ -815,13 +815,13 @@ public class DruidAdapterIT { final String sql = "select \"product_name\" from \"foodmart\"\n" + "where \"product_id\" BETWEEN 1500 AND 1502\n" + "order by \"state_province\" desc, \"product_id\""; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + "'filter':{'type':'and','fields':[" + "{'type':'bound','dimension':'product_id','lower':'1500','lowerStrict':false,'ordering':'numeric'}," + "{'type':'bound','dimension':'product_id','upper':'1502','upperStrict':false,'ordering':'numeric'}]}," - + "'dimensions':['product_name','state_province','product_id'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + + "'columns':['product_name','state_province','product_id'],'granularity':'all'," + + "'resultFormat':'compactedList'"; sql(sql) .limit(4) .returns( @@ -847,11 +847,11 @@ public class DruidAdapterIT { @Test public void testFilterOutEverything() { final String sql = "select \"product_name\" from \"foodmart\"\n" + "where \"product_id\" = -1"; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + "'filter':{'type':'selector','dimension':'product_id','value':'-1'}," - + "'dimensions':['product_name'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + + "'columns':['product_name'],'granularity':'all'," + + "'resultFormat':'compactedList'}"; sql(sql) .limit(4) .returnsUnordered() @@ -864,10 +864,10 @@ public class DruidAdapterIT { final String sql = "select \"product_name\" from \"foodmart\"\n" + "where cast(\"product_id\" as integer) - 1500 BETWEEN 0 AND 2\n" + "order by \"state_province\" desc, \"product_id\""; - final String druidQuery = "{'queryType':'select','dataSource':'foodmart'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," - + "'dimensions':['product_id','product_name','state_province'],'metrics':[],'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + final String druidQuery = "{'queryType':'scan','dataSource':'foodmart'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + + "'columns':['product_id','product_name','state_province'],'granularity':'all'," + + "'resultFormat':'compactedList'}"; sql(sql) .limit(4) .returns( @@ -976,7 +976,7 @@ public class DruidAdapterIT { sql(sql).returnsOrdered("c=494; month=1997-11-01 00:00:00; SALES=5.0", "c=475; month=1997-12-01 00:00:00; SALES=5.0", "c=468; month=1997-03-01 00:00:00; SALES=5.0" - ).queryContains(druidChecker("'queryType':'select'")); + ).queryContains(druidChecker("'queryType':'scan'")); } @Test public void testGroupByTimeAndOneColumnNotProjected() { @@ -1042,7 +1042,7 @@ public class DruidAdapterIT { .limit(2) .returnsUnordered("state_province=CA; A=3; S=74748; C=16347; C0=24441", "state_province=OR; A=3; S=67659; C=21610; C0=21610") - .queryContains(druidChecker("'queryType':'select'")); + .queryContains(druidChecker("'queryType':'scan'")); } @Test public void testGroupByMonthGranularity() { @@ -1135,6 +1135,7 @@ public class DruidAdapterIT { + " \"timestamp\" < '1998-01-01 00:00:00 UTC'\n" + "group by floor(\"timestamp\" to MONTH) order by s asc"; String druidQuery = "{'queryType':'timeseries','dataSource':'foodmart'"; + sql(sql) .limit(3) .returnsOrdered("S=19958; C=5606", "S=20179; C=5523", "S=20388; C=5591") @@ -1331,9 +1332,8 @@ public class DruidAdapterIT { + "where \"product_name\" = 'High Top Dried Mushrooms'\n" + "and \"quarter\" in ('Q2', 'Q3')\n" + "and \"state_province\" = 'WA'"; - final String druidQuery = "{'queryType':'select'," + final String druidQuery = "{'queryType':'scan'," + "'dataSource':'foodmart'," - + "'descending':false," + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + "'filter':{'type':'and','fields':[" + "{'type':'selector','dimension':'product_name','value':'High Top Dried Mushrooms'}," @@ -1341,10 +1341,9 @@ public class DruidAdapterIT { + "{'type':'selector','dimension':'quarter','value':'Q2'}," + "{'type':'selector','dimension':'quarter','value':'Q3'}]}," + "{'type':'selector','dimension':'state_province','value':'WA'}]}," - + "'dimensions':['state_province','city','product_name']," - + "'metrics':[]," + + "'columns':['state_province','city','product_name']," + "'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}"; + + "'resultFormat':'compactedList'}"; final String explain = "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], " + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], " @@ -1445,15 +1444,12 @@ public class DruidAdapterIT { + " DruidQuery(table=[[wiki, wiki]], intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], projects=[[$0, $5]])"; // NOTE: Druid query only has countryName as the dimension // being queried after project is pushed to druid query. - String druidQuery = "{'queryType':'select'," + String druidQuery = "{'queryType':'scan'," + "'dataSource':'wikiticker'," - + "'descending':false," + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," - + "'dimensions':['countryName']," - + "'metrics':[]," + + "'columns':['__time','countryName']," + "'granularity':'all'," - + "'pagingSpec':{'threshold':16384,'fromNext':true}," - + "'context':{'druid.query.fetch':false}}"; + + "'resultFormat':'compactedList'"; sql(sql, WIKI).explainContains(plan); sql(sql, WIKI).queryContains(druidChecker(druidQuery)); } @@ -2043,7 +2039,7 @@ public class DruidAdapterIT { + " DruidQuery(table=[[foodmart, foodmart]], " + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], " + "filter=[=($1, 1558)], projects=[[$0]])\n"; - sql(sql).explainContains(plan).queryContains(druidChecker("'queryType':'select'")) + sql(sql).explainContains(plan).queryContains(druidChecker("'queryType':'scan'")) .returnsUnordered("EXPR$0=20"); } @@ -2521,7 +2517,7 @@ public class DruidAdapterIT { + "\"store_cost\" as c from \"foodmart\" where \"timestamp\" " + ">= '1997-01-01 00:00:00 UTC' and \"timestamp\" < '1997-09-01 00:00:00 UTC' order by c " + "limit 5"; - String postAggString = "'queryType':'select'"; + String postAggString = "'queryType':'scan'"; final String plan = "PLAN=EnumerableInterpreter\n" + " BindableSort(sort0=[$2], dir0=[ASC], fetch=[5])\n" + " BindableProject(A=[$0], B=[$1], C=[-($0, $1)])\n" @@ -3280,13 +3276,13 @@ public class DruidAdapterIT { sql(sql, WIKI) // make sure user_id column is not present .queryContains( - druidChecker("{'queryType':'select','dataSource':'wikiticker'," - + "'descending':false,'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," - + "'dimensions':['channel','cityName','comment','countryIsoCode','countryName'," + druidChecker("{'queryType':'scan','dataSource':'wikiticker'," + + "'intervals':['1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z']," + + "'columns':['__time','channel','cityName','comment','countryIsoCode','countryName'," + "'isAnonymous','isMinor','isNew','isRobot','isUnpatrolled','metroCode'," - + "'namespace','page','regionIsoCode','regionName'],'metrics':['count','added'," - + "'deleted','delta'],'granularity':'all','pagingSpec':{'threshold':5,'fromNext'" - + ":true},'context':{'druid.query.fetch':true}}")); + + "'namespace','page','regionIsoCode','regionName','count','added'," + + "'deleted','delta'],'granularity':'all'," + + "'resultFormat':'compactedList','limit':5")); } /**
