Browse Source

Added the ability with webservices to specify exactly the entity IDs to return in bulk, as well as the fields that should be present

Stephen Ficklin 7 years ago
parent
commit
2d279d7ef0

+ 9 - 3
tripal_chado/includes/TripalFields/sbo__relationship/sbo__relationship.inc

@@ -328,9 +328,15 @@ class sbo__relationship extends ChadoField {
     // Example: The genetic_marker, MARKER1 , derives from the sequence_variant, VARIANT1.
     // The above relationship will be shown both on marker and variant pages
     // and as such both subject and object names need to be shown.
-    $entity->{$field_name}['und'][$delta]['value']['SIO:000493'] = 'The ' . $subject_type . ', ' .
-      $subject_name . ', ' . $verb . ' '  . $rel_type_clean . ' the '  .
-      $object_type . ', ' . $object_name . '.';
+    $clause = 'The ' . $subject_type . ', ' .
+        $subject_name . ', ' . $verb . ' '  . $rel_type_clean . ' the '  .
+        $object_type . ', ' . $object_name . '.';
+    $entity->{$field_name}['und'][$delta]['value']['SIO:000493'] = $clause;
+
+    // Adding a label allows us to provide a single text value for the
+    // entire field. It is this text value that can be used in tab/csv
+    // downloaders.
+    $entity->{$field_name}['und'][$delta]['value']['rdfs:label'] = $clause;
 
     $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__' . $pkey] = $relationship->$pkey;
     $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__' . $subject_id_key] = $relationship->$subject_id_key->$subject_pkey;

+ 50 - 0
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -601,6 +601,56 @@ function tripal_chado_field_storage_query($query) {
     $cquery->condition('TE.' . $condition['column'], $condition['value']);
   }
 
+  // If there is an entity_id filter then apply that.
+  if (array_key_exists('entity_id', $query->entityConditions)) {
+    $value = $query->entityConditions['entity_id']['value'];
+    $op = '';
+    if (array_key_exists('op', $query->entityConditions['entity_id'])) {
+      $op = $query->entityConditions['entity_id']['op'];
+    }
+    // Handle a single entity_id
+    if (!is_array($value)) {
+      switch ($op) {
+        case 'CONTAINS':
+          $op = 'LIKE';
+          $value = '%' . $value . '%';
+          break;
+        case 'STARTS_WITH':
+          $op = 'LIKE';
+          $value = $value . '%';
+          break;
+        case 'BETWEEN':
+          // This case doesn't make sense for a single value. So, just set it
+          // equal to the end.
+          $op = '=';
+        default:
+          $op = $op ? $op : '=';
+      }
+    }
+    // Handle multiple entitie IDs.
+    else {
+      switch ($op) {
+        case 'CONTAINS':
+          $op = 'IN';
+          break;
+        case 'STARTS_WITH':
+          $op = 'IN';
+          break;
+        case 'BETWEEN':
+          $op = 'BETWEEN';
+        default:
+          $op = 'IN';
+      }
+    }
+    if ($op == 'BETWEEN') {
+      $cquery->condition('TE.id', $value[0], '>');
+      $cquery->condition('TE.id', $value[1], '<');
+    }
+    else {
+      $cquery->condition('TE.id', $value, $op);
+    }
+  }
+
   // Now set any ordering.
   foreach ($query->order as $index => $sort) {
     // Add in property ordering.

+ 77 - 34
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -86,7 +86,7 @@ class TripalContentService_v0_1 extends TripalWebService {
     // If we have a content type then list all of the entities that belong
     // to it.
     if ($ctype and !$entity_id and !$expfield) {
-      $this->doContentTypeList($ctype);
+      $this->doEntityList($ctype);
     }
     // If we have an entity ID then build the resource for a single entity.
     else if ($ctype and $entity_id and !$expfield) {
@@ -97,7 +97,7 @@ class TripalContentService_v0_1 extends TripalWebService {
     }
     // Otherwise just list all of the available content types.
     else {
-      $this->doAllTypesList();
+      $this->doContentTypesList();
     }
   }
 
@@ -142,7 +142,7 @@ class TripalContentService_v0_1 extends TripalWebService {
           FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
     }
 
-    $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path, $expfield);
+    $this->addEntityField($this->resource, $term, $entity, $bundle, $field, $instance, $service_path, $expfield);
   }
 
   /**
@@ -163,7 +163,13 @@ class TripalContentService_v0_1 extends TripalWebService {
       $vocabulary = $instance['settings']['term_vocabulary'];
       $accession = $instance['settings']['term_accession'];
       $temp_term = tripal_get_term_details($vocabulary, $accession);
-      if ($temp_term['name'] == $expfield) {
+      // See if the name provided matches the field name after a bit of
+      // cleanup.
+      if (strtolower(preg_replace('/[^\w]/', '_', $temp_term['name'])) == strtolower($expfield)) {
+        return array($field, $instance, $temp_term);
+      }
+      // Alternatively if the CV term accession matches then we're good too.
+      if ($vocabulary . ':' . $accession == $expfield) {
         return array($field, $instance, $temp_term);
       }
     }
@@ -208,7 +214,7 @@ class TripalContentService_v0_1 extends TripalWebService {
     $this->addResourceProperty($this->resource, $itemPage, url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
 
     // Add in the entitie's fields.
-    $this->addEntityFields($entity, $bundle, $term, $service_path);
+    $this->addEntityFields($this->resource, $entity, $bundle, $term, $service_path);
   }
 
   /**
@@ -237,7 +243,7 @@ class TripalContentService_v0_1 extends TripalWebService {
   /**
    * Adds the fields as properties of an entity resource.
    */
-  private function addEntityFields($entity, $bundle, $term, $service_path) {
+  private function addEntityFields($resource, $entity, $bundle, $term, $service_path) {
 
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
@@ -296,34 +302,33 @@ class TripalContentService_v0_1 extends TripalWebService {
         // that information.
         $items = field_get_items('TripalEntity', $entity, $field_name);
         $term_key = $this->getContextTerm($term, array('lowercase', 'spacing'));
-        $this->resource->addContextItem($term_key, $vocabulary . ':' . $accession);
-        $this->resource->addContextItem($vocabulary . ':' . $accession, array(
+        $resource->addContextItem($term_key, $vocabulary . ':' . $accession);
+        $resource->addContextItem($vocabulary . ':' . $accession, array(
           '@id' => $term['url'],
           '@type' => '@id'
         ));
         if ($items and count($items) > 0 and $items[0]['value']) {
-         $this->addResourceProperty($this->resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']), array('lowercase', 'spacing'));
+          $this->addResourceProperty($resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']), array('lowercase', 'spacing'));
         }
         else {
           if ($hide_fields == 'show') {
-            $this->addResourceProperty($this->resource, $term, NULL, array('lowercase', 'spacing'));
+            $this->addResourceProperty($resource, $term, NULL, array('lowercase', 'spacing'));
           }
         }
         continue;
       }
 
       // Get the details for this field for the JSON-LD response.
-      $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path);
+      $this->addEntityField($resource, $term, $entity, $bundle, $field, $instance, $service_path);
     }
   }
 
   /**
    * Adds the field as a property of the entity resource.
    */
-  private function addEntityField($term, $entity, $bundle, $field, $instance,
+  private function addEntityField($resource, $term, $entity, $bundle, $field, $instance,
       $service_path, $expfield = NULL) {
 
-
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
     $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
@@ -346,11 +351,11 @@ class TripalContentService_v0_1 extends TripalWebService {
     $values = array();
     for ($i = 0; $i < count($items); $i++) {
       if (array_key_exists('value', $items[$i])) {
-        $values[$i] = $this->sanitizeFieldKeys($this->resource, $items[$i]['value'], $bundle, $service_path);
+        $values[$i] = $this->sanitizeFieldKeys($resource, $items[$i]['value'], $bundle, $service_path);
       }
       elseif ($field['type'] == 'image') {
         $url = file_create_url($items[$i]['uri']);
-        $values[$i] = $this->sanitizeFieldKeys($this->resource, $url, $bundle, $service_path);
+        $values[$i] = $this->sanitizeFieldKeys($resource, $url, $bundle, $service_path);
       }
       else {
         // TODO: handle this case.
@@ -369,17 +374,17 @@ class TripalContentService_v0_1 extends TripalWebService {
       if (is_array($values[0])) {
         if ($expfield) {
           foreach ($values[0] as $k => $v) {
-            $this->resource->addProperty($k, $v);
+            $resource->addProperty($k, $v);
           }
         }
         else {
-          $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
+          $this->addResourceProperty($resource, $term, $values[0], array('lowercase', 'spacing'));
         }
       }
       // If the value is not an array it's a scalar so add it as is to the
       // response.
       else {
-        $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
+        $this->addResourceProperty($resource, $term, $values[0], array('lowercase', 'spacing'));
       }
     }
 
@@ -397,7 +402,7 @@ class TripalContentService_v0_1 extends TripalWebService {
         $member->setID($i);
         // Add the context of the parent resource because all of the keys
         // were santizied and set to match the proper context.
-        $member->setContext($this->resource);
+        $member->setContext($resource);
         $this->setResourceType($member, $term);
         foreach ($element as $key => $value) {
           $member->addProperty($key, $value);
@@ -406,11 +411,11 @@ class TripalContentService_v0_1 extends TripalWebService {
         $i++;
       }
       if ($expfield) {
-        $this->resource = $response;
+        $resource = $response;
       }
       else {
         //$this->resource->addProperty($key, $response);
-        $this->addResourceProperty($this->resource, $term, $response, array('lowercase', 'spacing'));
+        $this->addResourceProperty($resource, $term, $response, array('lowercase', 'spacing'));
       }
     }
   }
@@ -684,20 +689,15 @@ class TripalContentService_v0_1 extends TripalWebService {
    *
    * @throws Exception
    */
-  private function getFilters($field_mapping, $bundle) {
+  private function getFieldFilters($field_mapping, $bundle) {
     $filters = array();
 
     // Iterate through the paramter list provided by user.
     foreach ($this->params as $param => $value) {
 
       // Ignore non filter parameters.
-      if ($param == 'page' or $param == 'limit') {
-        continue;
-      }
-
-      // Ignore the order parameter as that is handled by the getOrderBy()
-      // function
-      if ($param == 'order') {
+      if ($param == 'page' or $param == 'limit' or $param == 'order' or
+          $param == 'ids' or $param == 'fields') {
         continue;
       }
 
@@ -804,7 +804,7 @@ class TripalContentService_v0_1 extends TripalWebService {
   /**
    * Creates a collection of resources for a given type.
    */
-  private function doContentTypeList($ctype) {
+  private function doEntityList($ctype) {
     $service_path = $this->getServicePath() . '/' . preg_replace('/[^\w]/', '_', $ctype);
     $this->resource = new TripalWebServiceCollection($service_path, $this->params);
 
@@ -831,23 +831,34 @@ class TripalContentService_v0_1 extends TripalWebService {
     $field_mapping = $this->getFieldMapping($bundle);
 
     // Get arrays for filters and order by statements.
-    $filters = $this->getFilters($field_mapping, $bundle);
+    $filters = $this->getFieldFilters($field_mapping, $bundle);
     $order_by = $this->getOrderBy($field_mapping, $bundle);
 
-    // Initialize the query to search for records for out bundle type
+    // Initialize the query to search for records for our bundle types
     // that are published.
     $query = new TripalFieldQuery();
     $query->entityCondition('entity_type', 'TripalEntity');
     $query->entityCondition('bundle', $bundle->name);
     $query->propertyCondition('status', 1);
 
+    if (array_key_exists('ids', $this->params)) {
+      $eids = explode(',', $this->params['ids']);
+      if (count($eids) > 1000) {
+        throw new Exception('Please provide no more than 1000 ids.');
+      }
+      if (!is_numeric(implode('', $eids))) {
+        throw new Exception('All supplied ids must be numeric.');
+      }
+      $query->entityCondition('entity_id', $eids, 'IN');
+    }
+
     // Now iterate through the filters and add those.
     foreach ($filters as $key_field_name => $key_filters) {
       foreach ($key_filters as $i => $filter) {
          $column_name = $filter['column'];
          $value = $filter['value'];
          $op = $filter['op'];
-        $query->fieldCondition($key_field_name, $column_name, $value, $op);
+         $query->fieldCondition($key_field_name, $column_name, $value, $op);
       }
     }
 
@@ -891,6 +902,26 @@ class TripalContentService_v0_1 extends TripalWebService {
       $entity_ids = $results['TripalEntity'];
     }
 
+    // If the user wants to include any fields in the list then those provided
+    // names need to be converted to fields.
+    $add_fields = array();
+    $add_field_ids = array();
+    if (array_key_exists('fields', $this->params)) {
+      $fields = explode(',', $this->params['fields']);
+      foreach ($fields as $expfield) {
+        list($field, $instance, $temp_term) = $this->findField($bundle, $expfield);
+        if ($field) {
+          $add_fields[$expfield]['field'] = $field;
+          $add_fields[$expfield]['instance'] = $instance;
+          $add_fields[$expfield]['term'] = $temp_term;
+          $add_field_ids[] = $field['id'];
+        }
+        else {
+          throw new Exception(t('The field named, "!field", does not exist.', array('!field' => $expfield)));
+        }
+      }
+    }
+
     // Iterate through the entities and add them to the output list.
     foreach ($entity_ids as $entity_id => $stub) {
       // We don't need all of the attached fields for an entity so, we'll
@@ -910,6 +941,18 @@ class TripalContentService_v0_1 extends TripalWebService {
       $this->setResourceType($member, $term);
       $this->addResourceProperty($member, $label, $entity->title);
       $this->addResourceProperty($member, $itemPage, url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
+
+      $entity = tripal_load_entity('TripalEntity', array($entity_id), FALSE, $add_field_ids);
+      $entity = $entity[$entity_id];
+
+      // Add in any requested fields
+      foreach ($fields as $expfield) {
+        if (array_key_exists($expfield, $add_fields)) {
+          $this->addEntityField($member, $add_fields[$expfield]['term'], $entity,
+              $bundle, $add_fields[$expfield]['field'], $add_fields[$expfield]['instance'],
+              $service_path);
+        }
+      }
       $this->resource->addMember($member);
     }
   }
@@ -917,7 +960,7 @@ class TripalContentService_v0_1 extends TripalWebService {
   /**
    * Creates a resources that contains the list of content types.
    */
-  private function doAllTypesList() {
+  private function doContentTypesList() {
     $service_path = $this->getServicePath();
     $service_vocab = new TripalDocService_v0_1($this->base_path);
     $this->resource = new TripalWebServiceCollection($service_path, $this->params);