You are here

class SearchApiMultiQuery in Search API Multi-Index Searches 7

Standard implementation of SearchApiMultiQueryInterface.

If the search involves only a single server which supports the "search_api_multi" feature, the methods for this feature are used. Otherwise, generic code allows the searching of multiple indexes.

Hierarchy

Expanded class hierarchy of SearchApiMultiQuery

File

./search_api_multi.query.inc, line 323

View source
class SearchApiMultiQuery implements SearchApiMultiQueryInterface {

  /**
   * All indexes which are used in this search.
   *
   * This is first loaded with all indexes, and only restricted to the used ones
   * during preExecute().
   *
   * @var array
   */
  protected $indexes = array();

  /**
   * The indexes which are currently used in this search.
   *
   * This collects the index IDs (in the keys) of indexes as they are used in
   * the search, so the appropriate ones can be kept in $this->indexes during
   * preExecute().
   *
   * @var array
   */
  protected $used_indexes = array();

  /**
   * All indexes which are used in this search, ordered by their servers.
   *
   * The array contains server machine names mapped to an array of all their
   * searched indexes.
   *
   * @var array
   */
  protected $servers = array();

  /**
   * The search keys. If NULL, this will be a filter-only search.
   *
   * @var mixed
   */
  protected $keys;

  /**
   * The unprocessed search keys, as passed to the keys() method.
   *
   * @var mixed
   */
  protected $orig_keys;

  /**
   * The fields that will be searched for the keys.
   *
   * @var array|null
   */
  protected $fields;

  /**
   * The fields that will be searched, grouped by index.
   *
   * @var array
   */
  protected $index_fields = array();

  /**
   * The search filter associated with this query.
   *
   * @var SearchApiQueryFilterInterface
   */
  protected $filter;

  /**
   * The sort associated with this query.
   *
   * @var array
   */
  protected $sort = array();

  /**
   * Search options configuring this query.
   *
   * @var array
   */
  protected $options;

  /**
   * Flag for whether preExecute() was already called for this query.
   *
   * @var bool
   */
  protected $pre_execute = FALSE;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $options = array()) {
    if (isset($options['parse mode'])) {
      $modes = $this
        ->parseModes();
      if (!isset($modes[$options['parse mode']])) {
        throw new SearchApiException(t('Unknown parse mode: @mode.', array(
          '@mode' => $options['parse mode'],
        )));
      }
    }
    $this->options = $options + array(
      'conjunction' => 'AND',
      'parse mode' => 'terms',
      'filter class' => 'SearchApiQueryFilter',
      'search id' => __CLASS__,
    );
    $this->filter = $this
      ->createFilter('AND');
    $this->indexes = search_api_index_load_multiple(FALSE, array(
      'enabled' => TRUE,
    ));
    foreach ($this->indexes as $index_id => $index) {
      $this->servers[$index->server][$index_id] = $index;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function parseModes() {
    $modes['direct'] = array(
      'name' => t('Direct query'),
      'description' => t("Don't parse the query, just hand it to the search server unaltered. " . "Might fail if the query contains syntax errors in regard to the specific server's query syntax."),
    );
    $modes['single'] = array(
      'name' => t('Single term'),
      'description' => t('The query is interpreted as a single keyword, maybe containing spaces or special characters.'),
    );
    $modes['terms'] = array(
      'name' => t('Multiple terms'),
      'description' => t('The query is interpreted as multiple keywords seperated by spaces. ' . 'Keywords containing spaces may be "quoted". Quoted keywords must still be seperated by spaces.'),
    );
    return $modes;
  }

  /**
   * Parses the keys string according to a certain parse mode.
   *
   * @param string|array|null $keys
   *   The keys as passed to keys().
   * @param string $mode
   *   The parse mode to use. Must be one of the keys from parseModes().
   *
   * @return string|array|null
   *   The parsed keys.
   */
  protected function parseKeys($keys, $mode) {
    if ($keys == NULL || is_array($keys)) {
      return $keys;
    }
    $keys = '' . $keys;
    switch ($mode) {
      case 'direct':
        return $keys;
      case 'single':
        return array(
          '#conjunction' => $this->options['conjunction'],
          $keys,
        );
      case 'terms':
        $ret = explode(' ', $keys);
        $ret['#conjunction'] = $this->options['conjunction'];
        $quoted = FALSE;
        $str = '';
        foreach ($ret as $k => $v) {
          if (!$v) {
            continue;
          }
          if ($quoted) {
            if ($v[drupal_strlen($v) - 1] == '"') {
              $v = substr($v, 0, -1);
              $str .= ' ' . $v;
              $ret[$k] = $str;
              $quoted = FALSE;
            }
            else {
              $str .= ' ' . $v;
              unset($ret[$k]);
            }
          }
          elseif ($v[0] == '"') {
            $len = drupal_strlen($v);
            if ($len > 1 && $v[$len - 1] == '"') {
              $ret[$k] = substr($v, 1, -1);
            }
            else {
              $str = substr($v, 1);
              $quoted = TRUE;
              unset($ret[$k]);
            }
          }
        }
        if ($quoted) {
          $ret[] = $str;
        }
        return array_filter($ret);
      default:
        throw new SearchApiException(t('Unrecognized parse mode %mode.', array(
          '%mode' => $mode,
        )));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function createFilter($conjunction = 'AND', array $tags = array()) {
    $filter_class = $this->options['filter class'];
    return new $filter_class($conjunction, $tags);
  }

  /**
   * {@inheritdoc}
   */
  public function keys($keys = NULL) {
    $this->orig_keys = $keys;
    if (isset($keys)) {
      $this->keys = $this
        ->parseKeys($keys, $this->options['parse mode']);
    }
    else {
      $this->keys = NULL;
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function fields($fields = NULL) {
    $this->index_fields = array();
    if ($fields) {
      foreach ($fields as $spec) {
        list($index_id, $field) = explode(':', $spec, 2);
        $index = $this->indexes[$index_id];
        if (empty($index->options['fields'][$field]) || !search_api_is_text_type($index->options['fields'][$field]['type'])) {
          throw new SearchApiException(t('Trying to search on field @field which is no indexed fulltext field.', array(
            '@field' => $field,
          )));
        }
        $this->used_indexes[$index_id] = TRUE;
        $this->index_fields[$index_id][] = $field;
      }
    }
    $this->fields = $fields;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function filter(SearchApiQueryFilterInterface $filter) {
    $this->filter
      ->filter($filter);
    $indexes = $this
      ->checkFilterIndexes($filter);
    $this->used_indexes += $indexes;

    // Since the filter is added with AND to the query, the query will be
    // restricted to the indexes encountered within it.
    $this->indexes = array_intersect_key($this->indexes, $indexes);
    return $this;
  }

  /**
   * Checks a filter object for filters on the used indexes.
   *
   * @param SearchApiQueryFilterInterface $filter
   *   The filter whose indexes should be added.
   *
   * @return array
   *   An array mapping the machine names of all indexes used in the filter to
   *   TRUE.
   */
  protected function checkFilterIndexes(SearchApiQueryFilterInterface $filter) {
    $indexes = array();

    // Remember all the indexes of fields used in any filters, so we can later
    // restrict the search to only those. Also, restrict the search correctly if
    // the "search_api_multi_index" field is used.
    foreach ($filter
      ->getFilters() as $f) {
      if (is_array($f)) {
        if ($f[0] == 'search_api_multi_index') {
          if ($f[2] == '=') {
            $indexes[$f[1]] = TRUE;
          }
          else {
            foreach ($this->indexes as $id => $index) {
              if ($id != $f[1]) {
                $indexes[$id] = TRUE;
              }
            }
          }
        }
        elseif ($f[2] != '<>' && strpos($f[0], ':')) {
          list($index_id) = explode(':', $f[0], 2);
          $indexes[$index_id] = TRUE;
        }
      }
      else {
        $indexes += $this
          ->checkFilterIndexes($f);
      }
    }
    return $indexes;
  }

  /**
   * {@inheritdoc}
   */
  public function condition($field, $value, $operator = '=') {
    if ($field == 'search_api_multi_index') {
      if ($operator == '=') {
        if (isset($this->indexes[$value])) {
          $this->indexes = array(
            $value => $this->indexes[$value],
          );
        }
        else {
          throw new SearchApiException(t('Trying to filter multi-index query on two indexes simultaneously.'));
        }
      }
      else {
        unset($this->indexes[$value]);
      }
    }
    else {
      $this->filter
        ->condition($field, $value, $operator);
      if ($operator != '<>' && strpos($field, ':')) {
        list($index_id) = explode(':', $field, 2);
        $this->used_indexes[$index_id] = TRUE;
      }
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function sort($field, $order = 'ASC') {
    if ($field != 'search_api_relevance' && $field != 'search_api_id') {
      list($index_id, $f) = explode(':', $field, 2);
      $index = $this->indexes[$index_id];
      $fields = $index->options['fields'];
      if (empty($fields[$f])) {
        throw new SearchApiException(t('Trying to sort on unknown field @field.', array(
          '@field' => $f,
        )));
      }
      $type = $fields[$f]['type'];
      if (search_api_is_list_type($type) || search_api_is_text_type($type)) {
        throw new SearchApiException(t('Trying to sort on field @field of illegal type @type.', array(
          '@field' => $f,
          '@type' => $type,
        )));
      }
      $this->used_indexes[$index_id] = TRUE;
    }
    $order = strtoupper(trim($order)) == 'DESC' ? 'DESC' : 'ASC';
    $this->sort[$field] = $order;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function range($offset = NULL, $limit = NULL) {
    $this->options['offset'] = $offset;
    $this->options['limit'] = $limit;
    return $this;
  }

  /**
   * Implements SearchApiMultiQueryInterface::execute().
   *
   * Uses a server's searchMultiple() method, where possible.
   */
  public final function execute() {
    $start = microtime(TRUE);

    // Call pre-execute hook.
    $this
      ->preExecute();

    // Let modules alter the query.
    drupal_alter('search_api_multi_query', $this);
    $pre_search = microtime(TRUE);

    // Execute query.
    if (count($this->servers) == 1) {
      $server = search_api_server_load(key($this->servers));
      if ($server && $server
        ->supportsFeature('search_api_multi')) {
        $response = $server
          ->searchMultiple($this);
      }
    }
    if (!isset($response)) {
      $response = $this
        ->searchMultiple();
    }
    $post_search = microtime(TRUE);

    // Call post-execute hook.
    $this
      ->postExecute($response);
    $end = microtime(TRUE);
    $response['performance']['complete'] = $end - $start;
    $response['performance']['hooks'] = $response['performance']['complete'] - ($post_search - $pre_search);

    // Store search for later retrieval for facets, etc.
    search_api_multi_current_search(NULL, $this, $response);
    return $response;
  }

  /**
   * Helper method for adding a language filter.
   *
   * @param array $languages
   *   The languages which the query should include.
   */
  protected function addLanguages(array $languages) {
    if (array_search(LANGUAGE_NONE, $languages) === FALSE) {
      $languages[] = LANGUAGE_NONE;
    }
    $filter = $this
      ->createFilter('OR');
    foreach ($languages as $lang) {
      foreach ($this->indexes as $index_id => $index) {
        $filter
          ->condition("{$index_id}:search_api_language", $lang);
      }
    }
    $this
      ->filter($filter);
  }

  /**
   * Searches multiple indexes with this query.
   *
   * Workaround if there is no server's searchMultiple() method available.
   *
   * @return array
   *   Search results as specified by SearchApiMultiQueryInterface::execute().
   */
  protected function searchMultiple() {

    // Prepare options/range.
    $options = $this->options;
    if (!empty($options['offset']) || isset($options['limit'])) {
      $options['limit'] = isset($options['limit']) ? $options['offset'] + $options['limit'] : NULL;
      $options['offset'] = 0;
    }

    // Prepare a normal Search API query for all contained indexes.

    /** @var SearchApiQuery[] $queries */
    $queries = array();
    foreach ($this
      ->getIndexes() as $index_id => $index) {
      try {
        $queries[$index_id] = search_api_query($index_id, $options);
      } catch (SearchApiException $e) {
        watchdog_exception('search_api_multi', $e);
      }
    }

    // Set the filters appropriately.
    $this
      ->addFilters($this->filter
      ->getFilters(), $queries, $queries);

    // Prepare and execute the search on every index available.
    foreach ($queries as $index_id => $query) {
      if (isset($this->orig_keys)) {
        if (empty($this->index_fields[$index_id])) {
          continue;
        }
        $query
          ->keys($this->orig_keys);
        $query
          ->fields($this->index_fields[$index_id]);
      }
      foreach ($this->sort as $field => $order) {
        if (strpos($field, ':') !== FALSE) {
          list($field_index_id, $field) = explode(':', $field, 2);
          if ($field_index_id != $index_id) {
            continue;
          }
        }
        $query
          ->sort($field, $order);
      }
      $response = $query
        ->execute();
      if (!empty($response['results'])) {

        // Adapt the results array to the multi-index format.
        $results = array();
        foreach ($response['results'] as $key => $result) {
          $key = "{$index_id}:{$key}";
          $results[$key] = $result;
          $results[$key]['index_id'] = $index_id;
        }
        $response['results'] = $results;
      }
      if (!isset($return)) {
        $return = array(
          'result count' => 0,
          'results' => array(),
          'performance' => array(),
        );
      }

      // Add the new result count.
      $return['result count'] += $response['result count'];

      // Merge results.
      if (!empty($response['results'])) {
        $return['results'] = array_merge($return['results'], $response['results']);
      }

      // Merge performance.
      if (!empty($response['performance'])) {
        foreach ($response['performance'] as $measure => $time) {
          $return['performance'] += array(
            $measure => 0,
          );
          $return['performance'][$measure] += $time;
        }
      }

      // Merge any additional keys. We can only guess what to do here, but we
      // opt to merge array-valued keys together, and store all other kinds of
      // data in a new array keyed by index ID.
      unset($response['result count'], $response['results'], $response['performance']);
      foreach ($response as $key => $value) {
        if (is_array($value)) {
          $return[$key] = isset($return[$key]) ? array_merge($value, $return[$key]) : $value;
        }
        else {
          $return[$key][$index_id] = $value;
        }
      }
    }
    if (isset($return)) {
      if (!empty($return['results'])) {

        // Add default sorting by score, if it isn't included already.
        if ($this->keys && !isset($this->sort['search_api_relevance'])) {
          $this->sort['search_api_relevance'] = 'DESC';
        }

        // Sort the results.
        if ($this->sort) {
          $this
            ->ensureSortFields($return['results']);
          uasort($return['results'], array(
            $this,
            'compareResults',
          ));
        }

        // Apply range.
        $offset = $this
          ->getOption('offset', 0);
        $limit = $this
          ->getOption('limit', NULL);
        $return['results'] = array_slice($return['results'], $offset, $limit, TRUE);
      }
      return $return;
    }
    return array(
      'result count' => 0,
    );
  }

  /**
   * Helper method for adding a filter to index-specific queries.
   *
   * @param SearchApiQueryFilterInterface[]|array[] $filters
   *   An array of filters to add, as returned by
   *   SearchApiQueryFilterInterface::getFilters().
   * @param SearchApiQuery[] $parents
   *   The query or filter objects to which the filters should be applied, keyed
   *   by index ID.
   * @param SearchApiQuery[] $queries
   *   The queries used, keyed by index ID.
   */
  protected function addFilters(array $filters, array $parents, array $queries) {
    foreach ($filters as $filter) {
      if (is_array($filter)) {
        if ($filter[0] == 'search_api_multi_index') {
          continue;
        }
        list($index_id, $field) = explode(':', $filter[0], 2);
        if (!empty($parents[$index_id])) {
          $parents[$index_id]
            ->condition($field, $filter[1], $filter[2]);
        }
      }
      else {

        /** @var SearchApiQueryFilterInterface[] $nested */
        $nested = array();
        foreach ($parents as $index_id => $query) {
          $nested[$index_id] = $queries[$index_id]
            ->createFilter($filter
            ->getConjunction());
        }
        $this
          ->addFilters($filter
          ->getFilters(), $nested, $queries);
        foreach ($nested as $index_id => $nested_filter) {
          if ($nested_filter
            ->getFilters()) {
            $parents[$index_id]
              ->filter($nested_filter);
          }
        }
      }
    }
  }

  /**
   * Ensure that all results have all fields needed for sorting.
   *
   * @param array $results
   *   The results array, as in the 'results' key of the return value of
   *   SearchApiMultiQueryInterface::execute().
   */
  protected function ensureSortFields(array &$results) {
    $sort = array_keys($this->sort);

    // Eliminate special fields which are always included.
    foreach ($sort as $i => $key) {
      if ($key == 'search_api_id' || $key == 'search_api_relevance') {
        unset($sort[$i]);
      }
    }
    if (!$sort) {
      return;
    }

    // Determine what fields we need from items of each index.
    $fields = array();
    foreach ($sort as $key) {
      list($index_id, $field) = explode(':', $key, 2);
      if (!empty($this->indexes[$index_id])) {
        $fields[$index_id][$field] = $this->indexes[$index_id]->options['fields'][$field];
      }
    }
    if (!$fields) {
      return;
    }

    // Determine for which items we need the entity.
    $to_load = array();
    foreach ($results as $i => $result) {
      $results[$i] = $result += array(
        'fields' => array(),
        'entity' => NULL,
      );
      if (empty($fields[$result['index_id']]) || $result['entity']) {
        continue;
      }
      foreach ($fields[$result['index_id']] as $field => $info) {
        if (!array_key_exists($field, $result['fields'])) {
          $to_load[$this->indexes[$result['index_id']]->item_type][$i] = $result['id'];
          break;
        }
      }
    }

    // Load items, as necessary.
    foreach ($to_load as $type => $ids) {
      $type_items = search_api_get_datasource_controller($type)
        ->loadItems($ids);
      foreach ($ids as $i => $id) {
        if (isset($type_items[$id])) {
          $results[$i]['entity'] = $type_items[$id];
        }
      }
    }

    // Now extract the fields for each item.
    foreach ($results as $i => $result) {
      if (empty($fields[$result['index_id']])) {
        continue;
      }
      $item_fields = $fields[$result['index_id']];
      if (empty($result['entity'])) {
        $results[$i]['fields'] += array_fill_keys(array_keys($item_fields), NULL);
        continue;
      }
      $item_fields = array_diff_key($item_fields, $result['fields']);
      if ($item_fields) {
        $wrapper = $this->indexes[$result['index_id']]
          ->entityWrapper($result['entity']);
        $item_fields = search_api_extract_fields($wrapper, $item_fields);
        foreach ($item_fields as $field => $info) {
          $results[$i]['fields'][$field] = $info['value'];
        }
      }
    }
  }

  /**
   * Compare two result arrays.
   *
   * Callback for uasort() within searchMultiple().
   *
   * @param array $a
   *   One result.
   * @param array $b
   *   The other result.
   *
   * @return int
   *   A negative number if $a should come before $b, 0 if both compare equal
   *   and a positive number otherwise.
   */
  protected function compareResults(array &$a, array &$b) {
    foreach ($this->sort as $key => $order) {

      // Get the sorting for this specific field.
      if ($key == 'search_api_relevance') {
        $comp = $a['score'] - $b['score'];
      }
      elseif ($key == 'search_api_id') {
        if (is_numeric($a['id']) && is_numeric($b['id'])) {
          $comp = $a['id'] - $b['id'];
        }
        else {
          $comp = strnatcasecmp($a['id'], $b['id']);
        }
      }
      else {
        list($index_id, $field) = explode(':', $key, 2);
        $a_applies = $a['index_id'] == $index_id;
        $b_applies = $b['index_id'] == $index_id;
        if ($a_applies == $b_applies) {
          if (!$a_applies) {
            continue;
          }
          $value_a = $a['fields'][$field];
          $value_b = $b['fields'][$field];
          if (is_numeric($value_a) && is_numeric($value_b)) {
            $comp = $value_a - $value_b;
          }
          else {
            $comp = strnatcasecmp($value_a, $value_b);
          }
        }
        else {
          $comp = $a_applies ? -1 : 1;

          // When the sort only applies to one of the two results, we always
          // want it in front of the other, regardless of $order.
          $order = 'ASC';
        }
      }

      // Now apply the specified order and either return or continue.
      if (!$comp) {
        continue;
      }
      return (int) ($order == 'ASC' ? $comp : -$comp);
    }
    return 0;
  }

  /**
   * Pre-execute hook for modifying search behaviour.
   */
  public function preExecute() {

    // Make sure to only execute this once per query.
    if (!$this->pre_execute) {
      $this->pre_execute = TRUE;

      // Add filter for languages.
      if (isset($this->options['languages'])) {
        $this
          ->addLanguages($this->options['languages']);
      }

      // Filter indexes to those used. If no index was explicitly used, include
      // all of them.
      if ($this->used_indexes) {
        $this->indexes = array_intersect_key($this->indexes, $this->used_indexes);
      }

      // Add fulltext fields, unless set.
      if ($this->fields === NULL) {
        $this->fields = $this->index_fields = array();
        foreach ($this->indexes as $index_id => $index) {
          foreach ($index
            ->getFulltextFields() as $f) {
            $this->fields[] = "{$index_id}:{$f}";
            $this->index_fields[$index_id][] = $f;
          }
        }
      }
      elseif ($this->keys) {
        $this->indexes = array_intersect_key($this->indexes, $this->index_fields);
      }

      // Filter the $servers property according to the used indexes.
      foreach ($this->servers as $server_id => $indexes) {
        foreach ($indexes as $index_id => $index) {
          if (!isset($this->indexes[$index_id])) {
            unset($this->servers[$server_id][$index_id]);
          }
        }
      }
      $this->servers = array_filter($this->servers);
    }
  }

  /**
   * Post-execute hook for modifying search behaviour.
   *
   * @param array $results
   *   The results returned by the server, which may be altered.
   */
  public function postExecute(array &$results) {
  }

  /**
   * {@inheritdoc}
   */
  public function getIndexes() {
    return $this->indexes;
  }

  /**
   * {@inheritdoc}
   */
  public function &getKeys() {
    return $this->keys;
  }

  /**
   * {@inheritdoc}
   */
  public function getOriginalKeys() {
    return $this->orig_keys;
  }

  /**
   * {@inheritdoc}
   */
  public function &getFields() {
    return $this->fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getFilter() {
    return $this->filter;
  }

  /**
   * {@inheritdoc}
   */
  public function &getSort() {
    return $this->sort;
  }

  /**
   * {@inheritdoc}
   */
  public function getOption($name, $default = NULL) {
    return isset($this->options[$name]) ? $this->options[$name] : $default;
  }

  /**
   * {@inheritdoc}
   */
  public function setOption($name, $value) {
    $old = $this
      ->getOption($name);
    $this->options[$name] = $value;
    return $old;
  }

  /**
   * {@inheritdoc}
   */
  public function &getOptions() {
    return $this->options;
  }

  /**
   * Implements the magic __toString() method to simplify debugging.
   */
  public function __toString() {
    $ret = '';
    if ($this->indexes) {
      $indexes = array();
      foreach ($this->indexes as $index) {
        $indexes[] = $index->machine_name;
      }
      $ret .= 'Indexes: ' . implode(', ', $indexes) . "\n";
    }
    $ret .= 'Keys: ' . str_replace("\n", "\n  ", var_export($this->orig_keys, TRUE)) . "\n";
    if (isset($this->keys)) {
      $ret .= 'Parsed keys: ' . str_replace("\n", "\n  ", var_export($this->keys, TRUE)) . "\n";
      $ret .= 'Searched fields: ' . (isset($this->fields) ? implode(', ', $this->fields) : '[ALL]') . "\n";
    }
    if ($filter = (string) $this->filter) {
      $filter = str_replace("\n", "\n  ", $filter);
      $ret .= "Filters:\n  {$filter}\n";
    }
    if ($this->sort) {
      $sort = array();
      foreach ($this->sort as $field => $order) {
        $sort[] = "{$field} {$order}";
      }
      $ret .= 'Sorting: ' . implode(', ', $sort) . "\n";
    }
    $options = $this
      ->sanitizeOptions($this->options);
    $options = str_replace("\n", "\n  ", var_export($options, TRUE));
    $ret .= 'Options: ' . $options . "\n";
    return $ret;
  }

  /**
   * Sanitizes an array of options in a way that plays nice with var_export().
   *
   * @param array $options
   *   An array of options.
   *
   * @return array
   *   The sanitized options.
   */
  protected function sanitizeOptions(array $options) {
    foreach ($options as $key => $value) {
      if (is_object($value)) {
        $options[$key] = 'object (' . get_class($value) . ')';
      }
      elseif (is_array($value)) {
        $options[$key] = $this
          ->sanitizeOptions($value);
      }
    }
    return $options;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SearchApiMultiQuery::$fields protected property The fields that will be searched for the keys.
SearchApiMultiQuery::$filter protected property The search filter associated with this query.
SearchApiMultiQuery::$indexes protected property All indexes which are used in this search.
SearchApiMultiQuery::$index_fields protected property The fields that will be searched, grouped by index.
SearchApiMultiQuery::$keys protected property The search keys. If NULL, this will be a filter-only search.
SearchApiMultiQuery::$options protected property Search options configuring this query.
SearchApiMultiQuery::$orig_keys protected property The unprocessed search keys, as passed to the keys() method.
SearchApiMultiQuery::$pre_execute protected property Flag for whether preExecute() was already called for this query.
SearchApiMultiQuery::$servers protected property All indexes which are used in this search, ordered by their servers.
SearchApiMultiQuery::$sort protected property The sort associated with this query.
SearchApiMultiQuery::$used_indexes protected property The indexes which are currently used in this search.
SearchApiMultiQuery::addFilters protected function Helper method for adding a filter to index-specific queries.
SearchApiMultiQuery::addLanguages protected function Helper method for adding a language filter.
SearchApiMultiQuery::checkFilterIndexes protected function Checks a filter object for filters on the used indexes.
SearchApiMultiQuery::compareResults protected function Compare two result arrays.
SearchApiMultiQuery::condition public function Add a new ($field $operator $value) condition filter. Overrides SearchApiMultiQueryInterface::condition
SearchApiMultiQuery::createFilter public function Method for creating a filter to use with this query object. Overrides SearchApiMultiQueryInterface::createFilter
SearchApiMultiQuery::ensureSortFields protected function Ensure that all results have all fields needed for sorting.
SearchApiMultiQuery::execute final public function Implements SearchApiMultiQueryInterface::execute(). Overrides SearchApiMultiQueryInterface::execute
SearchApiMultiQuery::fields public function Sets the fields that will be searched for the search keys. Overrides SearchApiMultiQueryInterface::fields
SearchApiMultiQuery::filter public function Adds a subfilter to this query's filter. Overrides SearchApiMultiQueryInterface::filter
SearchApiMultiQuery::getFields public function Retrieves the searched fulltext fields. Overrides SearchApiMultiQueryInterface::getFields
SearchApiMultiQuery::getFilter public function Retrieves the query's filter object. Overrides SearchApiMultiQueryInterface::getFilter
SearchApiMultiQuery::getIndexes public function Retrieves the searched indexes. Overrides SearchApiMultiQueryInterface::getIndexes
SearchApiMultiQuery::getKeys public function Retrieves the search keys. Overrides SearchApiMultiQueryInterface::getKeys
SearchApiMultiQuery::getOption public function Retrieves a single option. Overrides SearchApiMultiQueryInterface::getOption
SearchApiMultiQuery::getOptions public function Retrieves all options for this query. Overrides SearchApiMultiQueryInterface::getOptions
SearchApiMultiQuery::getOriginalKeys public function Retrieves the original, unprocessed search keys. Overrides SearchApiMultiQueryInterface::getOriginalKeys
SearchApiMultiQuery::getSort public function Retrieves the set sorts. Overrides SearchApiMultiQueryInterface::getSort
SearchApiMultiQuery::keys public function Sets the keys to search for. Overrides SearchApiMultiQueryInterface::keys
SearchApiMultiQuery::parseKeys protected function Parses the keys string according to a certain parse mode.
SearchApiMultiQuery::parseModes public function Retrieve a list of all parse modes supported by this query class. Overrides SearchApiMultiQueryInterface::parseModes
SearchApiMultiQuery::postExecute public function Post-execute hook for modifying search behaviour.
SearchApiMultiQuery::preExecute public function Pre-execute hook for modifying search behaviour.
SearchApiMultiQuery::range public function Adds a range of results to return. This will be saved in the query's options. If called without parameters, this will remove all range restrictions previously set. Overrides SearchApiMultiQueryInterface::range
SearchApiMultiQuery::sanitizeOptions protected function Sanitizes an array of options in a way that plays nice with var_export().
SearchApiMultiQuery::searchMultiple protected function Searches multiple indexes with this query.
SearchApiMultiQuery::setOption public function Sets an option. Overrides SearchApiMultiQueryInterface::setOption
SearchApiMultiQuery::sort public function Add a sort directive to this search query. Overrides SearchApiMultiQueryInterface::sort
SearchApiMultiQuery::__construct public function Constructs a new query object. Overrides SearchApiMultiQueryInterface::__construct
SearchApiMultiQuery::__toString public function Implements the magic __toString() method to simplify debugging.