You are here

public function FuzzySearchService::search in Fuzzy Search 7

Executes a search on the server represented by this object.

Parameters

$query: The SearchApiQueryInterface object to execute.

Return value

array An associative array containing the search results, as required by SearchApiQueryInterface::execute().

Throws

SearchApiException If an error prevented the search from completing.

Overrides SearchApiServiceInterface::search

File

includes/service.inc, line 665

Class

FuzzySearchService
Search service class using the database for storing index information.

Code

public function search(SearchApiQueryInterface $query) {
  $time_method_called = microtime(TRUE);
  $set = $this
    ->setDb();
  $index = $query
    ->getIndex();
  if (empty($this->options['indexes'][$index->machine_name])) {
    throw new SearchApiException(t('Unknown index !id.', array(
      '!id' => $index->machine_name,
    )));
  }
  $fuzzy = fuzzysearch_get_index_options($index);
  $fields = $this->options['indexes'][$index->machine_name];
  $keys =& $query
    ->getKeys();
  $keys_set = (bool) $keys;
  $keys = $this
    ->prepareKeys($keys);
  if ($keys && !(is_array($keys) && count($keys) == 1)) {
    $fulltext_fields = $query
      ->getFields();
    if ($fulltext_fields) {
      $_fulltext_fields = $fulltext_fields;
      $fulltext_fields = array();
      foreach ($_fulltext_fields as $name) {
        if (!isset($fields[$name])) {
          throw new SearchApiException(t('Unknown field !field specified as search target.', array(
            '!field' => $name,
          )));
        }
        if (!search_api_is_text_type($fields[$name]['type'])) {
          throw new SearchApiException(t('Cannot perform fulltext search on field !field of type !type.', array(
            '!field' => $name,
            '!type' => $fields[$name]['type'],
          )));
        }
        $fulltext_fields[$name] = $fields[$name];
      }
      $db_query = $this
        ->createKeysQuery($keys, $fulltext_fields, $fields, $fuzzy);
      if (is_array($keys) && !empty($keys['#negation'])) {
        $db_query
          ->addExpression(':score', 'score', array(
          ':score' => 1,
        ));
      }
    }
    else {
      $msg = t('Search keys are given but no fulltext fields are defined.');
      watchdog('search api', $msg, NULL, WATCHDOG_WARNING);
      $this->warnings[$msg] = 1;
    }
  }
  elseif ($keys_set) {
    $msg = t('No valid search keys were present in the query.');
    $this->warnings[$msg] = 1;
    $results = array(
      'result count' => 0,
      'results' => array(),
    );
    $results['warnings'] = array_keys($this->warnings);
    $results['ignored'] = array_keys($this->ignored);
    return $results;
  }
  $filter = $query
    ->getFilter();
  if ($filter
    ->getFilters()) {
    if (!isset($db_query)) {
      $db_query = db_select($fields['search_api_language']['table'], 't', $this->queryOptions);
      $db_query
        ->addField('t', 'item_id', 'item_id');
      $db_query
        ->addExpression(':score', 'score', array(
        ':score' => 1,
      ));
    }
    $condition = $this
      ->createFilterCondition($filter, $fields, $db_query);
    if ($condition) {
      $db_query
        ->condition($condition);
    }
  }
  if (!isset($db_query)) {
    $db_query = db_select($fields['search_api_language']['table'], 't', $this->queryOptions);
    $db_query
      ->addField('t', 'item_id', 'item_id');
    $db_query
      ->addExpression(':score', 'score', array(
      ':score' => 1,
    ));
  }
  $time_processing_done = microtime(TRUE);
  $results = array();
  $count_query = $db_query
    ->countQuery();

  // See @todo below.
  $results['result count'] = $count_query
    ->execute()
    ->fetchField();
  if ($results['result count']) {
    if ($query
      ->getOption('search_api_facets')) {
      $results['search_api_facets'] = $this
        ->getFacets($query, clone $db_query);
    }
    $query_options = $query
      ->getOptions();
    if (isset($query_options['offset']) || isset($query_options['limit'])) {
      $offset = isset($query_options['offset']) ? $query_options['offset'] : 0;
      $limit = isset($query_options['limit']) ? $query_options['limit'] : 1000000;
      $db_query
        ->range($offset, $limit);
    }
    $sort = $query
      ->getSort();
    if ($sort) {
      foreach ($sort as $field_name => $order) {
        if ($order != 'ASC' && $order != 'DESC') {
          $msg = t('Unknown sort order !order. Assuming "ASC".', array(
            '!order' => $order,
          ));
          $this->warnings[$msg] = $msg;
          $order = 'ASC';
        }
        if ($field_name == 'search_api_relevance') {
          $db_query
            ->orderBy('score', $order);
          continue;
        }
        if ($field_name == 'search_api_id') {
          $db_query
            ->orderBy('item_id', $order);
          continue;
        }
        if (!isset($fields[$field_name])) {
          throw new SearchApiException(t('Trying to sort on unknown field !field.', array(
            '!field' => $field_name,
          )));
        }
        $field = $fields[$field_name];
        if (search_api_is_list_type($field['type'])) {
          throw new SearchApiException(t('Cannot sort on field !field of a list type.', array(
            '!field' => $field_name,
          )));
        }
        if (search_api_is_text_type($field['type'])) {
          throw new SearchApiException(t('Cannot sort on fulltext field !field.', array(
            '!field' => $field_name,
          )));
        }
        $alias = $this
          ->getTableAlias($field, $db_query);
        $db_query
          ->orderBy($alias . '.value', $order);
      }
    }
    else {

      // If there isn't a "percent" alias already, see if we can find the
      // completeness field to add the expression.
      if (($expressions = $db_query
        ->getExpressions()) != FALSE && !array_key_exists('percent', $expressions)) {
        foreach ($db_query
          ->getTables() as $alias => $tdata) {
          if (isset($tdata['table']) && is_string($tdata['table']) && db_field_exists($tdata['table'], 'completeness')) {
            $db_query
              ->addExpression('SUM(t.completeness)', 'percent');
          }
        }
      }

      // Check for the expression before attempting to sort by it.
      if (($expressions = $db_query
        ->getExpressions()) != FALSE && array_key_exists('percent', $expressions)) {

        // If "sort by score" is selected, score before percent.
        if ($fuzzy['sort_score']) {
          $db_query
            ->orderBy('score', 'DESC');
          $db_query
            ->orderBy('percent', 'DESC');
        }
        else {
          $db_query
            ->orderBy('percent', 'DESC');
          $db_query
            ->orderBy('score', 'DESC');
        }
      }
      else {
        $db_query
          ->orderBy('score', 'DESC');
      }
    }
    $result = $db_query
      ->execute();
    $time_queries_done = microtime(TRUE);
    foreach ($result as $row) {
      $results['results'][$row->item_id] = array(
        'id' => $row->item_id,
        'score' => $row->score,
        // Have to check because in a views search we return results by
        // default, even without keys.
        'percent' => isset($row->percent) ? $row->percent : '',
      );
    }
  }
  else {
    $time_queries_done = microtime(TRUE);
    $results['results'] = array();
  }
  $results['result count'] = $count_query
    ->execute()
    ->fetchField();
  $results['warnings'] = array_keys($this->warnings);
  $results['ignored'] = array_keys($this->ignored);
  if ($set) {
    $this
      ->resetDb();
  }
  $time_end = microtime(TRUE);
  $results['performance'] = array(
    'complete' => $time_end - $time_method_called,
    'preprocessing' => $time_processing_done - $time_method_called,
    'execution' => $time_queries_done - $time_processing_done,
    'postprocessing' => $time_end - $time_queries_done,
  );
  return $results;
}