You are here

public function SearchApiSolrBackend::search in Search API Solr 8.2

Same name and namespace in other branches
  1. 8.3 src/Plugin/search_api/backend/SearchApiSolrBackend.php \Drupal\search_api_solr\Plugin\search_api\backend\SearchApiSolrBackend::search()
  2. 8 src/Plugin/search_api/backend/SearchApiSolrBackend.php \Drupal\search_api_solr\Plugin\search_api\backend\SearchApiSolrBackend::search()
  3. 4.x 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%');

Overrides BackendSpecificInterface::search

File

src/Plugin/search_api/backend/SearchApiSolrBackend.php, line 1001

Class

SearchApiSolrBackend
Apache Solr backend for search api.

Namespace

Drupal\search_api_solr\Plugin\search_api\backend

Code

public function search(QueryInterface $query) {
  $this
    ->finalizeIndex($query
    ->getIndex());
  if ($query
    ->getOption('solr_streaming_expression', FALSE)) {
    $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 {
    $mlt_options = $query
      ->getOption('search_api_mlt');
    if (!empty($mlt_options)) {
      $query
        ->addTag('mlt');
    }

    // Call an object oriented equivalent to hook_search_api_query_alter().
    $this
      ->alterSearchApiQuery($query);

    // Get field information.

    /** @var \Drupal\search_api\Entity\Index $index */
    $index = $query
      ->getIndex();
    $index_id = $this
      ->getIndexId($index);
    $field_names = $this
      ->getSolrFieldNames($index);
    $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, $index_id, $index_fields, $field_names);
    }
    else {

      // Instantiate a Solarium select query.
      $solarium_query = $connector
        ->getSelectQuery();
      $edismax = $solarium_query
        ->getEDisMax();

      // Set searched fields.
      $search_fields = $this
        ->getQueryFulltextFields($query);
      $query_fields = [];
      $query_fields_boosted = [];
      foreach ($search_fields as $search_field) {
        $query_fields[] = $field_names[$search_field];

        /** @var \Drupal\search_api\Item\FieldInterface $field */
        $field = $index_fields[$search_field];
        $boost = $field
          ->getBoost() ? '^' . $field
          ->getBoost() : '';
        $query_fields_boosted[] = $field_names[$search_field] . $boost;
      }
      $edismax
        ->setQueryFields(implode(' ', $query_fields_boosted));
      try {
        $highlight_config = $index
          ->getProcessor('highlight')
          ->getConfiguration();
        if ($highlight_config['highlight'] != 'never') {
          $this
            ->setHighlighting($solarium_query, $query, $query_fields);
        }
      } catch (SearchApiException $exception) {

        // Highlighting processor is not enabled for this index. Just use the
        // the index configuration.
        $this
          ->setHighlighting($solarium_query, $query, $query_fields);
      }
    }
    $options = $query
      ->getOptions();

    // Set basic filters.
    $filter_queries = $this
      ->getFilterQueries($query, $field_names, $index_fields, $options);
    foreach ($filter_queries as $id => $filter_query) {
      $solarium_query
        ->createFilterQuery('filters_' . $id)
        ->setQuery($filter_query['query'])
        ->addTags($filter_query['tags']);
    }

    // Set the Index (and site) filter.
    $solarium_query
      ->createFilterQuery('index_filter')
      ->setQuery($this
      ->getIndexFilterQueryString($index));

    // Set the list of fields to retrieve.
    $this
      ->setFields($solarium_query, $field_names, $query
      ->getOption('search_api_retrieved_field_values', []));

    // Set sorts.
    $this
      ->setSorts($solarium_query, $query, $field_names);

    // Set facet fields. setSpatial() might add more facets.
    $this
      ->setFacets($query, $solarium_query, $field_names);

    // Handle spatial filters.
    if (isset($options['search_api_location'])) {
      $this
        ->setSpatial($solarium_query, $options['search_api_location'], $field_names);
    }

    // Handle spatial filters.
    if (isset($options['search_api_rpt'])) {
      $this
        ->setRpt($solarium_query, $options['search_api_rpt'], $field_names);
    }

    // Handle field collapsing / grouping.
    $grouping_options = $query
      ->getOption('search_api_grouping');
    if (!empty($grouping_options['use_grouping'])) {
      $this
        ->setGrouping($solarium_query, $query, $grouping_options, $index_fields, $field_names);
    }
    if (isset($options['offset'])) {
      $solarium_query
        ->setStart($options['offset']);
    }
    $rows = isset($options['limit']) ? $options['limit'] : 1000000;
    $solarium_query
      ->setRows($rows);
    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_id = $query
          ->getParseMode()
          ->getPluginId();
        if ('terms' == $parse_mode_id) {

          // Using the 'phrase' parse mode, Search API provides one big phrase
          // as keys. Using the 'terms' parse mode, Search API provides chunks
          // of single terms as keys. But these chunks might contain not just
          // real terms but again a phrase if you enter something like this in
          // the search box: term1 "term2 as phrase" term3. This will be
          // converted in this keys array: ['term1', 'term2 as phrase',
          // 'term3']. To have Solr behave like the database backend, these
          // three "terms" should be handled like three phrases.
          $parse_mode_id = 'phrase';
        }

        /** @var Query $solarium_query */
        $params = $solarium_query
          ->getParams();

        // Extract keys.
        $keys = $query
          ->getKeys();
        $query_fields_boosted = $edismax
          ->getQueryFields();
        if (isset($params['defType']) && 'edismax' == $params['defType'] || !$query_fields_boosted) {

          // Edismax was forced via API or the query fields were removed via
          // API (like the multilingual backend does).
          $keys = $this
            ->flattenKeys($keys, [], $parse_mode_id);
        }
        else {
          $keys = $this
            ->flattenKeys($keys, explode(' ', $query_fields_boosted), $parse_mode_id);
        }
        if (!empty($keys)) {

          // Set them.
          $solarium_query
            ->setQuery($keys);
        }
        if (!isset($params['defType']) || 'edismax' != $params['defType']) {
          $solarium_query
            ->removeComponent(ComponentAwareQueryInterface::COMPONENT_EDISMAX);
        }
      }

      // 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);
      $body = $response
        ->getBody();
      if (200 != $response
        ->getStatusCode()) {
        throw new SearchApiSolrException(strip_tags($body), $response
          ->getStatusCode());
      }
      $this
        ->alterSolrResponseBody($body, $query);
      $response = new Response($body, $response
        ->getHeaders());
      $solarium_result = $connector
        ->createSearchResult($solarium_query, $response);

      // Extract results.
      $search_api_result_set = $this
        ->extractResults($query, $solarium_result);

      // Add warnings, if present.
      if (!empty($warnings)) {
        foreach ($warnings as $warning) {
          $search_api_result_set
            ->addWarning($warning);
        }
      }

      // Extract facets.
      if ($solarium_result instanceof Result) {
        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, $field_names)) {
            $search_api_result_set
              ->setExtraData('search_api_facets', $search_api_facets);
          }
        }
      }
      $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);
    }
  }
}