Пример #1
0
 /**
  * Retrieves the search keys for this query.
  *
  * @return array|string|null
  *   This object's search keys - either a string or an array specifying a
  *   complex search expression.
  *   An array will contain a '#conjunction' key specifying the conjunction
  *   type, and search strings or nested expression arrays at numeric keys.
  *   Additionally, a '#negation' key might be present, which means – unless it
  *   maps to a FALSE value – that the search keys contained in that array
  *   should be negated, i.e. not be present in returned results. The negation
  *   works on the whole array, not on each contained term individually – i.e.,
  *   with the "AND" conjunction and negation, only results that contain all
  *   the terms in the array should be excluded; with the "OR" conjunction and
  *   negation, all results containing one or more of the terms in the array
  *   should be excluded.
  *
  * @see keys()
  *
  * @see \Drupal\search_api\Query\QueryInterface::getKeys()
  */
 public function &getKeys() {
   if (!$this->shouldAbort()) {
     return $this->query->getKeys();
   }
   $ret = NULL;
   return $ret;
 }
Пример #2
0
 /**
  * Extracts the positive keywords used in a search query.
  *
  * @param \Drupal\search_api\Query\QueryInterface $query
  *   The query from which to extract the keywords.
  *
  * @return string[]
  *   An array of all unique positive keywords used in the query.
  */
 protected function getKeywords(QueryInterface $query)
 {
     $keys = $query->getKeys();
     if (!$keys) {
         return array();
     }
     if (is_array($keys)) {
         return $this->flattenKeysArray($keys);
     }
     $keywords_in = preg_split(self::$split, $keys);
     // Assure there are no duplicates. (This is actually faster than
     // array_unique() by a factor of 3 to 4.)
     // Remove quotes from keywords.
     $keywords = array();
     foreach (array_filter($keywords_in) as $keyword) {
         if ($keyword = trim($keyword, "'\"")) {
             $keywords[$keyword] = $keyword;
         }
     }
     return $keywords;
 }
Пример #3
0
  /**
   * Creates a database query for a search.
   *
   * Used as a helper method in search() and getAutocompleteSuggestions().
   *
   * @param \Drupal\search_api\Query\QueryInterface $query
   *   The search query for which to create the database query.
   * @param array $fields
   *   The internal field information to use.
   *
   * @return \Drupal\Core\Database\Query\SelectInterface
   *   A database query object which will return the appropriate results (except
   *   for the range and sorting) for the given search query.
   *
   * @throws \Drupal\search_api\SearchApiException
   *   Thrown if some illegal query setting (unknown field, etc.) was
   *   encountered.
   */
  protected function createDbQuery(QueryInterface $query, array $fields) {
    $keys = &$query->getKeys();
    $keys_set = (boolean) $keys;
    $keys = $this->prepareKeys($keys);

    // Only filter by fulltext keys if there are any real keys present.
    if ($keys && (!is_array($keys) || count($keys) > 2 || (!isset($keys['#negation']) && count($keys) > 1))) {
      // Special case: if the outermost $keys array has "#negation" set, we can't
      // handle it like other negated subkeys. To avoid additional complexity
      // later, we just wrap $keys so it becomes a subkey.
      if (!empty($keys['#negation'])) {
        $keys = array(
          '#conjunction' => 'AND',
          $keys,
        );
      }

      $fulltext_fields = $this->getQueryFulltextFields($query);
      if ($fulltext_fields) {
        $fulltext_field_information = array();
        foreach ($fulltext_fields as $name) {
          if (!isset($fields[$name])) {
            throw new SearchApiException(new FormattableMarkup('Unknown field @field specified as search target.', array('@field' => $name)));
          }
          if (!Utility::isTextType($fields[$name]['type'])) {
            $types = $this->getDataTypePluginManager()->getInstances();
            $type = $types[$fields[$name]['type']]->label();
            throw new SearchApiException(new FormattableMarkup('Cannot perform fulltext search on field @field of type @type.', array('@field' => $name, '@type' => $type)));
          }
          $fulltext_field_information[$name] = $fields[$name];
        }

        $db_query = $this->createKeysQuery($keys, $fulltext_field_information, $fields, $query->getIndex());
      }
      else {
        $this->getLogger()->warning('Search keys are given but no fulltext fields are defined.');
        $msg = $this->t('Search keys are given but no fulltext fields are defined.');
        $this->warnings[(string) $msg] = 1;
      }
    }
    elseif ($keys_set) {
      $msg = $this->t('No valid search keys were present in the query.');
      $this->warnings[(string) $msg] = 1;
    }

    if (!isset($db_query)) {
      $db_info = $this->getIndexDbInfo($query->getIndex());
      $db_query = $this->database->select($db_info['index_table'], 't');
      $db_query->addField('t', 'item_id', 'item_id');
      $db_query->addExpression(':score', 'score', array(':score' => self::SCORE_MULTIPLIER));
      $db_query->distinct();
    }

    $filter = $query->getFilter();
    if ($filter->getFilters()) {
      $condition = $this->createFilterCondition($filter, $fields, $db_query, $query->getIndex());
      if ($condition) {
        $db_query->condition($condition);
      }
    }

    $db_query->addTag('search_api_db_search');
    $db_query->addMetaData('search_api_query', $query);
    $db_query->addMetaData('search_api_db_fields', $fields);

    return $db_query;
  }
 /**
  * {@inheritdoc}
  */
 public function preprocessSearchQuery(QueryInterface $query)
 {
     $keys =& $query->getKeys();
     if (isset($keys)) {
         $this->processKeys($keys);
     }
     $filter = $query->getFilter();
     $filters =& $filter->getFilters();
     $this->processFilters($filters);
 }
Пример #5
0
 public function getAutocompleteSuggestions(QueryInterface $query, SearchApiAutocompleteSearch $search, $incomplete_key, $user_input)
 {
     $suggestions = array();
     // Reset request handler
     $this->request_handler = NULL;
     // Turn inputs to lower case, otherwise we get case sensivity problems.
     $incomp = Unicode::strtolower($incomplete_key);
     $index = $query->getIndex();
     $field_names = $this->getFieldNames($index);
     $complete = $query->getOriginalKeys();
     // Extract keys
     $keys = $query->getKeys();
     if (is_array($keys)) {
         $keys_array = array();
         while ($keys) {
             reset($keys);
             if (!element_child(key($keys))) {
                 array_shift($keys);
                 continue;
             }
             $key = array_shift($keys);
             if (is_array($key)) {
                 $keys = array_merge($keys, $key);
             } else {
                 $keys_array[$key] = $key;
             }
         }
         $keys = $this->getSolrHelper()->flattenKeys($query->getKeys());
     } else {
         $keys_array = preg_split('/[-\\s():{}\\[\\]\\\\"]+/', $keys, -1, PREG_SPLIT_NO_EMPTY);
         $keys_array = array_combine($keys_array, $keys_array);
     }
     if (!$keys) {
         $keys = NULL;
     }
     // Set searched fields
     $options = $query->getOptions();
     $search_fields = $query->getFulltextFields();
     $qf = array();
     foreach ($search_fields as $f) {
         $qf[] = $field_names[$f];
     }
     // Extract filters
     $fq = $this->createFilterQueries($query->getFilter(), $field_names, $index->getOption('fields', array()));
     $index_id = $this->getIndexId($index->id());
     $fq[] = 'index_id:' . $this->getQueryHelper()->escapePhrase($index_id);
     if ($this->configuration['site_hash']) {
         $site_hash = $this->getQueryHelper()->escapePhrase(SearchApiSolrUtility::getSiteHash());
         $fq[] = 'hash:' . $site_hash;
     }
     // Autocomplete magic
     $facet_fields = array();
     foreach ($search_fields as $f) {
         $facet_fields[] = $field_names[$f];
     }
     $limit = $query->getOption('limit', 10);
     $params = array('qf' => $qf, 'fq' => $fq, 'rows' => 0, 'facet' => 'true', 'facet.field' => $facet_fields, 'facet.prefix' => $incomp, 'facet.limit' => $limit * 5, 'facet.mincount' => 1, 'spellcheck' => !isset($this->configuration['autocorrect_spell']) || $this->configuration['autocorrect_spell'] ? 'true' : 'false', 'spellcheck.count' => 1);
     // Retrieve http method from server options.
     $http_method = !empty($this->configuration['http_method']) ? $this->configuration['http_method'] : 'AUTO';
     $call_args = array('query' => &$keys, 'params' => &$params, 'http_method' => &$http_method);
     if ($this->request_handler) {
         $this->setRequestHandler($this->request_handler, $call_args);
     }
     $second_pass = !isset($this->configuration['autocorrect_suggest_words']) || $this->configuration['autocorrect_suggest_words'];
     for ($i = 0; $i < ($second_pass ? 2 : 1); ++$i) {
         try {
             // Send search request
             $this->connect();
             $this->moduleHandler->alter('search_api_solr_query', $call_args, $query);
             $this->preQuery($call_args, $query);
             $response = $this->solr->search($keys, $params, $http_method);
             if (!empty($response->spellcheck->suggestions)) {
                 $replace = array();
                 foreach ($response->spellcheck->suggestions as $word => $data) {
                     $replace[$word] = $data->suggestion[0];
                 }
                 $corrected = str_ireplace(array_keys($replace), array_values($replace), $user_input);
                 if ($corrected != $user_input) {
                     array_unshift($suggestions, array('prefix' => $this->t('Did you mean') . ':', 'user_input' => $corrected));
                 }
             }
             $matches = array();
             if (isset($response->facet_counts->facet_fields)) {
                 foreach ($response->facet_counts->facet_fields as $terms) {
                     foreach ($terms as $term => $count) {
                         if (isset($matches[$term])) {
                             // If we just add the result counts, we can easily get over the
                             // total number of results if terms appear in multiple fields.
                             // Therefore, we just take the highest value from any field.
                             $matches[$term] = max($matches[$term], $count);
                         } else {
                             $matches[$term] = $count;
                         }
                     }
                 }
                 if ($matches) {
                     // Eliminate suggestions that are too short or already in the query.
                     foreach ($matches as $term => $count) {
                         if (strlen($term) < 3 || isset($keys_array[$term])) {
                             unset($matches[$term]);
                         }
                     }
                     // Don't suggest terms that are too frequent (by default in more
                     // than 90% of results).
                     $result_count = $response->response->numFound;
                     $max_occurrences = $result_count * $this->searchApiSolrSettings->get('autocomplete_max_occurrences');
                     if (($max_occurrences >= 1 || $i > 0) && $max_occurrences < $result_count) {
                         foreach ($matches as $match => $count) {
                             if ($count > $max_occurrences) {
                                 unset($matches[$match]);
                             }
                         }
                     }
                     // The $count in this array is actually a score. We want the
                     // highest ones first.
                     arsort($matches);
                     // Shorten the array to the right ones.
                     $additional_matches = array_slice($matches, $limit - count($suggestions), NULL, TRUE);
                     $matches = array_slice($matches, 0, $limit, TRUE);
                     // Build suggestions using returned facets
                     $incomp_length = strlen($incomp);
                     foreach ($matches as $term => $count) {
                         if (Unicode::strtolower(substr($term, 0, $incomp_length)) == $incomp) {
                             $suggestions[] = array('suggestion_suffix' => substr($term, $incomp_length), 'term' => $term, 'results' => $count);
                         } else {
                             $suggestions[] = array('suggestion_suffix' => ' ' . $term, 'term' => $term, 'results' => $count);
                         }
                     }
                 }
             }
         } catch (SearchApiException $e) {
             watchdog_exception('search_api_solr', $e, "%type during autocomplete Solr query: !message in %function (line %line of %file).", array(), WATCHDOG_WARNING);
         }
         if (count($suggestions) >= $limit) {
             break;
         }
         // Change parameters for second query.
         unset($params['facet.prefix']);
         $keys = trim($keys . ' ' . $incomplete_key);
     }
     return $suggestions;
 }
 /**
  * {@inheritdoc}
  */
 public function preprocessSearchQuery(QueryInterface $query)
 {
     $keys =& $query->getKeys();
     if (isset($keys)) {
         $this->processKeys($keys);
     }
     $conditions = $query->getConditionGroup();
     $this->processConditions($conditions->getConditions());
 }