public function SearchApiSolrBackend::search in Search API Solr 4.x
Same name and namespace in other branches
- 8.3 src/Plugin/search_api/backend/SearchApiSolrBackend.php \Drupal\search_api_solr\Plugin\search_api\backend\SearchApiSolrBackend::search()
- 8 src/Plugin/search_api/backend/SearchApiSolrBackend.php \Drupal\search_api_solr\Plugin\search_api\backend\SearchApiSolrBackend::search()
- 8.2 src/Plugin/search_api/backend/SearchApiSolrBackend.php \Drupal\search_api_solr\Plugin\search_api\backend\SearchApiSolrBackend::search()
Options on $query prefixed by 'solr_param_' will be passed natively to Solr as query parameter without the prefix. For example you can set the "Minimum Should Match" parameter 'mm' to '75%' like this:
$query
->setOption('solr_param_mm', '75%');
Throws
\Drupal\Component\Plugin\Exception\PluginException
\Drupal\search_api_solr\SearchApiSolrException
File
- src/
Plugin/ search_api/ backend/ SearchApiSolrBackend.php, line 1422
Class
- SearchApiSolrBackend
- Apache Solr backend for search api.
Namespace
Drupal\search_api_solr\Plugin\search_api\backendCode
public function search(QueryInterface $query) {
/** @var \Drupal\search_api\Entity\Index $index */
$index = $query
->getIndex();
$this
->finalizeIndex($index);
if ($query
->getOption('solr_streaming_expression', FALSE)) {
if ($solarium_result = $this
->executeStreamingExpression($query)) {
// Extract results.
$search_api_result_set = $this
->extractResults($query, $solarium_result);
$this->moduleHandler
->alter('search_api_solr_search_results', $search_api_result_set, $query, $solarium_result);
$this
->postQuery($search_api_result_set, $query, $solarium_result);
}
else {
throw new SearchApiSolrException('Streaming expression has no result.');
}
}
else {
$mlt_options = $query
->getOption('search_api_mlt');
if (!empty($mlt_options)) {
$query
->addTag('mlt');
}
// Ensure language(s) condition is set.
$language_ids = $this
->ensureLanguageCondition($query);
// Get field information.
$connector = $this
->getSolrConnector();
$solarium_query = NULL;
$edismax = NULL;
$index_fields = $index
->getFields();
$index_fields += $this
->getSpecialFields($index);
if ($query
->hasTag('mlt')) {
$solarium_query = $this
->getMoreLikeThisQuery($query);
}
else {
// Instantiate a Solarium select query.
$solarium_query = $connector
->getSelectQuery();
$edismax = $solarium_query
->getEDisMax();
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($language_ids, $index);
// Set searched fields.
$search_fields = $this
->getQueryFulltextFields($query);
$query_fields_boosted = [];
foreach ($search_fields as $search_field) {
/** @var \Drupal\search_api\Item\FieldInterface $field */
$field = $index_fields[$search_field];
$boost = $field
->getBoost() ? '^' . $field
->getBoost() : '';
$names = [];
$first_name = reset($field_names[$search_field]);
if (strpos($first_name, 't') === 0) {
// Add all language-specific field names. This should work for
// non Drupal Solr Documents as well which contain only a single
// name.
$names = array_values($field_names[$search_field]);
}
else {
$names[] = $first_name;
}
foreach (array_unique($names) as $name) {
$query_fields_boosted[] = $name . $boost;
}
}
$edismax
->setQueryFields(implode(' ', $query_fields_boosted));
}
$options = $query
->getOptions();
// Set basic filters.
$filter_queries = $this
->getFilterQueries($query, $options);
foreach ($filter_queries as $id => $filter_query) {
$solarium_query
->createFilterQuery('filters_' . $id)
->setQuery($filter_query['query'])
->addTags($filter_query['tags']);
}
if (!Utility::hasIndexJustSolrDocumentDatasource($index)) {
// Set the Index (and site) filter.
$solarium_query
->createFilterQuery('index_filter')
->setQuery($this
->getIndexFilterQueryString($index));
}
else {
// Set requestHandler for the query type, if necessary and configured.
$config = $index
->getDatasource('solr_document')
->getConfiguration();
if (!empty($config['request_handler'])) {
$solarium_query
->addParam('qt', $config['request_handler']);
}
// Set the default query, if necessary and configured.
if (!$solarium_query
->getQuery() && !empty($config['default_query'])) {
$solarium_query
->setQuery($config['default_query']);
}
// The query builder of Search API Solr Search bases on 'OR' which is
// the default value for solr, too. But a foreign schema could have a
// non-default config for q.op. Therefore we need to set it explicitly
// if not set.
$params = $solarium_query
->getParams();
if (!isset($params['q.op'])) {
$solarium_query
->addParam('q.op', 'OR');
}
}
$unspecific_field_names = $this
->getSolrFieldNames($index);
// For solr_document datasource, search_api_language might not be mapped.
if (!empty($unspecific_field_names['search_api_language'])) {
$solarium_query
->createFilterQuery('language_filter')
->setQuery($this
->createFilterQuery($unspecific_field_names['search_api_language'], $language_ids, 'IN', new Field($index, 'search_api_language'), $options));
}
$search_api_retrieved_field_values = array_flip($query
->getOption('search_api_retrieved_field_values', []));
if (array_key_exists('search_api_solr_score_debugging', $search_api_retrieved_field_values)) {
unset($search_api_retrieved_field_values['search_api_solr_score_debugging']);
// Activate the debug query component.
$solarium_query
->getDebug();
}
$search_api_retrieved_field_values = array_keys($search_api_retrieved_field_values);
if ($query
->hasTag('mlt')) {
// Set the list of fields to retrieve, but avoid highlighting and
// different overhead.
$this
->setFields($solarium_query, $search_api_retrieved_field_values, $query, FALSE);
}
else {
// Set the list of fields to retrieve.
$this
->setFields($solarium_query, $search_api_retrieved_field_values, $query);
// Set sorts.
$this
->setSorts($solarium_query, $query);
// Set facet fields. setSpatial() might add more facets.
$this
->setFacets($query, $solarium_query);
// Handle spatial filters.
if (isset($options['search_api_location'])) {
$this
->setSpatial($solarium_query, $options['search_api_location'], $query);
}
// Handle spatial filters.
if (isset($options['search_api_rpt'])) {
$this
->setRpt($solarium_query, $options['search_api_rpt'], $query);
}
// Handle field collapsing / grouping.
if (isset($options['search_api_grouping'])) {
$this
->setGrouping($solarium_query, $query, $options['search_api_grouping'], $index_fields, $field_names);
}
// Handle spellcheck.
if (isset($options['search_api_spellcheck'])) {
$this
->setSpellcheck($solarium_query, $query, $options['search_api_spellcheck']);
}
}
if (isset($options['offset'])) {
$solarium_query
->setStart($options['offset']);
}
// In previous versions we set a high value for rows if no limit was set
// in the options. The intention was to retrieve "all" results instead of
// falling back to Solr's default of 10. But for Solr Cloud it turned out
// that independent from the real number of documents, Solr seems to
// allocate rows*shards memory for sorting the distributed result. That
// could lead to out of memory exceptions. The default limit is now
// configurable as advanced server option.
$solarium_query
->setRows($query
->getOption('limit') ?? $this->configuration['rows'] ?? 10);
foreach ($options as $option => $value) {
if (strpos($option, 'solr_param_') === 0) {
$solarium_query
->addParam(substr($option, 11), $value);
}
}
$this
->applySearchWorkarounds($solarium_query, $query);
try {
// Allow modules to alter the solarium query.
$this->moduleHandler
->alter('search_api_solr_query', $solarium_query, $query);
$this
->preQuery($solarium_query, $query);
// Since Solr 7.2 the edsimax query parser doesn't allow local
// parameters anymore. But since we don't want to force all modules that
// implemented our hooks to re-write their code, we transform the query
// back into a lucene query. flattenKeys() was adjusted accordingly, but
// in a backward compatible way.
// @see https://lucene.apache.org/solr/guide/7_2/solr-upgrade-notes.html#solr-7-2
if ($edismax) {
$parse_mode = $query
->getParseMode();
$parse_mode_id = $parse_mode
->getPluginId();
/** @var \Solarium\Core\Query\AbstractQuery $solarium_query */
$params = $solarium_query
->getParams();
// Extract keys.
$keys = $query
->getKeys();
$query_fields_boosted = $edismax
->getQueryFields() ?? '';
if (isset($params['defType']) && 'edismax' === $params['defType']) {
// Edismax was forced via API. In case of parse mode 'direct' we get
// a string we use as it is. In the other cases we just need to
// escape the keys.
$flatten_keys = 'direct' === $parse_mode_id ? $keys : Utility::flattenKeys($keys, [], 'keys');
}
else {
$settings = Utility::getIndexSolrSettings($index);
$flatten_keys = Utility::flattenKeys($keys, $query_fields_boosted ? explode(' ', $query_fields_boosted) : [], $parse_mode_id, $settings['term_modifiers']);
}
if ('direct' !== $parse_mode_id && strpos($flatten_keys, '-(') === 0) {
// flattenKeys() always wraps the query in parenthesis. If the query
// is negated we need to extend it by *:* which logically means 'all
// documents' except the ones that match the flatten keys.
$flatten_keys = '*:* ' . $flatten_keys;
}
$flatten_query = [];
if (!Utility::hasIndexJustSolrDocumentDatasource($index) && (!isset($params['defType']) || 'edismax' !== $params['defType'])) {
// Apply term boosts if configured via a Search API processor if
// sort by search_api_relevance is present.
$sorts = $solarium_query
->getSorts();
$relevance_field = reset($field_names['search_api_relevance']);
if (isset($sorts[$relevance_field])) {
if ($boosts = $query
->getOption('solr_document_boost_factors', [])) {
$sum[] = 'boost_document';
foreach ($boosts as $field_id => $boost) {
// Ensure a single value field for the boost function.
$solr_field_name = Utility::getSortableSolrField($field_id, $field_names, $query);
$sum[] = str_replace(self::FIELD_PLACEHOLDER, $solr_field_name, $boost);
}
$flatten_query[] = '{!boost b=sum(' . implode(',', $sum) . ')}';
}
else {
$flatten_query[] = '{!boost b=boost_document}';
}
// @todo Remove condition together with search_api_solr_legacy.
if (version_compare($connector
->getSolrMajorVersion(), '6', '>=')) {
// Since Solr 6 we could use payload_score!
$flatten_query[] = Utility::flattenKeysToPayloadScore($keys, $parse_mode);
}
}
}
$flatten_query[] = trim($flatten_keys ?: '*:*');
$solarium_query
->setQuery(implode(' ', $flatten_query));
if (!isset($params['defType']) || 'edismax' !== $params['defType']) {
$solarium_query
->removeComponent(ComponentAwareQueryInterface::COMPONENT_EDISMAX);
}
else {
// Remove defType 'edismax' because the solarium query still has the
// edismax component which will set defType itself. We should avoid
// to have this parameter twice.
$solarium_query
->removeParam('defType');
}
}
// Allow modules to alter the converted solarium query.
$this->moduleHandler
->alter('search_api_solr_converted_query', $solarium_query, $query);
// Send search request.
$response = $connector
->search($solarium_query, $this
->getCollectionEndpoint($index));
$body = $response
->getBody();
if (200 != $response
->getStatusCode()) {
throw new SearchApiSolrException(strip_tags($body), $response
->getStatusCode());
}
$search_api_response = new Response($body, $response
->getHeaders());
$solarium_result = $connector
->createSearchResult($solarium_query, $search_api_response);
// Extract results.
$search_api_result_set = $this
->extractResults($query, $solarium_result);
if ($solarium_result instanceof Result) {
// Extract facets.
if ($solarium_facet_set = $solarium_result
->getFacetSet()) {
$search_api_result_set
->setExtraData('facet_set', $solarium_facet_set);
if ($search_api_facets = $this
->extractFacets($query, $solarium_result)) {
$search_api_result_set
->setExtraData('search_api_facets', $search_api_facets);
}
}
// Extract spellcheck suggestions.
if (isset($options['search_api_spellcheck'])) {
$search_api_spellcheck['suggestions'] = $this
->extractSpellCheckSuggestions($solarium_result);
if (!empty($options['search_api_spellcheck']['collate'])) {
/** @var \Solarium\Component\Result\Spellcheck\Result $spellcheck_result */
if ($spellcheck_result = $solarium_result
->getComponent(ComponentAwareQueryInterface::COMPONENT_SPELLCHECK)) {
if ($collation = $spellcheck_result
->getCollation()) {
$search_api_spellcheck['collation'] = $collation
->getQuery();
}
}
}
$search_api_result_set
->setExtraData('search_api_spellcheck', $search_api_spellcheck);
}
}
$this->moduleHandler
->alter('search_api_solr_search_results', $search_api_result_set, $query, $solarium_result);
$this
->postQuery($search_api_result_set, $query, $solarium_result);
} catch (\Exception $e) {
throw new SearchApiSolrException('An error occurred while trying to search with Solr: ' . $e
->getMessage(), $e
->getCode(), $e);
}
}
}