=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java 2012-12-21 12:59:39 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java 2012-12-21 14:12:04 +0000 @@ -145,11 +145,24 @@ dimensions.put( dimension, values ); } + /** + * Returns the dimensions which are part of dimensions and filters. If any + * such dimensions exist this object is in an illegal state. + */ public Collection dimensionsAsFilters() { return CollectionUtils.intersection( dimensions.keySet(), filters.keySet() ); } + /** + * Indicates whether periods are present as a dimension or as a filter. If + * not this object is in an illegal state. + */ + public boolean hasPeriods() + { + return dimensions.containsKey( PERIOD_DIM_ID ) || filters.containsKey( PERIOD_DIM_ID ); + } + @Override public int hashCode() { @@ -291,7 +304,7 @@ } // ------------------------------------------------------------------------- - // Get and set helpers + // Get and set helpers for dimensions // ------------------------------------------------------------------------- public List getDatElements() @@ -321,6 +334,40 @@ public void setOrganisationUnits( List organisationUnits ) { - this.dimensions.put( ORGUNIT_DIM_ID, organisationUnits ); - } + dimensions.put( ORGUNIT_DIM_ID, organisationUnits ); + } + + // ------------------------------------------------------------------------- + // Get and set helpers for filters + // ------------------------------------------------------------------------- + + public List getFilterDatElements() + { + return filters.get( DATAELEMENT_DIM_ID ); + } + + public void setFilterDataElements( List dataElements ) + { + filters.put( DATAELEMENT_DIM_ID, dataElements ); + } + + public List getFilterPeriods() + { + return filters.get( PERIOD_DIM_ID ); + } + + public void setFilterPeriods( List periods ) + { + filters.put( PERIOD_DIM_ID, periods ); + } + + public List getFilterOrganisationUnits() + { + return filters.get( ORGUNIT_DIM_ID ); + } + + public void setFilterOrganisationUnits( List organisationUnits ) + { + filters.put( ORGUNIT_DIM_ID, organisationUnits ); + } } === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java 2012-12-21 12:59:39 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java 2012-12-21 14:12:04 +0000 @@ -54,8 +54,9 @@ public List planQuery( DataQueryParams params, int optimalQueries ) { - Assert.isTrue( params.getDimensions().size() > 0 ); + Assert.isTrue( !params.getDimensions().isEmpty() ); Assert.isTrue( params.dimensionsAsFilters().isEmpty() ); + Assert.isTrue( params.hasPeriods() ); // --------------------------------------------------------------------- // Group queries by partition, period type and organisation unit level @@ -122,7 +123,7 @@ */ private List splitByDimension( List queries, String dimension, int optimalQueries ) { - int pageNo = MathUtils.divideToCeil( optimalQueries, queries.size() ); + int optimalForSubQuery = MathUtils.divideToCeil( optimalQueries, queries.size() ); List subQueries = new ArrayList(); @@ -130,14 +131,20 @@ { List values = query.getDimensions().get( dimension ); - List> valuePages = new PaginatedList( values ).setNumberOfPages( pageNo ).getPages(); + if ( values == null || values.isEmpty() ) + { + subQueries.add( new DataQueryParams( query ) ); + continue; + } + + List> valuePages = new PaginatedList( values ).setNumberOfPages( optimalForSubQuery ).getPages(); for ( List valuePage : valuePages ) { DataQueryParams subQuery = new DataQueryParams( query ); subQuery.setDimension( dimension, valuePage ); subQueries.add( subQuery ); - } + } } return subQueries; @@ -146,20 +153,35 @@ /** * Groups the given query into sub queries based on its periods and which * partition it should be executed against. Sets the partition table name on - * each query. + * each query. Queries are grouped based on both dimensions and filters. */ private List groupByPartition( DataQueryParams params ) { List queries = new ArrayList(); - ListMap tablePeriodMap = PartitionUtils.getTablePeriodMap( params.getPeriods() ); - - for ( String tableName : tablePeriodMap.keySet() ) - { - DataQueryParams query = new DataQueryParams( params ); - query.setPeriods( tablePeriodMap.get( tableName ) ); - query.setTableName( tableName ); - queries.add( query ); + if ( params.getPeriods() != null && !params.getPeriods().isEmpty() ) + { + ListMap tablePeriodMap = PartitionUtils.getTablePeriodMap( params.getPeriods() ); + + for ( String tableName : tablePeriodMap.keySet() ) + { + DataQueryParams query = new DataQueryParams( params ); + query.setPeriods( tablePeriodMap.get( tableName ) ); + query.setTableName( tableName ); + queries.add( query ); + } + } + else + { + ListMap tablePeriodMap = PartitionUtils.getTablePeriodMap( params.getFilterPeriods() ); + + for ( String tableName : tablePeriodMap.keySet() ) + { + DataQueryParams query = new DataQueryParams( params ); + query.setFilterPeriods( tablePeriodMap.get( tableName ) ); + query.setTableName( tableName ); + queries.add( query ); + } } return queries; @@ -172,6 +194,12 @@ private List groupByPeriodType( DataQueryParams params ) { List queries = new ArrayList(); + + if ( params.getPeriods() == null || params.getPeriods().isEmpty() ) + { + queries.add( new DataQueryParams( params ) ); + return queries; + } ListMap periodTypePeriodMap = getPeriodTypePeriodMap( params.getPeriods() ); @@ -193,6 +221,12 @@ private List groupByOrgUnitLevel( DataQueryParams params ) { List queries = new ArrayList(); + + if ( params.getOrganisationUnits() == null || params.getOrganisationUnits().isEmpty() ) + { + queries.add( new DataQueryParams( params ) ); + return queries; + } ListMap levelOrgUnitMap = getLevelOrgUnitMap( params.getOrganisationUnits() ); === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/data/QueryPlannerTest.java' --- dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/data/QueryPlannerTest.java 2012-12-19 15:51:21 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/data/QueryPlannerTest.java 2012-12-21 14:12:04 +0000 @@ -192,6 +192,61 @@ } } + /** + * Splits on 3 data elements. No organisation units specified. + */ + @Test + public void planQueryE() + { + DataQueryParams params = new DataQueryParams(); + params.setDataElements( Arrays.asList( "a", "b", "c" ) ); + params.setPeriods( Arrays.asList( "200001", "200002", "200003", "200004", "200005", "200006", "200007", "200008", "200009" ) ); + + List queries = queryPlanner.planQuery( params, 4 ); + + assertEquals( 3, queries.size() ); + + for ( DataQueryParams query : queries ) + { + assertTrue( samePeriodType( query.getPeriods() ) ); + assertTrue( samePartition( query.getPeriods() ) ); + } + } + + /** + * Splits on 5 organisation units. No data elements units specified. + */ + @Test + public void planQueryF() + { + DataQueryParams params = new DataQueryParams(); + params.setOrganisationUnits( Arrays.asList( ouA.getUid(), ouB.getUid(), ouC.getUid(), ouD.getUid(), ouE.getUid() ) ); + params.setPeriods( Arrays.asList( "200001", "200002", "200003", "200004", "200005", "200006", "200007", "200008", "200009" ) ); + + List queries = queryPlanner.planQuery( params, 4 ); + + assertEquals( 3, queries.size() ); + + for ( DataQueryParams query : queries ) + { + assertTrue( samePeriodType( query.getPeriods() ) ); + assertTrue( samePartition( query.getPeriods() ) ); + } + } + + /** + * Expected to fail because of no periods specified. + */ + @Test( expected = IllegalArgumentException.class ) + public void planQueryG() + { + DataQueryParams params = new DataQueryParams(); + params.setDataElements( Arrays.asList( "a", "b", "c" ) ); + params.setOrganisationUnits( Arrays.asList( ouA.getUid(), ouB.getUid(), ouC.getUid(), ouD.getUid(), ouE.getUid() ) ); + + queryPlanner.planQuery( params, 4 ); + } + // ------------------------------------------------------------------------- // Supportive methods // ------------------------------------------------------------------------- === modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/PaginatedList.java' --- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/PaginatedList.java 2011-12-26 10:07:59 +0000 +++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/PaginatedList.java 2012-12-21 14:12:04 +0000 @@ -66,7 +66,8 @@ /** * Sets the number of pages. The page size will be calculated and set in - * order to provide the appropriate total number of pages. + * order to provide the appropriate total number of pages. The resulting + * number of pages can be lower than the given argument but not higher. */ public PaginatedList setNumberOfPages( int pages ) { === modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AnalyticsController.java' --- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AnalyticsController.java 2012-12-21 12:59:39 +0000 +++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AnalyticsController.java 2012-12-21 14:12:04 +0000 @@ -148,6 +148,12 @@ return false; } + if ( !params.hasPeriods() ) + { + ContextUtils.conflictResponse( response, "Periods must be specified as dimension or filter" ); + return false; + } + return true; } }