Browse Source

Made aggregate join handler work for joins where one table is already aggregated

Lacey Sanderson 13 years ago
parent
commit
344df7aa51
1 changed files with 171 additions and 74 deletions
  1. 171 74
      base/tripal_views/views/handlers/views_handler_join_chado_aggregator.inc

+ 171 - 74
base/tripal_views/views/handlers/views_handler_join_chado_aggregator.inc

@@ -10,7 +10,9 @@
         $data['analysisprop']['table']['join']['analysis'] = array(
           'left_field' => 'analysis_id',
           'field' => 'analysis_id',
-          'handler' => 'views_handler_join_chado_aggregator'
+          'handler' => 'views_handler_join_chado_aggregator',
+          'pre-aggregated' => TRUE | FALSE //whether the table is already aggregated (contains arrays)
+          'table_aggregated' => CURRENT | LEFT //the table which has many records for each record in the other
         );
  *   @endcode
  */
@@ -39,95 +41,174 @@ class views_handler_join_chado_aggregator extends views_join {
    */
   function join($table, &$query) {
     $output = array();
-
+    
     // Create the table SQL (used in join) -------
     // query creating one-to-one table using array_agg
-    $table_desc = module_invoke_all('chado_'.$this->definition['table'].'_schema');
-    $select_fields[ $this->definition['table'] ] = $table_desc['fields'];
     
-    // Add joins to tables with a foreign key in this table
-    // (ie: add join to cvterm if this table has a type_id
-    $joins = array();
-    foreach($table_desc['foreign keys'] as $defn) {
-      if ($defn['table'] != $this->left_table) {
-        foreach( $defn['columns'] as $left => $right) {
-          $left = $this->definition['table'] .'.'. $left;
-          $right = $defn[table] .'.'. $right;
-          $joins[] = "LEFT JOIN $defn[table] $defn[table] ON $left=$right";
+    // Only aggregate each field if it the join table hadn't been pre-aggregated
+    // Example where it might be pre-aggregated: Materialized view
+    if (!$this->definition['pre-aggregated']) {
+      // Determine Order BY's for aggregates
+      $order_by = array();
+      if (!is_array($this->sort)) { $this->sort = array(); }
+      foreach ($this->sort as $s) {
+        $order_by[] = $s['table'].'.'.$s['field'].' '.$s['order'];
+      }
+        
+      // get table description (if defined via schema api)
+      $table_desc = module_invoke_all('chado_'.$this->definition['table'].'_schema');
+      $select_fields[ $this->definition['table'] ] = $table_desc['fields'];
+      
+      if (!empty($table_desc)) {
+        // Add joins to tables with a foreign key in this table
+        // (ie: add join to cvterm if this table has a type_id
+        $joins = array();
+        foreach($table_desc['foreign keys'] as $defn) {
+          if ($defn['table'] != $this->left_table) {
+            foreach( $defn['columns'] as $left => $right) {
+              $left = $this->definition['table'] .'.'. $left;
+              $right = $defn[table] .'.'. $right;
+              $joins[] = "LEFT JOIN $defn[table] $defn[table] ON $left=$right";
+            }
+            
+            // Fields to be selected from joined table
+            $join_table = module_invoke_all('chado_'.$defn['table'].'_schema');
+            $select_fields[ $defn['table'] ] = $join_table['fields'];
+          } 
         }
         
-        // Fields to be selected from joined table
-        $join_table = module_invoke_all('chado_'.$defn['table'].'_schema');
-        $select_fields[ $defn['table'] ] = $join_table['fields'];
-      } 
-    }
-    
-    // Determine Order BY's for aggregates
-    $order_by = array();
-    foreach ($this->sort as $s) {
-      $order_by[] = $s['table'].'.'.$s['field'].' '.$s['order'];
-    }
-    
-    
-    // Fields to be selected
-    foreach ($select_fields as $table => $table_fields) {
-      foreach ($table_fields as $fname => $f) {
-        $alias = '';
-        if ($table != $this->definition['table']) {
-          $alias = $table .'_';
+        // Fields to be selected
+        foreach ($select_fields as $table => $table_fields) {
+          foreach ($table_fields as $fname => $f) {
+            $alias = '';
+            if ($table != $this->definition['table']) {
+              $alias = $table .'_';
+            }
+            
+            if ($fname != $this->definition['field']) {
+              // Add sort to aggregate field if postgreSQL 9.0+
+              if ($this->postgresql_9up && !empty($order_by)) {
+                $fields[] = 'array_agg('.$table.'.'.$fname.' ORDER BY '.implode(',',$order_by).') as '.$alias.$fname;
+              } else {
+                $fields[] = 'array_agg('.$table.'.'.$fname.') as '.$alias.$fname;
+              }
+              $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
+            } else {
+              $fields[] = $fname;
+              $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
+            }
+          }
         }
+      
+      // There is no definition in schema api
+      // then use postgresql select
+      } else {
         
-        if ($fname != $this->definition['field']) {
-          // Add sort to aggregate field if postgreSQL 9.0+
-          if ($this->postgresql_9up && !empty($order_by)) {
-            $fields[] = 'array_agg('.$table.'.'.$fname.' ORDER BY '.implode(',',$order_by).') as '.$alias.$fname;
+        // No known foreign key reelationships
+        $joins = array();
+        
+        // Fields to be selected
+        $sql = "SELECT 
+                  attname as column, 
+                  format_type(atttypid, atttypmod) as datatype 
+                FROM pg_attribute, pg_type 
+                WHERE typname='nd_genotype_experiment' 
+                  AND attrelid=typrelid 
+                  AND attname NOT IN ('cmin','cmax','ctid','oid','tableoid','xmin','xmax')";
+        $previous_db = tripal_db_set_active('chado');
+        $resource = db_query($sql);
+        tripal_db_set_active($previous_db);
+        while ($r = db_fetch_object($resource)) {
+          $table = $this->definition['table'];
+          $alias = ''; //no alias needed if table is current table (only option if no schema api definition)
+          $fname = $r->column;
+          
+          if ($fname != $this->definition['field']) {
+            // Add sort to aggregate field if postgreSQL 9.0+
+            if ($this->postgresql_9up && !empty($order_by)) {
+              $fields[] = 'array_agg('.$table.'.'.$fname.' ORDER BY '.implode(',',$order_by).') as '.$alias.$fname;
+            } else {
+              $fields[] = 'array_agg('.$table.'.'.$fname.') as '.$alias.$fname;
+            }
+            $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
           } else {
-            $fields[] = 'array_agg('.$table.'.'.$fname.') as '.$alias.$fname;
+            $fields[] = $fname;
+            $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
           }
-          $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
-        } else {
-          $fields[] = $fname;
-          $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
         }
+        
       }
-    }
+      
+      // composite field 
+      // (combines all other fields before aggregating)
+      // Add sort to aggregate field if postgreSQL 9.0+
+      if ($this->postgresql_9up && !empty($order_by)) {
+        $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}' ORDER BY ".implode(',',$order_by).") as all";
+      } else {
+        $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}') as all";
+      }
+      $fields[] = $composite_field;
+        
+      // SQL to use in the join
+      $sql = 'SELECT '.implode(', ',$fields)
+        .' FROM '.$this->definition['table']
+        .' '.implode(' ',$joins);
+   
+      if (!empty($this->filter)) {
+        $sql .= ' WHERE '.implode(', ', $this->filter);
+      }
+      
+      $sql .= ' GROUP BY '.$this->definition['field'];
+      
+      // Create the join (full SQL) ----------------
+      $output[] = $this->create_single_join(
+        $query,
+        array(
+          'table' => $this->definition['table'],
+          'field' => $this->definition['field'],
+          'table_sql' => $sql,
+          'is_drupal' => FALSE,
+        ),
+        array(
+          'table' => $this->definition['left_table'],
+          'field' => $this->definition['left_field'],
+        ),
+        'LEFT'
+      );
     
-    // composite field 
-    // (combines all other fields before aggregating)
-    // Add sort to aggregate field if postgreSQL 9.0+
-    if ($this->postgresql_9up && !empty($order_by)) {
-      $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}' ORDER BY ".implode(',',$order_by).") as all";
+    // Otherwise the table has been pre-aggregated
+    // Then only need to do a regular join with any in where
     } else {
-      $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}') as all";
-    }
-    $fields[] = $composite_field;
-    
-    // SQL to use in the join
-    $sql = 'SELECT '.implode(', ',$fields)
-      .' FROM '.$this->definition['table']
-      .' '.implode(' ',$joins);
- 
-    if (!empty($this->filter)) {
-      $sql .= ' WHERE '.implode(', ', $this->filter);
-    }
-    
-    $sql .= ' GROUP BY '.$this->definition['field'];
-    
-    // Create the join (full SQL) ----------------
-    $output[] = $this->create_single_join(
-      $query,
-      array(
+
+      // Create the join
+
+      $current_table_spec = array(
         'table' => $this->definition['table'],
         'field' => $this->definition['field'],
-        'table_sql' => $sql,
         'is_drupal' => FALSE,
-      ),
-      array(
+      );
+      $left_table_spec = array(
         'table' => $this->definition['left_table'],
         'field' => $this->definition['left_field'],
-      ),
-      'LEFT'
-    );
+      );
+      
+      switch ($this->definition['table_aggregated']) {
+        default:
+        case 'CURRENT':
+          $current_table_spec['pre-aggregated'] = TRUE;
+        break;
+        case 'LEFT':
+          $left_table_spec['pre-aggregated'] = TRUE;
+        break;
+      }
+      
+      $output[] = $this->create_single_join(
+        $query,
+        $current_table_spec,
+        $left_table_spec,
+        'LEFT'
+      );
+    }
     
     return implode("\n",$output);
   }
@@ -144,6 +225,12 @@ class views_handler_join_chado_aggregator extends views_join {
       if (!$right['alias']) { $right['alias'] = $right_spec['table']; }
       $right_field = "$right[alias].$right_spec[field]";
 
+      // Add any() around field if already aggregated
+      if ($right_spec['pre-aggregated']) {
+        $right_field = "any(".$right_field.")";
+      }
+      
+      // Add drupal { } around table 
       if ($right_spec['is_drupal']) {
         $right_table = '{'.$right_spec['table'].'}';
       } else {
@@ -159,9 +246,19 @@ class views_handler_join_chado_aggregator extends views_join {
       // This can be used if left_field is a formula or something. It should be used only *very* rarely.
       $left_field = $this->left_spec['field'];
     }
+
+    // Add any() around field if already aggregated
+    if ($left_spec['pre-aggregated']) {
+      $left_field = "any(".$left_field.")";
+    }
+    
+    // Concatenate parts together to form join sql
+    if (!empty($right_spec[table_sql])) {
+      $output = " $join_type JOIN ($right_spec[table_sql]) $right[alias] ON $left_field = $right_field";
+    } else {
+      $output = " $join_type JOIN $right_spec[table] $right[alias] ON $left_field = $right_field";
+    }
     
-    $output = " $join_type JOIN ($right_spec[table_sql]) $right[alias] ON $left_field = $right_field";
-     
     return $output;
   }