You are here

protected function AbstractSearchApiSolrMultilingualBackend::preQuery in Search API Solr 8.2

Modify the query before it is sent to solr.

Replaces all language unspecific fulltext query fields by language specific ones.

Parameters

\Solarium\Core\Query\QueryInterface $solarium_query: The Solarium select query object.

\Drupal\search_api\Query\QueryInterface $query: The \Drupal\search_api\Query\Query object representing the executed search query.

Overrides SearchApiSolrBackend::preQuery

File

src/Plugin/search_api/backend/AbstractSearchApiSolrMultilingualBackend.php, line 174

Class

AbstractSearchApiSolrMultilingualBackend
A abstract base class for all multilingual Solr Search API backends.

Namespace

Drupal\search_api_solr\Plugin\search_api\backend

Code

protected function preQuery(SolariumQueryInterface $solarium_query, QueryInterface $query) {

  // Do not modify 'Server index status' queries.
  // @see https://www.drupal.org/node/2668852
  if ($query
    ->hasTag('server_index_status')) {
    return;
  }
  parent::preQuery($solarium_query, $query);
  $language_ids = $query
    ->getLanguages();
  if (!empty($language_ids)) {
    $index = $query
      ->getIndex();
    $fulltext_fields = $this
      ->getQueryFulltextFields($query);
    $field_names = $this
      ->getSolrFieldNames($index);
    $language_specific_fields = [];
    foreach ($language_ids as $language_id) {
      foreach ($fulltext_fields as $fulltext_field) {
        $field_name = $field_names[$fulltext_field];
        $language_specific_fields[$language_id][$field_name] = Utility::encodeSolrName(Utility::getLanguageSpecificSolrDynamicFieldNameForSolrDynamicFieldName($field_name, $language_id));
      }
    }
    $components = $solarium_query
      ->getComponents();
    if (isset($components[ComponentAwareQueryInterface::COMPONENT_HIGHLIGHTING])) {
      $hl = $solarium_query
        ->getHighlighting();
      $highlighted_fields = $hl
        ->getFields();
      foreach ($field_names as $field_name) {
        if (isset($highlighted_fields[$field_name])) {
          $exchanged = FALSE;
          foreach ($language_ids as $language_id) {
            if (isset($language_specific_fields[$language_id][$field_name])) {
              $language_specific_field = $language_specific_fields[$language_id][$field_name];

              // Copy the already set highlighting options over to the language
              // specific fields. getField() creates a new one first.
              $highlighted_field = $hl
                ->getField($language_specific_field);
              $highlighted_field
                ->setOptions($highlighted_fields[$field_name]
                ->getOptions());
              $highlighted_field
                ->setName($language_specific_field);
              $exchanged = TRUE;
            }
          }
          if ($exchanged) {
            $hl
              ->removeField($field_name);
          }
        }
      }
    }
    if (!empty($this->configuration['retrieve_data'])) {
      if ($fields_to_be_retrieved = $query
        ->getOption('search_api_retrieved_field_values', [])) {
        $language_specific_fields_to_retrieve = [];
        $fields_to_retrieve = $solarium_query
          ->getFields();
        foreach ($fields_to_retrieve as $key => $field_name) {
          $language_specific_name = FALSE;
          foreach ($language_ids as $language_id) {
            if (isset($language_specific_fields[$language_id][$field_name])) {
              $language_specific_fields_to_retrieve[] = $language_specific_fields[$language_id][$field_name];
              $language_specific_name = TRUE;
            }
          }
          if ($language_specific_name) {
            unset($fields_to_retrieve[$key]);
          }
        }
        if ($language_specific_fields_to_retrieve) {
          $solarium_query
            ->setFields(array_merge($language_specific_fields_to_retrieve, $fields_to_retrieve));
        }
      }
    }
    if ($query
      ->hasTag('mlt')) {
      $mlt_fields = [];
      foreach ($language_ids as $language_id) {

        /** @var \Solarium\QueryType\MoreLikeThis\Query $solarium_query */
        foreach ($solarium_query
          ->getMltFields() as $mlt_field) {
          if (isset($language_specific_fields[$language_id][$mlt_field])) {
            $mlt_fields[] = $language_specific_fields[$language_id][$mlt_field];
          }
          else {

            // Make sure untranslated fields are still kept.
            $mlt_fields[$mlt_field] = $mlt_field;
          }
        }
      }
      $solarium_query
        ->setMltFields($mlt_fields);
    }
    elseif ($keys = $query
      ->getKeys()) {

      /** @var \Solarium\QueryType\Select\Query\Query $solarium_query */
      $edismax = $solarium_query
        ->getEDisMax();
      if ($solr_fields = $edismax
        ->getQueryFields()) {
        $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';
        }
        $new_keys = [];
        foreach ($language_ids as $language_id) {
          $new_solr_fields = $solr_fields;
          foreach ($fulltext_fields as $fulltext_field) {
            $field_name = $field_names[$fulltext_field];
            $new_solr_fields = str_replace($field_name, $language_specific_fields[$language_id][$field_name], $new_solr_fields);
          }
          if ($new_solr_fields == $solr_fields) {

            // If there's no change for the first language, there won't be
            // any change for the other languages, too.
            return;
          }
          $flat_keys = $this
            ->flattenKeys($keys, explode(' ', $new_solr_fields), $parse_mode_id);
          if (strpos($flat_keys, '(') !== 0 && strpos($flat_keys, '+(') !== 0 && strpos($flat_keys, '-(') !== 0) {
            $flat_keys = '(' . $flat_keys . ')';
          }
          $new_keys[] = $flat_keys;
        }
        if (count($new_keys) > 1) {
          $new_keys['#conjunction'] = 'OR';
        }
        $new_keys['#escaped'] = TRUE;

        // Preserve the original keys to be set again in postQuery().
        $this->origKeys = $query
          ->getOriginalKeys();

        // The orginal keys array is now already flatten once per language.
        // that means that we already build Solr query strings containing
        // fields and keys. Now we set these query strings again as keys
        // using an OR conjunction but remove all query fields. That will
        // cause the parent class to just concatenate the language specific
        // query strings using OR as they are.
        $query
          ->keys($new_keys);
        $edismax
          ->setQueryFields([]);
      }
    }
  }
}