← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 9388: Analytics, impl filtering

 

------------------------------------------------------------
revno: 9388
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2012-12-21 17:50:36 +0100
message:
  Analytics, impl filtering
modified:
  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/data/DefaultAnalyticsService.java
  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/JdbcAnalyticsManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/data/QueryPlannerTest.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== 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 14:12:04 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java	2012-12-21 16:50:36 +0000
@@ -49,7 +49,7 @@
     public static final String ORGUNIT_DIM_ID = "ou";
     public static final String VALUE_ID = "value";
     
-    private static final String LEVEL_PREFIX = "uidlevel";
+    public static final String LEVEL_PREFIX = "uidlevel";
     
     private Map<String, List<String>> dimensions = new HashMap<String, List<String>>();
     
@@ -120,6 +120,11 @@
                 
         return list;
     }
+    
+    public List<String> getFilterNames()
+    {
+        return new ArrayList<String>( filters.keySet() );
+    }
         
     public Map<String, List<String>> getDimensionMap()
     {
@@ -140,11 +145,6 @@
         return map;
     }
     
-    public void setDimension( String dimension, List<String> values )
-    {
-        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.
@@ -229,7 +229,7 @@
     @Override
     public String toString()
     {
-        return dimensions != null ? dimensions.toString() : "";
+        return "[Dimensions: " + dimensions + ", Filters: " + filters + "]";
     }
         
     // -------------------------------------------------------------------------
@@ -302,6 +302,27 @@
     {
         this.organisationUnitLevel = organisationUnitLevel;
     }
+
+    // -------------------------------------------------------------------------
+    // Get and set helpers for dimensions or filter
+    // -------------------------------------------------------------------------
+  
+    public List<String> getDimensionOrFilter( String key )
+    {
+        return dimensions.containsKey( key ) ? dimensions.get( key ) : filters.get( key );
+    }
+    
+    public void resetDimensionOrFilter( String key, List<String> values )
+    {
+        if ( dimensions.containsKey( key ) )
+        {
+            dimensions.put( key, values );
+        }
+        else if ( filters.containsKey( key ) )
+        {
+            filters.put( key, values );
+        }
+    }
     
     // -------------------------------------------------------------------------
     // Get and set helpers for dimensions

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2012-12-19 14:35:23 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2012-12-21 16:50:36 +0000
@@ -56,9 +56,6 @@
     //TODO create data mart for average, less-than yearly data elements
     //     aggregate in time dimension only
     //     insert into standard analytics table?
-    //TODO filter, exclude from select
-    
-    //TODO investigate whether quarterly partitions are faster
     
     @Autowired
     private AnalyticsManager analyticsManager;
@@ -93,7 +90,7 @@
     {
         Timer t = new Timer().start();
 
-        int optimalQueries = MathUtils.getWithin( SystemUtils.getCpuCores(), 1, 4 );
+        int optimalQueries = MathUtils.getWithin( SystemUtils.getCpuCores(), 1, 6 );
         
         List<DataQueryParams> queries = queryPlanner.planQuery( params, optimalQueries );
         

=== 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 14:12:04 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java	2012-12-21 16:50:36 +0000
@@ -45,6 +45,8 @@
 public class DefaultQueryPlanner
     implements QueryPlanner
 {
+    //TODO call getLevelOrgUnitMap once
+    
     @Autowired
     private OrganisationUnitService organisationUnitService;
     
@@ -86,6 +88,13 @@
             }
         }
 
+        // ---------------------------------------------------------------------
+        // Set filters for each period type and organisation unit level
+        // ---------------------------------------------------------------------
+        
+        queries = setFilterByPeriodType( queries );
+        queries = setFilterByOrgUnitLevel( queries );
+        
         if ( queries.size() >= optimalQueries )
         {
             return queries;
@@ -95,7 +104,7 @@
         // Group by organisation unit
         // ---------------------------------------------------------------------
         
-        queries = splitByDimension( queries, DataQueryParams.ORGUNIT_DIM_ID, optimalQueries );
+        queries = splitByDimensionOrFilter( queries, DataQueryParams.ORGUNIT_DIM_ID, optimalQueries );
 
         if ( queries.size() >= optimalQueries )
         {
@@ -106,7 +115,7 @@
         // Group by data element
         // ---------------------------------------------------------------------
         
-        return splitByDimension( queries, DataQueryParams.DATAELEMENT_DIM_ID, optimalQueries );
+        return splitByDimensionOrFilter( queries, DataQueryParams.DATAELEMENT_DIM_ID, optimalQueries );
     }
         
     public boolean canQueryFromDataMart( DataQueryParams params )
@@ -121,7 +130,7 @@
     /**
      * Splits the given list of queries in sub queries on the given dimension.
      */
-    private List<DataQueryParams> splitByDimension( List<DataQueryParams> queries, String dimension, int optimalQueries )
+    private List<DataQueryParams> splitByDimensionOrFilter( List<DataQueryParams> queries, String dimension, int optimalQueries )
     {
         int optimalForSubQuery = MathUtils.divideToCeil( optimalQueries, queries.size() );
         
@@ -129,7 +138,7 @@
         
         for ( DataQueryParams query : queries )
         {
-            List<String> values = query.getDimensions().get( dimension );
+            List<String> values = query.getDimensionOrFilter( dimension );
 
             if ( values == null || values.isEmpty() )
             {
@@ -142,7 +151,7 @@
             for ( List<String> valuePage : valuePages )
             {
                 DataQueryParams subQuery = new DataQueryParams( query );
-                subQuery.setDimension( dimension, valuePage );
+                subQuery.resetDimensionOrFilter( dimension, valuePage );
                 subQueries.add( subQuery );
             }
         }
@@ -242,6 +251,41 @@
     }
     
     /**
+     * Replaces the period filter with individual filters for each period type.
+     */
+    private List<DataQueryParams> setFilterByPeriodType( List<DataQueryParams> queries )
+    {
+        for ( DataQueryParams params : queries )
+        {
+            if ( params.getFilterPeriods() != null && !params.getFilterPeriods().isEmpty() )
+            {
+                params.getFilters().putAll( getPeriodTypePeriodMap( params.getFilterPeriods() ) );
+                params.getFilters().remove( DataQueryParams.PERIOD_DIM_ID );
+            }
+        }
+        
+        return queries;
+    }
+    
+    /**
+     * Replaces the organisation unit filter with individual filters for each
+     * organisation unit level.
+     */
+    private List<DataQueryParams> setFilterByOrgUnitLevel( List<DataQueryParams> queries )
+    {
+        for ( DataQueryParams params : queries )
+        {
+            if ( params.getFilterOrganisationUnits() != null && !params.getFilterOrganisationUnits().isEmpty() )
+            {
+                params.getFilters().putAll( getLevelColumnOrgUnitMap( params.getFilterOrganisationUnits() ) );
+                params.getFilters().remove( DataQueryParams.ORGUNIT_DIM_ID );
+            }
+        }
+        
+        return queries;
+    }
+    
+    /**
      * Creates a mapping between period type name and period for the given periods.
      */
     private ListMap<String, String> getPeriodTypePeriodMap( Collection<String> isoPeriods )
@@ -275,4 +319,22 @@
         
         return map;
     }
+    
+    /**
+     * Creates a mapping between the level column and organisation unit for the 
+     * given organisation units.
+     */
+    private ListMap<String, String> getLevelColumnOrgUnitMap( Collection<String> orgUnits )
+    {
+        ListMap<String, String> map = new ListMap<String, String>();
+        
+        for ( String orgUnit : orgUnits )
+        {
+            int level = organisationUnitService.getLevelOfOrganisationUnit( orgUnit );
+            
+            map.putValue( DataQueryParams.LEVEL_PREFIX + level, orgUnit );
+        }
+        
+        return map;
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java	2012-12-21 12:59:39 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java	2012-12-21 16:50:36 +0000
@@ -84,6 +84,11 @@
         {
             sql += sqlHelper.whereAnd() + " " + dim + " in (" + getQuotedCommaDelimitedString( dimensionMap.get( dim ) ) + " ) ";
         }
+
+        for ( String filter : params.getFilterNames() )
+        {
+            sql += sqlHelper.whereAnd() + " " + filter + " in (" + getQuotedCommaDelimitedString( params.getFilters().get( filter ) ) + " ) ";
+        }
         
         sql += "group by " + getCommaDelimitedString( dimensions );
     

=== 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-21 14:12:04 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/data/QueryPlannerTest.java	2012-12-21 16:50:36 +0000
@@ -246,6 +246,42 @@
 
         queryPlanner.planQuery( params, 4 );
     }
+
+    /**
+     * Query filters span 2 partitions. Splits in 2 queries for each partition, 
+     * then splits in 2 queries on organisation units to satisfy optimal for a 
+     * total of 4 queries.
+     */
+    @Test
+    public void planQueryH()
+    {
+        DataQueryParams params = new DataQueryParams();
+        params.setDataElements( Arrays.asList( "a", "b", "c", "d" ) );
+        params.setOrganisationUnits( Arrays.asList( ouA.getUid(), ouB.getUid(), ouC.getUid(), ouD.getUid(), ouE.getUid() ) );
+        params.setFilterPeriods( Arrays.asList( "2000Q1", "2000Q2", "2000Q3", "2000Q4", "2001Q1", "2001Q2" ) );
+        
+        List<DataQueryParams> queries = queryPlanner.planQuery( params, 4 );
+        
+        assertEquals( 4, queries.size() );
+    }
+
+    /**
+     * Query spans 3 period types. Splits in 3 queries for each period type, then
+     * splits in 2 queries on data elements units to satisfy optimal for a total 
+     * of 6 queries.
+     */
+    @Test
+    public void planQueryI()
+    {
+        DataQueryParams params = new DataQueryParams();
+        params.setDataElements( Arrays.asList( "a", "b", "c", "d" ) );
+        params.setFilterOrganisationUnits( Arrays.asList( ouA.getUid(), ouB.getUid(), ouC.getUid(), ouD.getUid(), ouE.getUid() ) );
+        params.setPeriods( Arrays.asList( "2000Q1", "2000Q2", "2000", "200002", "200003", "200004" ) );
+        
+        List<DataQueryParams> queries = queryPlanner.planQuery( params, 4 );
+        
+        assertEquals( 6, queries.size() );
+    }
     
     // -------------------------------------------------------------------------
     // Supportive methods