Fixes #131: Improved query builder support for "group by" and "having"
authorKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Sun, 9 Oct 2016 06:54:24 +0000 (23:54 -0700)
committerKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Sun, 9 Oct 2016 06:54:24 +0000 (23:54 -0700)
core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilder.java
core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java
core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java
core/src/main/java/org/apache/metamodel/query/builder/HavingBuilderImpl.java
core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java
core/src/test/java/org/apache/metamodel/query/builder/SyntaxExamplesTest.java

index 18b65d6..75a0c38 100644 (file)
@@ -19,6 +19,7 @@
 package org.apache.metamodel.query.builder;
 
 import org.apache.metamodel.query.FunctionType;
+import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Column;
 
 /**
@@ -28,6 +29,10 @@ public interface GroupedQueryBuilder extends
                SatisfiedQueryBuilder<GroupedQueryBuilder> {
 
        public HavingBuilder having(FunctionType functionType, Column column);
+       
+       public HavingBuilder having(SelectItem selectItem);
+       
+       public HavingBuilder having(String columnExpression);
 
        public SatisfiedOrderByBuilder<GroupedQueryBuilder> orderBy(
                        FunctionType function, Column column);
index b5367ca..7a11ce2 100644 (file)
@@ -26,6 +26,7 @@ import org.apache.metamodel.query.FilterItem;
 import org.apache.metamodel.query.FunctionType;
 import org.apache.metamodel.query.Query;
 import org.apache.metamodel.query.ScalarFunction;
+import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.util.BaseObject;
 
@@ -142,6 +143,21 @@ abstract class GroupedQueryBuilderCallback extends BaseObject implements Grouped
     }
 
     @Override
+    public HavingBuilder having(String columnExpression) {
+        return getQueryBuilder().having(columnExpression);
+    }
+    
+    @Override
+    public HavingBuilder having(SelectItem selectItem) {
+        return getQueryBuilder().having(selectItem);
+    }
+
+    @Override
+    public GroupedQueryBuilder groupBy(String... columnNames) {
+        return getQueryBuilder().groupBy(columnNames);
+    }
+
+    @Override
     public GroupedQueryBuilder groupBy(Column... columns) {
         getQueryBuilder().groupBy(columns);
         return this;
index 0ea0098..d73ae8f 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.metamodel.query.FunctionType;
 import org.apache.metamodel.query.Query;
 import org.apache.metamodel.query.ScalarFunction;
 import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.query.parser.SelectItemParser;
 import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.schema.Table;
 import org.apache.metamodel.util.BaseObject;
@@ -96,7 +97,7 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
 
     @Override
     public ColumnSelectBuilder<GroupedQueryBuilder> select(String columnName) {
-        Column column = findColumn(columnName);
+        final Column column = findColumn(columnName);
         return select(column);
     }
 
@@ -196,8 +197,8 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
             for (FromItem fromItem : fromItems) {
                 final Table table = fromItem.getTable();
                 if (table != null) {
-                    logger.debug("Table available in FROM item: {}. Column names: {}", table,
-                            Arrays.toString(table.getColumnNames()));
+                    logger.debug("Table available in FROM item: {}. Column names: {}", table, Arrays.toString(table
+                            .getColumnNames()));
                 }
             }
         }
@@ -271,11 +272,17 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
 
     @Override
     public GroupedQueryBuilder groupBy(String columnName) {
-        Column column = findColumn(columnName);
+        final Column column = findColumn(columnName);
         return groupBy(column);
     }
 
     @Override
+    public GroupedQueryBuilder groupBy(String... columnNames) {
+        _query.groupBy(columnNames);
+        return this;
+    }
+
+    @Override
     public GroupedQueryBuilder groupBy(Column... columns) {
         if (columns == null) {
             throw new IllegalArgumentException("columns cannot be null");
@@ -285,6 +292,21 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
     }
 
     @Override
+    public HavingBuilder having(String columnExpression) {
+        final SelectItemParser parser = new SelectItemParser(_query, false);
+        final SelectItem selectItem = parser.findSelectItem(columnExpression);
+        return having(selectItem);
+    }
+    
+    @Override
+    public HavingBuilder having(SelectItem selectItem) {
+        if (selectItem == null) {
+            throw new IllegalArgumentException("selectItem cannot be null");
+        }
+        return new HavingBuilderImpl(selectItem, _query, this);
+    }
+
+    @Override
     public HavingBuilder having(FunctionType function, Column column) {
         if (function == null) {
             throw new IllegalArgumentException("function cannot be null");
index 4406e65..41a2b11 100644 (file)
@@ -27,61 +27,59 @@ import org.apache.metamodel.query.Query;
 import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Column;
 
-final class HavingBuilderImpl extends
-               AbstractQueryFilterBuilder<SatisfiedHavingBuilder> implements
-               HavingBuilder, SatisfiedHavingBuilder {
+final class HavingBuilderImpl extends AbstractQueryFilterBuilder<SatisfiedHavingBuilder> implements
+        HavingBuilder,
+        SatisfiedHavingBuilder {
 
-       private final Query _query;
-       private final List<FilterItem> _orFilters;
-       private FilterItem _parentOrFilter;
+    private final Query _query;
+    private final List<FilterItem> _orFilters;
+    private FilterItem _parentOrFilter;
 
-       public HavingBuilderImpl(FunctionType function, Column column, Query query,
-                       GroupedQueryBuilder queryBuilder) {
-               super(new SelectItem(function, column), queryBuilder);
-               _query = query;
-               _orFilters = new ArrayList<FilterItem>();
-       }
+    public HavingBuilderImpl(SelectItem selectItem, Query query, GroupedQueryBuilder queryBuilder) {
+        super(selectItem, queryBuilder);
+        _query = query;
+        _orFilters = new ArrayList<FilterItem>();
+    }
 
-       public HavingBuilderImpl(FunctionType function, Column column, Query query,
-                       FilterItem parentOrFilter, List<FilterItem> orFilters,
-                       GroupedQueryBuilder queryBuilder) {
-               super(new SelectItem(function, column), queryBuilder);
-               _query = query;
-               _orFilters = orFilters;
-               _parentOrFilter = parentOrFilter;
-       }
+    public HavingBuilderImpl(FunctionType function, Column column, Query query, GroupedQueryBuilder queryBuilder) {
+        this(new SelectItem(function, column), query, queryBuilder);
+    }
 
-       @Override
-       protected SatisfiedHavingBuilder applyFilter(FilterItem filter) {
-               if (_parentOrFilter == null) {
-                       _query.having(filter);
-               } else {
-                       if (_parentOrFilter.getChildItemCount() == 1) {
-                               _query.getHavingClause().removeItem(_orFilters.get(0));
-                               _query.getHavingClause().addItem(_parentOrFilter);
-                       }
-               }
-               _orFilters.add(filter);
-               return this;
-       }
+    public HavingBuilderImpl(FunctionType function, Column column, Query query, FilterItem parentOrFilter,
+            List<FilterItem> orFilters, GroupedQueryBuilder queryBuilder) {
+        this(function, column, query, queryBuilder);
+    }
 
-       @Override
-       public HavingBuilder or(FunctionType function, Column column) {
-               if (function == null) {
-                       throw new IllegalArgumentException("function cannot be null");
-               }
-               if (column == null) {
-                       throw new IllegalArgumentException("column cannot be null");
-               }
-               if (_parentOrFilter == null) {
-                       _parentOrFilter = new FilterItem(_orFilters);
-               }
-               return new HavingBuilderImpl(function, column, _query, _parentOrFilter,
-                               _orFilters, getQueryBuilder());
-       }
+    @Override
+    protected SatisfiedHavingBuilder applyFilter(FilterItem filter) {
+        if (_parentOrFilter == null) {
+            _query.having(filter);
+        } else {
+            if (_parentOrFilter.getChildItemCount() == 1) {
+                _query.getHavingClause().removeItem(_orFilters.get(0));
+                _query.getHavingClause().addItem(_parentOrFilter);
+            }
+        }
+        _orFilters.add(filter);
+        return this;
+    }
 
-       @Override
-       public HavingBuilder and(FunctionType function, Column column) {
-               return getQueryBuilder().having(function, column);
-       }
+    @Override
+    public HavingBuilder or(FunctionType function, Column column) {
+        if (function == null) {
+            throw new IllegalArgumentException("function cannot be null");
+        }
+        if (column == null) {
+            throw new IllegalArgumentException("column cannot be null");
+        }
+        if (_parentOrFilter == null) {
+            _parentOrFilter = new FilterItem(_orFilters);
+        }
+        return new HavingBuilderImpl(function, column, _query, _parentOrFilter, _orFilters, getQueryBuilder());
+    }
+
+    @Override
+    public HavingBuilder and(FunctionType function, Column column) {
+        return getQueryBuilder().having(function, column);
+    }
 }
\ No newline at end of file
index e6b0670..f94ebdb 100644 (file)
@@ -103,10 +103,12 @@ public interface SatisfiedQueryBuilder<B extends SatisfiedQueryBuilder<?>> {
     public SatisfiedOrderByBuilder<B> orderBy(Column column);
 
     public GroupedQueryBuilder groupBy(String columnName);
+    
+    public GroupedQueryBuilder groupBy(String ... columnNames);
 
     public GroupedQueryBuilder groupBy(Column column);
 
-    public B groupBy(Column... columns);
+    public GroupedQueryBuilder groupBy(Column... columns);
 
     /**
      * Gets the built query as a {@link Query} object. Typically the returned
index 8876df9..af81801 100644 (file)
@@ -70,6 +70,11 @@ public class SyntaxExamplesTest extends TestCase {
         dc.query().from(table1).selectCount().select(col1).groupBy(col1).having(FunctionType.SUM, col1).greaterThan(3)
                 .orderBy(col1).asc();
     }
+    
+    public void testMultiGroupByAndHaving() throws Exception {
+        dc.query().from(table1).select("foo", "bar", "COUNT(*)").groupBy("foo","bar").having("COUNT(*)").greaterThan(3)
+                .orderBy(col1).asc();
+    }
 
     public void testMultipleTables() throws Exception {
         Query q = dc.query().from(table1).as("t1").and(table2).as("t2").select(col1).where(col1).greaterThan(col2)