SOLR-8002: Add column alias support to the Parallel SQL Interface

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1724319 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Joel Bernstein 2016-01-12 20:47:33 +00:00
parent 4ca99387a3
commit f3dd997f65
3 changed files with 402 additions and 26 deletions

View File

@ -130,6 +130,9 @@ New Features
* SOLR-8479: Add JDBCStream to Streaming API and Streaming Expressions for integration with external data sources
(Dennis Gove)
* SOLR-8002: Add column alias support to the Parallel SQL Interface (Joel Bernstein)
Bug Fixes
----------------------
* SOLR-8386: Add field option in the new admin UI schema page loads up even when no schemaFactory has been

View File

@ -149,6 +149,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
SQLVisitor sqlVistor = new SQLVisitor(new StringBuilder());
sqlVistor.process(statement, new Integer(0));
sqlVistor.reverseAliases();
TupleStream sqlStream = null;
@ -234,13 +235,13 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
// Once we make this a Expressionable the problem will be solved.
if(sqlVisitor.havingExpression != null) {
tupleStream = new HavingStream(tupleStream, sqlVisitor.havingExpression);
tupleStream = new HavingStream(tupleStream, sqlVisitor.havingExpression, sqlVisitor.reverseColumnAliases );
}
if(sqlVisitor.sorts != null && sqlVisitor.sorts.size() > 0) {
if(!sortsEqual(buckets, sortDirection, sqlVisitor.sorts)) {
if(!sortsEqual(buckets, sortDirection, sqlVisitor.sorts, sqlVisitor.reverseColumnAliases)) {
int limit = sqlVisitor.limit == -1 ? 100 : sqlVisitor.limit;
StreamComparator comp = getComp(sqlVisitor.sorts);
StreamComparator comp = getComp(sqlVisitor.sorts, sqlVisitor.reverseColumnAliases);
//Rank the Tuples
//If parallel stream is used ALL the Rolled up tuples from the workers will be ranked
//Providing a true Top or Bottom.
@ -254,6 +255,10 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
}
}
if(sqlVisitor.hasColumnAliases) {
tupleStream = new SelectStream(tupleStream, sqlVisitor.columnAliases);
}
return tupleStream;
}
@ -277,7 +282,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
StreamComparator comp = null;
if(sqlVisitor.sorts != null && sqlVisitor.sorts.size() > 0) {
StreamComparator[] adjustedSorts = adjustSorts(sqlVisitor.sorts, buckets);
StreamComparator[] adjustedSorts = adjustSorts(sqlVisitor.sorts, buckets, sqlVisitor.reverseColumnAliases);
// Because of the way adjustSorts works we know that each FieldComparator has a single
// field name. For this reason we can just look at the leftFieldName
FieldEqualitor[] fieldEqualitors = new FieldEqualitor[adjustedSorts.length];
@ -364,18 +369,22 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
tupleStream = new LimitStream(tupleStream, sqlVisitor.limit);
}
if(sqlVisitor.hasColumnAliases) {
tupleStream = new SelectStream(tupleStream, sqlVisitor.columnAliases);
}
return tupleStream;
}
private static StreamComparator[] adjustSorts(List<SortItem> sorts, Bucket[] buckets) throws IOException {
private static StreamComparator[] adjustSorts(List<SortItem> sorts, Bucket[] buckets, Map<String, String> reverseColumnAliases) throws IOException {
List<FieldComparator> adjustedSorts = new ArrayList();
Set<String> bucketFields = new HashSet();
Set<String> sortFields = new HashSet();
for(SortItem sortItem : sorts) {
sortFields.add(getSortField(sortItem));
adjustedSorts.add(new FieldComparator(getSortField(sortItem),
sortFields.add(getSortField(sortItem, reverseColumnAliases));
adjustedSorts.add(new FieldComparator(getSortField(sortItem, reverseColumnAliases),
ascDescComp(sortItem.getOrdering().toString())));
}
@ -384,7 +393,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
}
for(SortItem sortItem : sorts) {
String sortField = getSortField(sortItem);
String sortField = getSortField(sortItem, reverseColumnAliases);
if(!bucketFields.contains(sortField)) {
throw new IOException("All sort fields must be in the field list.");
}
@ -431,7 +440,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
sorts[i] = new FieldComparator("index", ComparatorOrder.ASCENDING);
}
} else {
StreamComparator[] comps = adjustSorts(sqlVisitor.sorts, buckets);
StreamComparator[] comps = adjustSorts(sqlVisitor.sorts, buckets, sqlVisitor.reverseColumnAliases);
sorts = new FieldComparator[comps.length];
for(int i=0; i<comps.length; i++) {
sorts[i] = (FieldComparator)comps[i];
@ -450,7 +459,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
tupleStream = new LimitStream(tupleStream, sqlVisitor.limit);
}
return new SelectStream(tupleStream, sqlVisitor.fields);
return new SelectStream(tupleStream, sqlVisitor.columnAliases);
}
private static TupleStream doGroupByWithAggregatesFacets(SQLVisitor sqlVisitor) throws IOException {
@ -480,7 +489,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
sorts[i] = new FieldComparator("index", ComparatorOrder.ASCENDING);
}
} else {
sorts = getComps(sqlVisitor.sorts);
sorts = getComps(sqlVisitor.sorts, sqlVisitor.reverseColumnAliases);
}
TupleStream tupleStream = new FacetStream(zkHost,
@ -492,7 +501,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
limit);
if(sqlVisitor.havingExpression != null) {
tupleStream = new HavingStream(tupleStream, sqlVisitor.havingExpression);
tupleStream = new HavingStream(tupleStream, sqlVisitor.havingExpression, sqlVisitor.reverseColumnAliases);
}
if(sqlVisitor.limit > 0)
@ -500,6 +509,10 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
tupleStream = new LimitStream(tupleStream, sqlVisitor.limit);
}
if(sqlVisitor.hasColumnAliases) {
tupleStream = new SelectStream(tupleStream, sqlVisitor.columnAliases);
}
return tupleStream;
}
@ -559,7 +572,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
if (comma) {
siBuf.append(",");
}
siBuf.append(getSortField(sortItem) + " " + ascDesc(sortItem.getOrdering().toString()));
siBuf.append(getSortField(sortItem, sqlVisitor.reverseColumnAliases) + " " + ascDesc(sortItem.getOrdering().toString()));
}
} else {
if(sqlVisitor.limit < 0) {
@ -585,17 +598,25 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
params.put("sort", siBuf.toString());
}
TupleStream tupleStream = null;
if(sqlVisitor.limit > -1) {
params.put("rows", Integer.toString(sqlVisitor.limit));
return new LimitStream(new CloudSolrStream(zkHost, collection, params), sqlVisitor.limit);
tupleStream = new LimitStream(new CloudSolrStream(zkHost, collection, params), sqlVisitor.limit);
} else {
//Only use the export handler when no limit is specified.
params.put(CommonParams.QT, "/export");
return new CloudSolrStream(zkHost, collection, params);
tupleStream = new CloudSolrStream(zkHost, collection, params);
}
if(sqlVisitor.hasColumnAliases) {
return new SelectStream(tupleStream, sqlVisitor.columnAliases);
} else {
return tupleStream;
}
}
private static boolean sortsEqual(Bucket[] buckets, String direction, List<SortItem> sortItems) {
private static boolean sortsEqual(Bucket[] buckets, String direction, List<SortItem> sortItems, Map<String, String> reverseColumnAliases) {
if(buckets.length != sortItems.size()) {
return false;
}
@ -603,7 +624,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
for(int i=0; i< buckets.length; i++) {
Bucket bucket = buckets[i];
SortItem sortItem = sortItems.get(i);
if(!bucket.toString().equals(getSortField(sortItem))) {
if(!bucket.toString().equals(getSortField(sortItem, reverseColumnAliases))) {
return false;
}
@ -635,6 +656,10 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
params,
metrics);
if(sqlVisitor.hasColumnAliases) {
tupleStream = new SelectStream(tupleStream, sqlVisitor.columnAliases);
}
return tupleStream;
}
@ -690,13 +715,13 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
}
}
private static StreamComparator getComp(List<SortItem> sortItems) {
private static StreamComparator getComp(List<SortItem> sortItems, Map<String, String> reverseColumnAliases) {
FieldComparator[] comps = new FieldComparator[sortItems.size()];
for(int i=0; i<sortItems.size(); i++) {
SortItem sortItem = sortItems.get(i);
String ordering = sortItem.getOrdering().toString();
ComparatorOrder comparatorOrder = ascDescComp(ordering);
String sortKey = getSortField(sortItem);
String sortKey = getSortField(sortItem, reverseColumnAliases);
comps[i] = new FieldComparator(sortKey, comparatorOrder);
}
@ -707,13 +732,13 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
}
}
private static FieldComparator[] getComps(List<SortItem> sortItems) {
private static FieldComparator[] getComps(List<SortItem> sortItems, Map<String, String> reverseColumnAliases) {
FieldComparator[] comps = new FieldComparator[sortItems.size()];
for(int i=0; i<sortItems.size(); i++) {
SortItem sortItem = sortItems.get(i);
String ordering = sortItem.getOrdering().toString();
ComparatorOrder comparatorOrder = ascDescComp(ordering);
String sortKey = getSortField(sortItem);
String sortKey = getSortField(sortItem, reverseColumnAliases);
comps[i] = new FieldComparator(sortKey, comparatorOrder);
}
@ -884,6 +909,9 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
public boolean groupByQuery;
public Expression havingExpression;
public boolean isDistinct;
public boolean hasColumnAliases;
public Map<String, String> columnAliases = new HashMap();
public Map<String, String> reverseColumnAliases = new HashMap();
public SQLVisitor(StringBuilder builder) {
this.builder = builder;
@ -893,6 +921,28 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
throw new UnsupportedOperationException("not yet implemented: " + node);
}
protected void reverseAliases() {
for(String key : columnAliases.keySet()) {
reverseColumnAliases.put(columnAliases.get(key), key);
}
//Handle the group by.
List<String> newGroups = new ArrayList();
for(String g : groupBy) {
if (reverseColumnAliases.containsKey(g)) {
newGroups.add(reverseColumnAliases.get(g));
} else {
newGroups.add(g);
}
}
groupBy = newGroups;
}
protected Void visitUnnest(Unnest node, Integer indent) {
return null;
}
@ -966,8 +1016,6 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
return null;
}
protected Void visitComparisonExpression(ComparisonExpression node, Integer index) {
String field = node.getLeft().toString();
String value = node.getRight().toString();
@ -1029,6 +1077,11 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
fields.add(field);
if(node.getAlias().isPresent()) {
String alias = node.getAlias().get();
columnAliases.put(field, alias);
hasColumnAliases = true;
} else {
columnAliases.put(field, field);
}
return null;
@ -1078,7 +1131,7 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
}
}
private static String getSortField(SortItem sortItem)
private static String getSortField(SortItem sortItem, Map<String, String> reverseColumnAliases)
{
String field;
Expression ex = sortItem.getSortKey();
@ -1105,6 +1158,10 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
field = stripSingleQuotes(stringLiteral.toString());
}
if(reverseColumnAliases.containsKey(field)) {
field = reverseColumnAliases.get(field);
}
return field;
}
@ -1238,9 +1295,9 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
private HavingVisitor havingVisitor;
private Expression havingExpression;
public HavingStream(TupleStream stream, Expression havingExpression) {
public HavingStream(TupleStream stream, Expression havingExpression, Map<String, String> reverseAliasMap) {
this.stream = stream;
this.havingVisitor = new HavingVisitor();
this.havingVisitor = new HavingVisitor(reverseAliasMap);
this.havingExpression = havingExpression;
}
@ -1282,6 +1339,12 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
private static class HavingVisitor extends AstVisitor<Boolean, Tuple> {
private Map<String,String> reverseAliasMap;
public HavingVisitor(Map<String, String> reverseAliasMap) {
this.reverseAliasMap = reverseAliasMap;
}
protected Boolean visitLogicalBinaryExpression(LogicalBinaryExpression node, Tuple tuple) {
Boolean b = process(node.getLeft(), tuple);
@ -1304,6 +1367,11 @@ public class SQLHandler extends RequestHandlerBase implements SolrCoreAware {
protected Boolean visitComparisonExpression(ComparisonExpression node, Tuple tuple) {
String field = getHavingField(node.getLeft());
if(reverseAliasMap.containsKey(field)) {
field = reverseAliasMap.get(field);
}
double d = Double.parseDouble(node.getRight().toString());
double td = tuple.getDouble(field);
ComparisonExpression.Type t = node.getType();

View File

@ -320,6 +320,56 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getLong("field_i") == 7);
assert(tuple.get("str_s").equals("a"));
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select id as myId, field_i as myInt, str_s as myString from collection1 where text='XXXX' AND id='(1 2 3)' order by myInt desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
assert(tuples.size() == 3);
tuple = tuples.get(0);
assert(tuple.getLong("myId") == 3);
assert(tuple.getLong("myInt") == 20);
assert(tuple.get("myString").equals("a"));
tuple = tuples.get(1);
assert(tuple.getLong("myId") == 2);
assert(tuple.getLong("myInt") == 8);
assert(tuple.get("myString").equals("b"));
tuple = tuples.get(2);
assert(tuple.getLong("myId") == 1);
assert(tuple.getLong("myInt") == 7);
assert(tuple.get("myString").equals("a"));
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select id as myId, field_i as myInt, str_s as myString from collection1 where text='XXXX' AND id='(1 2 3)' order by field_i desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
assert(tuples.size() == 3);
tuple = tuples.get(0);
assert(tuple.getLong("myId") == 3);
assert(tuple.getLong("myInt") == 20);
assert(tuple.get("myString").equals("a"));
tuple = tuples.get(1);
assert(tuple.getLong("myId") == 2);
assert(tuple.getLong("myInt") == 8);
assert(tuple.get("myString").equals("b"));
tuple = tuples.get(2);
assert(tuple.getLong("myId") == 1);
assert(tuple.getLong("myInt") == 7);
assert(tuple.get("myString").equals("a"));
} finally {
delete();
}
@ -559,6 +609,33 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getDouble("max(field_i)") == 20);
assert(tuple.getDouble("avg(field_i)") == 13.5D);
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s as myString, 'count(*)', sum('field_i') as sum, min(field_i), max(field_i), avg(field_i) from collection1 where text='XXXX' group by 'str_s' order by sum asc limit 2");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
//Only two results because of the limit.
assert(tuples.size() == 2);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("b"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 19);
assert(tuple.getDouble("min(field_i)") == 8);
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
tuple = tuples.get(1);
assert(tuple.get("myString").equals("a"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 27);
assert(tuple.getDouble("min(field_i)") == 7);
assert(tuple.getDouble("max(field_i)") == 20);
assert(tuple.getDouble("avg(field_i)") == 13.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s, count(*), sum(field_i), min(field_i), max(field_i), avg(field_i) from collection1 where (text='XXXX' AND NOT text='XXXX XXX') group by str_s order by str_s desc");
@ -596,6 +673,44 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getDouble("avg(field_i)") == 13.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s as myString, count(*) as count, sum(field_i) as sum, min(field_i) as min, max(field_i) as max, avg(field_i) as avg from collection1 where (text='XXXX' AND NOT text='XXXX XXX') group by str_s order by str_s desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
//The sort by and order by match and no limit is applied. All the Tuples should be returned in
//this scenario.
assert(tuples.size() == 3);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("c"));
assert(tuple.getDouble("count") == 4);
assert(tuple.getDouble("sum") == 180);
assert(tuple.getDouble("min") == 30);
assert(tuple.getDouble("max") == 60);
assert(tuple.getDouble("avg") == 45);
tuple = tuples.get(1);
assert(tuple.get("myString").equals("b"));
assert(tuple.getDouble("count") == 2);
assert(tuple.getDouble("sum") == 19);
assert(tuple.getDouble("min") == 8);
assert(tuple.getDouble("max") == 11);
assert(tuple.getDouble("avg") == 9.5D);
tuple = tuples.get(2);
assert(tuple.get("myString").equals("a"));
assert(tuple.getDouble("count") == 2);
assert(tuple.getDouble("sum") == 27);
assert(tuple.getDouble("min") == 7);
assert(tuple.getDouble("max") == 20);
assert(tuple.getDouble("avg") == 13.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s, count(*), sum(field_i), min(field_i), max(field_i), avg(field_i) from collection1 where text='XXXX' group by str_s having sum(field_i) = 19");
@ -631,6 +746,25 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s as myString, count(*), sum(field_i) as sum, min(field_i), max(field_i), avg(field_i) from collection1 where text='XXXX' group by myString having ((sum = 19) AND (min(field_i) = 8))");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
//Only two results because of the limit.
assert(tuples.size() == 1);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("b"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 19);
assert(tuple.getDouble("min(field_i)") == 8);
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select str_s, count(*), sum(field_i), min(field_i), max(field_i), avg(field_i) from collection1 where text='XXXX' group by str_s having ((sum(field_i) = 19) AND (min(field_i) = 100))");
@ -740,6 +874,43 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getLong("field_i") == 1);
//reverse the sort
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("aggregationMode", "facet");
params.put("stmt", "select distinct str_s as myString, field_i as myInt from collection1 order by str_s desc, myInt desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
assert(tuples.size() == 6);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("myInt") == 60);
tuple = tuples.get(1);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("myInt") == 50);
tuple = tuples.get(2);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("myInt") == 30);
tuple = tuples.get(3);
assert(tuple.get("myString").equals("b"));
assert(tuple.getLong("myInt") == 2);
tuple = tuples.get(4);
assert(tuple.get("myString").equals("a"));
assert(tuple.getLong("myInt") == 20);
tuple = tuples.get(5);
assert(tuple.get("myString").equals("a"));
assert(tuple.getLong("myInt") == 1);
//test with limit
params = new HashMap();
params.put(CommonParams.QT, "/sql");
@ -915,6 +1086,42 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getLong("field_i") == 1);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select distinct str_s as myString, field_i from collection1 order by myString desc, field_i desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
assert(tuples.size() == 6);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("field_i") == 60);
tuple = tuples.get(1);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("field_i") == 50);
tuple = tuples.get(2);
assert(tuple.get("myString").equals("c"));
assert(tuple.getLong("field_i") == 30);
tuple = tuples.get(3);
assert(tuple.get("myString").equals("b"));
assert(tuple.getLong("field_i") == 2);
tuple = tuples.get(4);
assert(tuple.get("myString").equals("a"));
assert(tuple.getLong("field_i") == 20);
tuple = tuples.get(5);
assert(tuple.get("myString").equals("a"));
assert(tuple.getLong("field_i") == 1);
//test with limit
params = new HashMap();
params.put(CommonParams.QT, "/sql");
@ -1254,6 +1461,45 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getDouble("max(field_i)") == 20);
assert(tuple.getDouble("avg(field_i)") == 13.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("aggregationMode", "facet");
params.put("stmt", "select str_s as myString, count(*), sum(field_i) as sum, min(field_i), max(field_i), avg(field_i) from collection1 where (text='XXXX' AND NOT text='XXXX XXX') group by myString order by myString desc");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
//The sort by and order by match and no limit is applied. All the Tuples should be returned in
//this scenario.
assert(tuples.size() == 3);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("c"));
assert(tuple.getDouble("count(*)") == 4);
assert(tuple.getDouble("sum") == 180);
assert(tuple.getDouble("min(field_i)") == 30);
assert(tuple.getDouble("max(field_i)") == 60);
assert(tuple.getDouble("avg(field_i)") == 45);
tuple = tuples.get(1);
assert(tuple.get("myString").equals("b"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 19);
assert(tuple.getDouble("min(field_i)") == 8);
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
tuple = tuples.get(2);
assert(tuple.get("myString").equals("a"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 27);
assert(tuple.getDouble("min(field_i)") == 7);
assert(tuple.getDouble("max(field_i)") == 20);
assert(tuple.getDouble("avg(field_i)") == 13.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
@ -1292,6 +1538,26 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("aggregationMode", "facet");
params.put("stmt", "select str_s myString, count(*), sum(field_i) as sum, min(field_i), max(field_i), avg(field_i) from collection1 where text='XXXX' group by myString having ((sum = 19) AND (min(field_i) = 8))");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
//Only two results because of the limit.
assert(tuples.size() == 1);
tuple = tuples.get(0);
assert(tuple.get("myString").equals("b"));
assert(tuple.getDouble("count(*)") == 2);
assert(tuple.getDouble("sum") == 19);
assert(tuple.getDouble("min(field_i)") == 8);
assert(tuple.getDouble("max(field_i)") == 11);
assert(tuple.getDouble("avg(field_i)") == 9.5D);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("aggregationMode", "facet");
@ -1516,6 +1782,45 @@ public class TestSQLHandler extends AbstractFullDistribZkTestBase {
assertTrue(count.doubleValue() == 10);
params = new HashMap();
params.put(CommonParams.QT, "/sql");
params.put("stmt", "select count(*) as count, sum(a_i) as sum, min(a_i) as min, max(a_i) as max, avg(a_i) as avg, sum(a_f), min(a_f), max(a_f), avg(a_f) from collection1");
solrStream = new SolrStream(jetty.url, params);
tuples = getTuples(solrStream);
assert(tuples.size() == 1);
//Test Long and Double Sums
tuple = tuples.get(0);
sumi = tuple.getDouble("sum");
sumf = tuple.getDouble("sum(a_f)");
mini = tuple.getDouble("min");
minf = tuple.getDouble("min(a_f)");
maxi = tuple.getDouble("max");
maxf = tuple.getDouble("max(a_f)");
avgi = tuple.getDouble("avg");
avgf = tuple.getDouble("avg(a_f)");
count = tuple.getDouble("count");
assertTrue(sumi.longValue() == 70);
assertTrue(sumf.doubleValue() == 55.0D);
assertTrue(mini.doubleValue() == 0.0D);
assertTrue(minf.doubleValue() == 1.0D);
assertTrue(maxi.doubleValue() == 14.0D);
assertTrue(maxf.doubleValue() == 10.0D);
assertTrue(avgi.doubleValue() == 7.0D);
assertTrue(avgf.doubleValue() == 5.5D);
assertTrue(count.doubleValue() == 10);
// Test where clause hits
params = new HashMap();