From cfa9857f8520c255a9d08fecdd15da1f47e66370 Mon Sep 17 00:00:00 2001
From: TonyPa <apaschenko@inuits.eu>
Date: Mon, 23 Jan 2012 13:11:44 +0200
Subject: [PATCH] Issue #1327964: added Entity API integration

---
 search_api_multi.info               |    1 +
 search_api_multi.module             |   20 ++++-
 search_api_multi.query.inc          |    2 +-
 service.inc                         |  181 +++++++++++++++++++++++++++++++++++
 views/handler_argument_fulltext.inc |    6 +-
 views/handler_filter_fulltext.inc   |    4 +-
 views/query.inc                     |   74 +++++++++++++-
 views/search_api_multi.views.inc    |  128 ++++++++-----------------
 8 files changed, 315 insertions(+), 101 deletions(-)
 create mode 100644 service.inc

diff --git a/search_api_multi.info b/search_api_multi.info
index 55d917b..25889ae 100644
--- a/search_api_multi.info
+++ b/search_api_multi.info
@@ -5,6 +5,7 @@ dependencies[] = search_api
 core = 7.x
 package = Search
 
+files[] = service.inc
 files[] = search_api_multi.query.inc
 files[] = search_api_multi.service.inc
 files[] = views/handler_argument_fulltext.inc
diff --git a/search_api_multi.module b/search_api_multi.module
index ce652b9..ad9952c 100644
--- a/search_api_multi.module
+++ b/search_api_multi.module
@@ -6,7 +6,7 @@
 function search_api_multi_views_api() {
   if (module_exists('search_api_views')) {
     return array(
-      'api' => '3.0-alpha1',
+      'api' => '3.0',
       'path' => drupal_get_path('module', 'search_api_multi') . '/views',
     );
   }
@@ -146,3 +146,21 @@ function search_api_multi_current_search($search_id = NULL, SearchApiMultiQuery
   }
   return $searches;
 }
+
+/**
+ * Implements hook_search_api_service_info().
+ */
+function search_api_multi_search_api_service_info() {
+  $services['search_api_multi_solr_service'] = array(
+    'name' => t('Solr service(Multi index search support)'),
+    'description' => t('<p>Index items using an Apache Solr search server.</p>' .
+        '<ul>' . '<li>All field types are supported and indexed in a special way, with URI/String and Integer/Duration being equivalent.</li>' .
+        '<li>See <a href="@url">the Solr wiki</a> for information about the "direct" parse mode.</li>' .
+        '<li>Supports the search_api_facets and search_api_multi features.</li>' .
+        '<li>Will use internal Solr preprocessors, so Search API preprocessors should for the most part be deactivated.</li>' .
+        '<li>See the README.txt file provided with this module for details.</li>' . '</ul>',
+        array('@url' => url('http://wiki.apache.org/solr/SolrQuerySyntax'))),
+    'class' => 'SearchApiMultiSolrService',
+  );
+  return $services;
+}
\ No newline at end of file
diff --git a/search_api_multi.query.inc b/search_api_multi.query.inc
index 593e177..2c57cef 100644
--- a/search_api_multi.query.inc
+++ b/search_api_multi.query.inc
@@ -387,7 +387,7 @@ class SearchApiMultiQuery implements SearchApiMultiQueryInterface {
       throw new SearchApiException(t('There are no enabled indexes on the searched server !name.', array('!name' => $server->name)));
     }
     foreach ($this->indexes as $index) {
-      if (empty($index->options['fields'])) {
+      if (!$index->getFields()) {
         throw new SearchApiException(t("Can't search an index which hasn't got any fields defined."));
       }
       if (isset($options['parse mode'])) {
diff --git a/service.inc b/service.inc
new file mode 100644
index 0000000..8b8358b
--- /dev/null
+++ b/service.inc
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * Search service class using Solr server.
+ */
+class SearchApiMultiSolrService extends SearchApiSolrService {
+
+  /**
+   * Executes a search on the server represented by this object.
+   *
+   * @param SearchApiMultiQueryInterface $query
+   *   The search query to execute.
+   *
+   * @throws SearchApiException
+   *   If an error prevented the search from completing.
+   *
+   * @return array
+   *   An associative array containing the search results, as required by
+   *   SearchApiMultiQueryInterface::execute().
+   */
+  public function searchMultiple(SearchApiMultiQueryInterface $query) {
+    $time_method_called = microtime(TRUE);
+    // Get field information
+    $solr_fields = array(
+      'search_api_id' => 'ss_search_api_id',
+      'search_api_relevance' => 'score',
+      'search_api_multi_index' => 'index_id',
+    );
+    $fields = array(
+      'search_api_multi_index' => array(
+        'type' => 'string',
+      ),
+    );
+    foreach ($query->getIndexes() as $index_id => $index) {
+      if (empty($index->options['fields'])) {
+        continue;
+      }
+      $prefix = $index_id . ':';
+      foreach ($this->getFieldNames($index) as $field => $key) {
+        if (substr($field, 0, 11) !== 'search_api_') {
+          $solr_fields[$prefix . $field] = $key;
+        }
+      }
+      foreach ($index->options['fields'] as $field => $info) {
+        $fields[$prefix . $field] = $info;
+      }
+    }
+
+    // Extract keys
+    $keys = $query->getKeys();
+    if (is_array($keys)) {
+      $keys = $this->flattenKeys($keys);
+    }
+
+    // Set searched fields
+    $search_fields = $query->getFields();
+    $qf = array();
+    foreach ($search_fields as $f) {
+      $qf[] = $solr_fields[$f];
+    }
+
+    // Extract filters
+    $filter = $query->getFilter();
+    $fq = $this->createFilterQueries($filter, $solr_fields, $fields);
+
+    // Extract sort
+    $sort = array();
+    foreach ($query->getSort() as $f => $order) {
+      $f = $solr_fields[$f];
+      $order = strtolower($order);
+      $sort[] = "$f $order";
+    }
+
+    // Get facet fields
+    $facets = $query->getOption('search_api_facets') ? $query->getOption('search_api_facets') : array();
+    $facet_params = $this->getFacetParams($facets, $solr_fields);
+
+    // Set defaults
+    if (!$keys) {
+      $keys = NULL;
+    }
+    $options = $query->getOptions();
+    $offset = isset($options['offset']) ? $options['offset'] : 0;
+    $limit = isset($options['limit']) ? $options['limit'] : 1000000;
+
+    // Collect parameters
+    $params = array(
+      'qf' => $qf,
+      'fl' => 'item_id,index_id,score',
+      'fq' => $fq,
+    );
+    if ($sort) {
+      $params['sort'] = implode(', ', $sort);
+    }
+    if (!empty($facet_params['facet.field'])) {
+      $params += $facet_params;
+    }
+    try {
+      // Send search request
+      $time_processing_done = microtime(TRUE);
+      $this->connect();
+      $call_args = array(
+        'query'  => &$keys,
+        'offset' => &$offset,
+        'limit'  => &$limit,
+        'params' => &$params,
+      );
+      drupal_alter('search_api_solr_multi_query', $call_args, $query);
+      $response = $this->solr->search($keys, $offset, $limit, $params);
+      $time_query_done = microtime(TRUE);
+
+      if ($response->getHttpStatus() != 200) {
+        throw new SearchApiException(t('The Solr server responded with status code !status: !msg.',
+            array('!status' => $response->getHttpStatus(), '!msg' => $response->getHttpStatusMessage())));
+      }
+
+      // Extract results
+      $results = array();
+      $results['result count'] = $response->response->numFound;
+      $results['results'] = array();
+      foreach ($response->response->docs as $doc) {
+        $doc->id = $doc->item_id;
+        unset($doc->item_id);
+        foreach ($doc as $k => $v) {
+          $result[$k] = $v;
+        }
+        $results['results'][$doc->index_id . '-' . $doc->id] = $result;
+      }
+
+      // Extract facets
+      if (isset($response->facet_counts->facet_fields)) {
+        $results['search_api_facets'] = array();
+        $facet_fields = $response->facet_counts->facet_fields;
+        foreach ($facets as $delta => $info) {
+          $field = $this->getFacetField($solr_fields[$info['field']]);
+          if (!empty($facet_fields->$field)) {
+            $min_count = $info['min_count'];
+            $terms = $facet_fields->$field;
+            if ($info['missing']) {
+              // We have to correctly incorporate the "_empty_" term.
+              // This will ensure that the term with the least results is dropped, if the limit would be exceeded.
+              $terms = (array) $terms;
+              arsort($terms);
+              if (count($terms) > $info['limit']) {
+                array_pop($terms);
+              }
+            }
+            foreach ($terms as $term => $count) {
+              if ($count >= $min_count) {
+                $term = $term == '_empty_' ? '!' : '"' . $term . '"';
+                $results['search_api_facets'][$delta][] = array(
+                  'filter' => $term,
+                  'count' => $count,
+                );
+              }
+            }
+            if (empty($results['search_api_facets'][$delta]) || count($results['search_api_facets'][$delta]) <= 1) {
+              unset($results['search_api_facets'][$delta]);
+            }
+          }
+        }
+      }
+
+      // Compute performance
+      $time_end = microtime(TRUE);
+      $results['performance'] = array(
+        'complete' => $time_end - $time_method_called,
+        'preprocessing' => $time_processing_done - $time_method_called,
+        'execution' => $time_query_done - $time_processing_done,
+        'postprocessing' => $time_end - $time_query_done,
+      );
+
+      return $results;
+    }
+    catch (Exception $e) {
+      throw new SearchApiException($e->getMessage());
+    }
+  }
+
+  
+}
diff --git a/views/handler_argument_fulltext.inc b/views/handler_argument_fulltext.inc
index db106a1..f8c96c4 100644
--- a/views/handler_argument_fulltext.inc
+++ b/views/handler_argument_fulltext.inc
@@ -15,10 +15,10 @@ class SearchApiMultiHandlerArgumentFulltext extends SearchApiViewsHandlerArgumen
     $server_id = substr($this->table, 18);
     $indexes = search_api_index_load_multiple(FALSE, array('enabled' => TRUE, 'server' => $server_id));
     foreach ($indexes as $index) {
-      if (!empty($index->options['fields'])) {
+      if (!empty($index->getFields())) {
         $prefix = $index->machine_name . ':';
         $prefix_name = $index->name . ' » ';
-        $f = $index->options['fields'];
+        $f = $index->getFields();
         foreach ($index->getFulltextFields() as $name) {
           $fields[$prefix . $name] = $prefix_name . $f[$name]['name'];
         }
@@ -32,7 +32,7 @@ class SearchApiMultiHandlerArgumentFulltext extends SearchApiViewsHandlerArgumen
         '#options' => $fields,
         '#size' => min(4, count($fields)),
         '#multiple' => TRUE,
-        '#default_value' => $this->options['fields'],
+        '#default_value' => $this->getFields(),
       );
     }
     else {
diff --git a/views/handler_filter_fulltext.inc b/views/handler_filter_fulltext.inc
index 2dd1fe6..ca99287 100644
--- a/views/handler_filter_fulltext.inc
+++ b/views/handler_filter_fulltext.inc
@@ -13,10 +13,10 @@ class SearchApiMultiHandlerFilterFulltext extends SearchApiViewsHandlerFilterFul
     $server_id = substr($this->table, 18);
     $indexes = search_api_index_load_multiple(FALSE, array('enabled' => TRUE, 'server' => $server_id));
     foreach ($indexes as $index) {
-      if (!empty($index->options['fields'])) {
+      if ($index->getFields()) {
         $prefix = $index->machine_name . ':';
         $prefix_name = $index->name . ' » ';
-        $f = $index->options['fields'];
+        $f = $index->getFields();
         foreach ($index->getFulltextFields() as $name) {
           $fields[$prefix . $name] = $prefix_name . $f[$name]['name'];
         }
diff --git a/views/query.inc b/views/query.inc
index 6a65c30..3c0f570 100644
--- a/views/query.inc
+++ b/views/query.inc
@@ -45,7 +45,8 @@ class SearchApiMultiViewsQuery extends SearchApiViewsQuery {
       $id = $result['id'];
       $index_id = $result['index_id'];
       if (!isset($index_types[$index_id])) {
-        $index_types[$index_id] = search_api_index_load($index_id)->item_type;
+        $this->index[$index_id] = search_api_index_load($index_id);
+        $index_types[$index_id] = $this->index[$index_id]->item_type;
       }
       // Maybe the service class or a postprocessor already set the entities.
       if (!empty($result['entity'])) {
@@ -58,7 +59,7 @@ class SearchApiMultiViewsQuery extends SearchApiViewsQuery {
 
     // Then extract the needed fields for each involved entity type.
     $type_fields = array();
-    foreach ($this->fields as $field => $true) {
+    foreach ($this->query->getFields() as $field) {
       if (strpos($field, ':') !== FALSE) {
         list($index_id, $field) = explode(':', $field, 2);
         if (isset($index_types[$index_id])) {
@@ -100,8 +101,10 @@ class SearchApiMultiViewsQuery extends SearchApiViewsQuery {
           $key = $type . '-' . $id;
           $wrapper = $datasource->getMetadataWrapper($item);
           $view_result[$key] = (object) $this->extractFields($wrapper, $type_fields[$type]);
-          $view_result[$key]->entity = $item;
-                  }
+          $view_result[$key]->entity = $item; 
+          if (!isset($view_result[$key]->entity->type))
+          $view_result[$key]->entity->type = $type; 
+        }
         catch (Exception $e) {
           continue;
         }
@@ -122,7 +125,7 @@ class SearchApiMultiViewsQuery extends SearchApiViewsQuery {
         }
         $view->result[$key]->search_api_relevance = $result['score'];
         $view->result[$key]->search_api_multi_index = $result['index_id'];
-        foreach ($this->fields as $field => $true) {
+        foreach ($this->query->getFields() as $field => $true) {
           if (!isset($view->result[$key]->$field)) {
             $view->result[$key]->$field = '';
           }
@@ -145,4 +148,65 @@ class SearchApiMultiViewsQuery extends SearchApiViewsQuery {
     }
   }
 
+  /**
+   * Returns the according metadata wrappers for the given query results.
+   *
+   * This is necessary to support generic entity handlers and plugins with this
+   * query backend.
+   */
+  public function get_result_wrappers($results, $relationship = NULL, $field = NULL) {
+    $wrappers = array();
+    $load_entities_index = array();
+    $load_entities_types = array();
+    $fields = array();
+    foreach ($results as $row_index => $row) {
+    $fields = $this->index[$row->search_api_multi_index]->getFields();
+      if(in_array($field, array_keys($fields))){
+        $current_field_index = $this->index[$row->search_api_multi_index]->item_type;
+      }
+    $is_entity = (boolean) entity_get_info($this->index[$row->search_api_multi_index]->item_type);
+      if ($is_entity && isset($row->entity)) {
+        // If this entity isn't load, register it for pre-loading.
+        if (!is_object($row->entity)) {
+          $load_entities_index[$row->entity] = $row_index;
+          $load_entities_types[$row->entity] = $this->index[$row->search_api_multi_index]->item_type;
+        }
+
+        $wrappers[$row_index] = $this->index[$row->search_api_multi_index]->entityWrapper($row->entity);
+      }
+    }
+
+    // If the results are entities, we pre-load them to make use of a multiple
+    // load. (Otherwise, each result would be loaded individually.)
+    if (!empty($load_entities_index)) {
+      foreach ($load_entities_types as $type) {
+        $entities = entity_load($type, array_keys($load_entities_index));
+        foreach ($entities as $entity_id => $entity) {
+          $wrappers[$load_entities_index[$entity_id]] = $this->index[$row->search_api_multi_index]->entityWrapper($entity);
+        }
+      }
+    }
+
+    // Apply the relationship, if necessary.
+    $type = isset($current_field_index) ? $current_field_index : $this->index[$row->search_api_multi_index]->item_type;
+    $selector_suffix = '';
+    if ($field && ($pos = strrpos($field, ':'))) {
+      $selector_suffix = substr($field, 0, $pos);
+    }
+    if ($selector_suffix || ($relationship && !empty($this->view->relationship[$relationship]))) {
+      // Use EntityFieldHandlerHelper to compute the correct data selector for
+      // the relationship.
+      $handler = (object) array(
+        'view' => $this->view,
+        'relationship' => $relationship,
+        'real_field' => '',
+      );
+      $selector = EntityFieldHandlerHelper::construct_property_selector($handler);
+      $selector .= ($selector ? ':' : '') . $selector_suffix;
+      list($type, $wrappers) = EntityFieldHandlerHelper::extract_property_multiple($wrappers, $selector);
+    }
+
+    return array($type, $wrappers);
+  }
+  
 }
diff --git a/views/search_api_multi.views.inc b/views/search_api_multi.views.inc
index 6a370bf..24d6721 100644
--- a/views/search_api_multi.views.inc
+++ b/views/search_api_multi.views.inc
@@ -8,6 +8,7 @@
 function search_api_multi_views_data() {
   $data = array();
   $servers = array();
+  $entity_types = entity_get_info();
   foreach (search_api_index_load_multiple(FALSE) as $index) {
     if (!($server = $index->server()) || !$server->supportsFeature('search_api_multi')) {
       continue;
@@ -20,6 +21,8 @@ function search_api_multi_views_data() {
     // Base data
     $key = 'search_api_server_' . $server->machine_name;
     $table = &$data[$key];
+    $type_info = search_api_get_item_type_info($index->item_type);
+    $table['table']['group'] = t('Indexed @entity_type', array('@entity_type' => $type_info['name']));
     $table['table']['base'] = array(
       'field' => 'search_api_id',
       'index' => $server->machine_name,
@@ -27,90 +30,26 @@ function search_api_multi_views_data() {
       'help' => t('Use search indexes on the %name search server for filtering and retrieving data.', array('%name' => $server->name)),
       'query class' => 'search_api_multi_query',
     );
+    if (isset($entity_types[$index->item_type])) {
+      $table['table'] += array(
+        'entity type' => $index->item_type,
+        'skip entity load' => TRUE,
+      );
+    }
 
-    // Add all available fields
-    // This is largely copied from _search_api_admin_get_fields().
-    $max_depth = variable_get('search_api_multi_max_fields_depth', 2);
-    $orig_wrapper = $index->entityWrapper(NULL, FALSE);
-    $fields = empty($index->options['fields']) ? array() : $index->options['fields'];
-
-    // A wrapper for a specific field name prefix, e.g. 'user:' mapped to the user wrapper
-    $wrappers = array('' => $orig_wrapper);
-    // Display names for the prefixes
-    $prefix_names = array('' => '');
-    // The list nesting level for entities with a certain prefix
-    $nesting_levels = array('' => 0);
-
-    $types = search_api_field_types();
-    $types['options'] = t('Options');
-    while ($wrappers) {
-      foreach ($wrappers as $prefix => $wrapper) {
-        $prefix_name = $prefix_names[$prefix];
-        $depth = substr_count($prefix, ':');
-        // Deal with lists.
-        $nesting_level = $nesting_levels[$prefix];
-        $type_prefix = str_repeat('list<', $nesting_level);
-        $type_suffix = str_repeat('>', $nesting_level);
-        if ($nesting_level) {
-          $info = $wrapper->info();
-          // The real nesting level of the wrapper, not the accumulated one.
-          $level = search_api_list_nesting_level($info['type']);
-          for ($i = 0; $i < $level; ++$i) {
-            $wrapper = $wrapper[0];
-          }
-        }
-        // Now look at all properties.
-        foreach ($wrapper as $property => $value) {
-          $info = $value->info();
-          $type = $type_prefix . $info['type'] . $type_suffix;
-          $inner_type = search_api_extract_inner_type($info['type']);
-          if ($inner_type == 'token') {
-            $inner_type = 'string';
-            $type = search_api_nest_type('string', $type);
-          }
-          $key = $index->machine_name . ':' . $prefix . $property;
-          if (isset($types[$inner_type])) {
-            if ($value->optionsList()) {
-              $inner_type = 'options';
-              $type = search_api_nest_type('options', $type);
-            }
-            // Add field handler.
-            $table[$key]['group'] = $prefix_name ? $index->name . ' » ' . $prefix_name : $index->name;
-            $table[$key]['title'] = $info['label'];
-            $table[$key]['help'] = empty($info['description']) ? t('(No information available)') : $info['description'];
-            $table[$key]['type'] = $type;
-            $table[$key]['field']['handler'] = _search_api_views_field_handler($type, $inner_type);
-            if ($inner_type == 'options') {
-              $table[$key]['field']['options'] = $value->optionsList();
-            }
-
-            // If field is indexed, also add additional handlers.
-            if (!empty($fields[$prefix . $property])) {
-              // Discern between original and indexed type
-              $table[$key]['field']['type'] = $table[$key]['type'];
-              $table[$key]['type'] = $fields[$prefix . $property]['type'];
-              $table[$key] += _search_api_views_add_handlers($fields[$prefix . $property], $value);
-              if (!empty($table[$key]['sort'])) {
-                $table[$key]['field']['click sortable'] = TRUE;
-              }
-            }
-            unset($fields[$prefix . $property]);
-          }
-          elseif ($depth < $max_depth) {
-            // Visit this entity/struct in a later iteration.
-            $key = $prefix . $property . ':';
-            $wrappers[$key] = $value;
-            $prefix_names[$key] = $prefix_name ? $prefix_name . ' » ' . $info['label'] : $info['label'];
-            $nesting_levels[$key] = search_api_list_nesting_level($type);
-          }
-        }
-        unset($wrappers[$prefix]);
+    $wrapper = $index->entityWrapper(NULL, TRUE);
+   
+    // Add field handlers and relationships provided by the Entity API.
+    foreach ($wrapper as $key => $property) {
+      $info = $property->info();
+      if ($info) {
+        entity_views_field_definition($key, $info, $table);
       }
     }
 
     // Add handlers for all indexed fields which weren't processed yet.
-    foreach ($fields as $key => $field) {
-      $tmp = $orig_wrapper;
+    foreach ($index->getFields() as $key => $field) {
+      $tmp = $wrapper;
       $group = '';
       $name = $index->name;
       $parts = explode(':', $key);
@@ -119,7 +58,7 @@ function search_api_multi_views_data() {
           continue 2;
         }
         $tmp = $tmp->$part;
-        $info = $tmp->info();
+         $info = $tmp->info();
         $group = ($group ? $group . ' » ' . $name : ($name ? $name : ''));
         $name = $info['label'];
         if ($i < count($parts) - 1) {
@@ -130,22 +69,33 @@ function search_api_multi_views_data() {
           }
         }
       }
-      $key = $index->machine_name . ':' . $key;
+      $id = _entity_views_field_identifier($key, $table);
       if ($group) {
-        $table[$key]['group'] = $group;
+        // @todo Entity type label instead of $group?
+        $table[$id]['group'] = $group;
+        $name = t('!field (indexed)', array('!field' => $name));
       }
-      $table[$key]['title'] = $name;
-      $table[$key]['help'] = empty($info['description']) ? t('(No information available)') : $info['description'];
-      $table[$key]['type'] = $field['type'];
-      $table[$key] += _search_api_views_add_handlers($field, $tmp);
+      $table[$id]['title'] = $name;
+      $table[$id]['help'] = empty($info['description']) ? t('(No information available)') : $info['description'];
+      $table[$id]['type'] = $field['type'];
+      if ($id != $key) {
+        $table[$id]['real field'] = $key;
+      }
+      _search_api_views_add_handlers($key, $field, $tmp, $table);
     }
 
     // Special handlers
+    $table['search_api_language']['filter']['handler'] = 'SearchApiViewsHandlerFilterLanguage';
+
+    $table['search_api_id']['title'] = t('Entity ID');
+    $table['search_api_id']['help'] = t("The entity's ID.");
+    $table['search_api_id']['sort']['handler'] = 'SearchApiViewsHandlerSort';
+
     $table['search_api_relevance']['group'] = t('Search');
     $table['search_api_relevance']['title'] = t('Relevance');
     $table['search_api_relevance']['help'] = t('The relevance of this search result with respect to the query.');
-    $table['search_api_relevance']['type'] = 'decimal';
-    $table['search_api_relevance']['field']['handler'] = _search_api_views_field_handler('decimal', 'decimal');
+    $table['search_api_relevance']['field']['type'] = 'decimal';
+    $table['search_api_relevance']['field']['handler'] = 'entity_views_handler_field_numeric';
     $table['search_api_relevance']['field']['click sortable'] = TRUE;
     $table['search_api_relevance']['sort']['handler'] = 'SearchApiViewsHandlerSort';
 
@@ -164,7 +114,7 @@ function search_api_multi_views_data() {
     $table['search_api_multi_index']['title'] = t('Index');
     $table['search_api_multi_index']['help'] = t('The search indexes that will be searched.');
     $table['search_api_multi_index']['type'] = 'options';
-    $table['search_api_multi_index']['field']['handler'] = _search_api_views_field_handler('options', 'options');
+    $table['search_api_multi_index']['field']['handler'] = 'entity_views_handler_field_options';
     $table['search_api_multi_index']['field']['options'] = $indexes;
     $table['search_api_multi_index']['argument']['handler'] = 'SearchApiViewsHandlerArgument';
     $table['search_api_multi_index']['filter']['handler'] = 'SearchApiViewsHandlerFilterOptions';
-- 
1.7.5.4

