/** * {@inheritdoc} */ public function getAutocompleteSuggestions(SearchApiQueryInterface $query, $incomplete_key, $user_input) { if ($this->configuration['fields']) { $query->fields($this->configuration['fields']); } return $query->getIndex()->server()->getAutocompleteSuggestions($query, $this->getSearch(), $incomplete_key, $user_input); }
/** * Preprocesses a search's database query before it is executed. * * @param SelectQueryInterface $db_query * The database query to be executed for the search. Will have "item_id" and * "score" columns in its result. * @param SearchApiQueryInterface $query * The search query that is being executed. * * @see SearchApiDbService::preQuery() */ function hook_search_api_db_query_alter(SelectQueryInterface &$db_query, SearchApiQueryInterface $query) { // If the option was set on the query, add additional SQL conditions. if ($custom = $query->getOption('custom_sql_conditions')) { foreach ($custom as $condition) { $db_query->condition($condition['field'], $condition['value'], $condition['operator']); } } }
/** * Implements hook_search_api_query_alter(). * * This example hook implementation shows how a custom module could fix the * problem with Views contextual filters in a specific context. */ function example_search_api_query_alter(SearchApiQueryInterface $query) { // Check whether this is an appropriate automcomplete query. if ($query->getOption('search id') === 'search_api_autocomplete:example') { // If it is, add the necessary filters that would otherwise be added by // contextual filters. This is easy if the argument comes from the global // user or a similar global source. If the argument comes from the URL or // some other page-specific source, however, you would need to somehow pass // that information along to this function. global $user; $query->condition('group', $user->data['group']); } }
/** * Alter the search results before they are returned. * * @param array $results * The results returned by the server, which may be altered. The data * structure is the same as returned by SearchApiQueryInterface::execute(). * @param SearchApiQueryInterface $query * The search query that was executed. */ function hook_search_api_results_alter(array &$results, SearchApiQueryInterface $query) { if ($query->getOption('search id') == 'search_api_views:my_search_view:page') { // Log the number of results. $vars = array('@keys' => $query->getOriginalKeys(), '@num' => $results['result count']); watchdog('my_module', 'Search view with query "@keys" had @num results.', $vars, WATCHDOG_DEBUG); } }
/** * Executes a search on the server represented by this object. * * If the service class supports facets, it should check for an additional * option on the query object: * - search_api_facets: An array of facets to return along with the results * for this query. The array is keyed by an arbitrary string which should * serve as the facet's unique identifier for this search. The values are * arrays with the following keys: * - field: The field to construct facets for. * - limit: The maximum number of facet terms to return. 0 or an empty * value means no limit. * - min_count: The minimum number of results a facet value has to have in * order to be returned. * - missing: If TRUE, a facet for all items with no value for this field * should be returned (if it conforms to limit and min_count). * - operator: (optional) If the service supports "OR" facets and this key * contains the string "or", the returned facets should be "OR" facets. If * the server doesn't support "OR" facets, this key can be ignored. * * The basic principle of facets is explained quite well in the * @link http://en.wikipedia.org/wiki/Faceted_search Wikipedia article on * "Faceted search" @endlink. Basically, you should return for each field * filter values which would yield some results when used with the search. * E.g., if you return for a field $field the term $term with $count results, * the given $query along with * $query->condition($field, $term) * should yield exactly (or about) $count results. * * For "OR" facets, all existing filters on the facetted field should be * ignored for computing the facets. * * @param $query * The SearchApiQueryInterface object to execute. * * @return array * An associative array containing the search results, as required by * SearchApiQueryInterface::execute(). * In addition, if the "search_api_facets" option is present on the query, * the results should contain an array of facets in the "search_api_facets" * key, as specified by the option. The facets array should be keyed by the * facets' unique identifiers, and contain a numeric array of facet terms, * sorted descending by result count. A term is represented by an array with * the following keys: * - count: Number of results for this term. * - filter: The filter to apply when selecting this facet term. A filter is * a string of one of the following forms: * - "VALUE": Filter by the literal value VALUE (always include the * quotes, not only for strings). * - [VALUE1 VALUE2]: Filter for a value between VALUE1 and VALUE2. Use * parantheses for excluding the border values and square brackets for * including them. An asterisk (*) can be used as a wildcard. E.g., * (* 0) or [* 0) would be a filter for all negative values. * - !: Filter for items without a value for this field (i.e., the * "missing" facet). * * @throws SearchApiException * If an error prevented the search from completing. */ public function search(SearchApiQueryInterface $query) { // We assume here that we have an AI search which understands English // commands. // First, create the normal search query, without facets. $search = new SuperCoolAiSearch($query->getIndex()); $search->cmd('create basic search for the following query', $query); $ret = $search->cmd('return search results in Search API format'); // Then, let's see if we should return any facets. if ($facets = $query->getOption('search_api_facets')) { // For the facets, we need all results, not only those in the specified // range. $results = $search->cmd('return unlimited search results as a set'); foreach ($facets as $id => $facet) { $field = $facet['field']; $limit = empty($facet['limit']) ? 'all' : $facet['limit']; $min_count = $facet['min_count']; $missing = $facet['missing']; $or = isset($facet['operator']) && $facet['operator'] == 'or'; // If this is an "OR" facet, existing filters on the field should be // ignored for computing the facets. // You can ignore this if your service class doesn't support the // "search_api_facets_operator_or" feature. if ($or) { // We have to execute another query (in the case of this hypothetical // search backend, at least) to get the right result set to facet. $tmp_search = new SuperCoolAiSearch($query->getIndex()); $tmp_search->cmd('create basic search for the following query', $query); $tmp_search->cmd("remove all conditions for field {$field}"); $tmp_results = $tmp_search->cmd('return unlimited search results as a set'); } else { // Otherwise, we can just use the normal results. $tmp_results = $results; } $filters = array(); if ($search->cmd("{$field} is a date or numeric field")) { // For date, integer or float fields, range facets are more useful. $ranges = $search->cmd("list {$limit} ranges of field {$field} in the following set", $tmp_results); foreach ($ranges as $range) { if ($range->getCount() >= $min_count) { // Get the lower and upper bound of the range. * means unlimited. $lower = $range->getLowerBound(); $lower = $lower == SuperCoolAiSearch::RANGE_UNLIMITED ? '*' : $lower; $upper = $range->getUpperBound(); $upper = $upper == SuperCoolAiSearch::RANGE_UNLIMITED ? '*' : $upper; // Then, see whether the bounds are included in the range. These // can be specified independently for the lower and upper bound. // Parentheses are used for exclusive bounds, square brackets are // used for inclusive bounds. $lowChar = $range->isLowerBoundInclusive() ? '[' : '('; $upChar = $range->isUpperBoundInclusive() ? ']' : ')'; // Create the filter, which separates the bounds with a single // space. $filter = "{$lowChar}{$lower} {$upper}{$upChar}"; $filters[$filter] = $range->getCount(); } } } else { // Otherwise, we use normal single-valued facets. $terms = $search->cmd("list {$limit} values of field {$field} in the following set", $tmp_results); foreach ($terms as $term) { if ($term->getCount() >= $min_count) { // For single-valued terms, we just need to wrap them in quotes. $filter = '"' . $term->getValue() . '"'; $filters[$filter] = $term->getCount(); } } } // If we should also return a "missing" facet, compute that as the // number of results without a value for the facet field. if ($missing) { $count = $search->cmd("return number of results without field {$field} in the following set", $tmp_results); if ($count >= $min_count) { $filters['!'] = $count; } } // Sort the facets descending by result count. arsort($filters); // With the "missing" facet, we might have too many facet terms (unless // $limit was empty and is therefore now set to "all"). If this is the // case, remove those with the lowest number of results. while (is_numeric($limit) && count($filters) > $limit) { array_pop($filters); } // Now add the facet terms to the return value, as specified in the doc // comment for this method. foreach ($filters as $filter => $count) { $ret['search_api_facets'][$id][] = array('count' => $count, 'filter' => $filter); } } } // Return the results, which now also includes the facet information. return $ret; }
/** * Lets modules alter a Solr search request before sending it. * * Apache_Solr_Service::search() is called afterwards with these parameters. * Please see this method for details on what should be altered where and what * is set afterwards. * * @param array $call_args * An associative array containing all four arguments to the * Apache_Solr_Service::search() call ("query", "offset", "limit" and * "params") as references. * @param SearchApiQueryInterface $query * The SearchApiQueryInterface object representing the executed search query. */ function hook_search_api_solr_query_alter(array &$call_args, SearchApiQueryInterface $query) { if ($query->getOption('foobar')) { $call_args['params']['foo'] = 'bar'; } }
/** * Lets modules alter a search query before executing it. * * @param SearchApiQueryInterface $query * The search query being executed. */ function hook_search_api_query_alter(SearchApiQueryInterface $query) { // Exclude entities with ID 0. (Assume the ID field is always indexed.) if ($query->getIndex()->getEntityType()) { $info = entity_get_info($query->getIndex()->getEntityType()); $query->condition($info['entity keys']['id'], 0, '!='); } }
/** * Lets modules alter a search query before executing it. * * @param SearchApiQueryInterface $query * The SearchApiQueryInterface object representing the search query. */ function hook_search_api_query_alter(SearchApiQueryInterface $query) { $info = entity_get_info($index->item_type); $query->condition($info['entity keys']['id'], 0, '!='); }
function ardent_search_api_solr_query_alter(array &$call_args, SearchApiQueryInterface $query) { // Fetching the facet name to change solr query on. $facet_item = $query->getFilter()->getFilters(); if (!empty($facet_item)) { $facet_item = $facet_item[0]->getFilters(); if (!empty($facet_item[0])) { if (!empty($facet_item[0][1])) { $facet_item = $facet_item[0][1]; // This is my facet item I wont to change solr query on "Foo" and also add node type "Bar" to the filter. if ($facet_item === 'research_highlights') { $call_args['params']['fq'][0] = $call_args['params']['fq'][0] . ' OR ss_type:"people"'; } } } } }
/** * Overrides SearchApiSolrService::preQuery(). * * Sets the eDisMax parameters if certain conditions are met, adds the default * parameters that are usually set in Search API's solrconfig.xml file. */ protected function preQuery(array &$call_args, SearchApiQueryInterface $query) { $params =& $call_args['params']; // Bails if this is a 'mlt' query or something else custom. if (!empty($params['qt']) || !empty($params['defType'])) { return; } // The Search API module adds default "fl" parameters in solrconfig.xml // that are not present in Acquia Search's solrconfig.xml file. Add them // and others here as a backwards compatible solution. // @see http://drupal.org/node/1619770 $params += array('echoParams' => 'none', 'fl' => 'item_id,score', 'q.op' => 'AND', 'q.alt' => '*:*', 'spellcheck' => 'false', 'spellcheck.onlyMorePopular' => 'true', 'spellcheck.extendedResults' => 'false', 'spellcheck.count' => '1', 'hl' => 'false', 'hl.fl' => 'spell', 'hl.simple.pre' => '[HIGHLIGHT]', 'hl.simple.post' => '[/HIGHLIGHT]', 'hl.snippets' => '3', 'hl.fragsize' => '70', 'hl.mergeContiguous' => 'true'); // Set the qt to eDisMax if we have keywords and either the configuration // is set to always use eDisMax or the keys contain a wildcard (* or ?). $keys = $query->getOriginalKeys(); if ($keys && (($wildcard = preg_match('/\\S+[*?]/', $keys)) || $this->options['edismax'])) { $params['defType'] = 'edismax'; if ($wildcard) { // Converts keys to lower case, reset keys in query and replaces param. $new_keys = preg_replace_callback('/(\\S+[*?]\\S*)/', array($this, 'toLower'), $keys); $query->keys($new_keys); $call_args['query'] = $new_keys; } } }