/** * Retrieves the filter object associated with this search query. * * @return \Drupal\search_api\Query\FilterInterface * This object's associated filter object. * * @see \Drupal\search_api\Query\QueryInterface::getFilter() */ public function getFilter() { if (!$this->shouldAbort()) { return $this->query->getFilter(); } return NULL; }
/** * {@inheritdoc} */ public function preprocessSearchQuery(QueryInterface $query) { $keys =& $query->getKeys(); if (isset($keys)) { $this->processKeys($keys); } $filter = $query->getFilter(); $filters =& $filter->getFilters(); $this->processFilters($filters); }
/** * 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 = (bool) $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 = $query->getFields(); if ($fulltext_fields) { $_fulltext_fields = $fulltext_fields; $fulltext_fields = array(); foreach ($_fulltext_fields as $name) { if (!isset($fields[$name])) { throw new SearchApiException(SafeMarkup::format('Unknown field @field specified as search target.', array('@field' => $name))); } if (!Utility::isTextType($fields[$name]['type'])) { $types = $this->getDataTypePluginManager()->getDataTypeDefinitions(); $type = $types[$fields[$name]['type']]['label']; throw new SearchApiException(SafeMarkup::format('Cannot perform fulltext search on field @field of type @type.', array('@field' => $name, '@type' => $type))); } $fulltext_fields[$name] = $fields[$name]; } $db_query = $this->createKeysQuery($keys, $fulltext_fields, $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[$msg] = 1; } } elseif ($keys_set) { $msg = $this->t('No valid search keys were present in the query.'); $this->warnings[$msg] = 1; } if (!isset($db_query)) { $db_query = $this->database->select($this->configuration['index_tables'][$query->getIndex()->id()], '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; }
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; }