You are here

abstract class AbstractSearchApiSolrMultilingualBackend in Search API Solr 8.2

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

Hierarchy

Expanded class hierarchy of AbstractSearchApiSolrMultilingualBackend

File

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

Namespace

Drupal\search_api_solr\Plugin\search_api\backend
View source
abstract class AbstractSearchApiSolrMultilingualBackend extends SearchApiSolrBackend implements SolrMultilingualBackendInterface {

  /**
   * The unprocessed search keys.
   *
   * @var mixed
   */
  protected $origKeys = FALSE;

  /**
   * Creates and deploys a missing dynamic Solr field if the server supports it.
   *
   * @param string $solr_field_name
   *   The name of the new dynamic Solr field.
   *
   * @param string $solr_field_type_name
   *   The name of the Solr Field Type to be used for the new dynamic Solr
   *   field.
   */
  protected abstract function createSolrDynamicField($solr_field_name, $solr_field_type_name);

  /**
   * Creates and deploys a missing Solr Field Type if the server supports it.
   *
   * @param string $solr_field_type_name
   *   The name of the Solr Field Type.
   */
  protected abstract function createSolrMultilingualFieldType($solr_field_type_name);

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['multilingual'] = [
      '#type' => 'fieldset',
      '#title' => $this
        ->t('Multilingual'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
    ];
    $form['multilingual']['sasm_limit_search_page_to_content_language'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Limit to current content language.'),
      '#description' => $this
        ->t('Limit all search results for custom queries or search pages not managed by Views to current content language if no language is specified in the query.'),
      '#default_value' => isset($this->configuration['sasm_limit_search_page_to_content_language']) ? $this->configuration['sasm_limit_search_page_to_content_language'] : FALSE,
    ];
    $form['multilingual']['sasm_search_page_include_language_independent'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Include language independent content in search results.'),
      '#description' => $this
        ->t('This option will include content without a language assigned in the results of custom queries or search pages not managed by Views. For example, if you search for English content, but have an article with languague of "undefined", you will see those results as well. If you disable this option, you will only see content that matches the language.'),
      '#default_value' => isset($this->configuration['sasm_search_page_include_language_independent']) ? $this->configuration['sasm_search_page_include_language_independent'] : FALSE,
    ];
    $form['multilingual']['sasm_language_unspecific_fallback_on_schema_issues'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Use language fallbacks.'),
      '#description' => $this
        ->t('This option is suitable for two use-cases. First, if you have languages like "de" and "de-at", both could be handled by a shared configuration for "de". Second, new languages will be handled by language-unspecific fallback configuration until the schema gets updated on your Solr server.'),
      '#default_value' => isset($this->configuration['sasm_language_unspecific_fallback_on_schema_issues']) ? $this->configuration['sasm_language_unspecific_fallback_on_schema_issues'] : TRUE,
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::validateConfigurationForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state
      ->getValues();

    // Since the form is nested into another, we can't simply use #parents for
    // doing this array restructuring magic. (At least not without creating an
    // unnecessary dependency on internal implementation.)
    foreach ($values['multilingual'] as $key => $value) {
      $form_state
        ->setValue($key, $value);
    }

    // Clean-up the form to avoid redundant entries in the stored configuration.
    $form_state
      ->unsetValue('multilingual');
    parent::submitConfigurationForm($form, $form_state);
  }

  /**
   * Adjusts the language filter before converting the query into a Solr query.
   *
   * @param \Drupal\search_api\Query\QueryInterface $query
   *   The \Drupal\search_api\Query\Query object.
   */
  protected function alterSearchApiQuery(QueryInterface $query) {

    // Do not modify 'Server index status' queries.
    // @see https://www.drupal.org/node/2668852
    if ($query
      ->hasTag('server_index_status') || $query
      ->hasTag('mlt')) {
      return;
    }
    parent::alterSearchApiQuery($query);
    $languages = $query
      ->getLanguages();

    // If there are no languages set, we need to set them.
    // As an example, a language might be set by a filter in a search view.
    if (empty($languages)) {
      if (!$query
        ->hasTag('views') && $this->configuration['sasm_limit_search_page_to_content_language']) {

        // Limit the language to the current language being used.
        $languages[] = \Drupal::languageManager()
          ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
          ->getId();
      }
      else {

        // If the query is generated by views and/or the query isn't limited by
        // any languages we have to search for all languages using their
        // specific fields.
        $languages = array_keys(\Drupal::languageManager()
          ->getLanguages());
      }
    }
    if ($this->configuration['sasm_search_page_include_language_independent']) {
      $languages[] = LanguageInterface::LANGCODE_NOT_SPECIFIED;
      $languages[] = LanguageInterface::LANGCODE_NOT_APPLICABLE;
    }
    $query
      ->setLanguages($languages);
  }

  /**
   * Modify the query before it is sent to solr.
   *
   * Replaces all language unspecific fulltext query fields by language specific
   * ones.
   *
   * @param \Solarium\Core\Query\QueryInterface $solarium_query
   *   The Solarium select query object.
   * @param \Drupal\search_api\Query\QueryInterface $query
   *   The \Drupal\search_api\Query\Query object representing the executed
   *   search query.
   */
  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([]);
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function getFilterQueries(QueryInterface $query, array $solr_fields, array $index_fields, array &$options) {
    $condition_group = $query
      ->getConditionGroup();
    $conditions = $condition_group
      ->getConditions();
    if (empty($conditions) || empty($query
      ->getLanguages())) {
      return parent::getFilterQueries($query, $solr_fields, $index_fields, $options);
    }
    $fq = [];
    foreach ($conditions as $condition) {
      $language_fqs = [];
      foreach ($query
        ->getLanguages() as $langcode) {
        $language_specific_condition_group = $query
          ->createConditionGroup();
        $language_specific_condition_group
          ->addCondition(SEARCH_API_LANGUAGE_FIELD_NAME, $langcode);
        $language_specific_conditions =& $language_specific_condition_group
          ->getConditions();
        $language_specific_conditions[] = $condition;
        $language_fqs = array_merge($language_fqs, $this
          ->reduceFilterQueries($this
          ->createFilterQueries($language_specific_condition_group, $this
          ->getLanguageSpecificSolrFieldNames($langcode, $solr_fields, reset($index_fields)
          ->getIndex()), $index_fields, $options, $query), $condition_group));
      }
      $language_aware_condition_group = $query
        ->createConditionGroup('OR');
      $fq = array_merge($fq, $this
        ->reduceFilterQueries($language_fqs, $language_aware_condition_group, TRUE));
    }
    return $fq;
  }

  /**
   * Gets a language-specific mapping from Drupal to Solr field names.
   *
   * @param string $langcode
   *   The lanaguage to get the mapping for.
   * @param array $solr_fields
   *   The mapping from Drupal to Solr field names.
   * @param \Drupal\search_api\IndexInterface $index_fields
   *   The fields handled by the curent index.
   *
   * @return array
   *   The language-specific mapping from Drupal to Solr field names.
   */
  protected function getLanguageSpecificSolrFieldNames($lancgcode, array $solr_fields, IndexInterface $index) {

    // @todo Caching.
    foreach ($index
      ->getFulltextFields() as $fulltext_field) {
      $solr_fields[$fulltext_field] = Utility::encodeSolrName(Utility::getLanguageSpecificSolrDynamicFieldNameForSolrDynamicFieldName($solr_fields[$fulltext_field], $lancgcode));
    }
    return $solr_fields;
  }

  /**
   * @inheritdoc
   */
  protected function alterSolrResponseBody(&$body, QueryInterface $query) {
    $data = json_decode($body);
    $index = $query
      ->getIndex();
    $field_names = $this
      ->getSolrFieldNames($index, TRUE);
    $doc_languages = [];
    if (isset($data->response)) {
      foreach ($data->response->docs as $doc) {
        $language_id = $doc_languages[$this
          ->createId($this
          ->getIndexId($index), $doc->{$field_names['search_api_id']})] = $doc->{$field_names[SEARCH_API_LANGUAGE_FIELD_NAME]};
        foreach (array_keys(get_object_vars($doc)) as $language_specific_field_name) {
          $field_name = Utility::getSolrDynamicFieldNameForLanguageSpecificSolrDynamicFieldName($language_specific_field_name);
          if ($field_name != $language_specific_field_name) {
            if (Utility::getLanguageIdFromLanguageSpecificSolrDynamicFieldName($language_specific_field_name) == $language_id) {
              $doc->{$field_name} = $doc->{$language_specific_field_name};
              unset($doc->{$language_specific_field_name});
            }
          }
        }
      }
    }
    if (isset($data->highlighting)) {
      foreach ($data->highlighting as $solr_id => &$item) {
        foreach (array_keys(get_object_vars($item)) as $language_specific_field_name) {
          $field_name = Utility::getSolrDynamicFieldNameForLanguageSpecificSolrDynamicFieldName($language_specific_field_name);
          if ($field_name != $language_specific_field_name) {
            if (Utility::getLanguageIdFromLanguageSpecificSolrDynamicFieldName($language_specific_field_name) == $doc_languages[$solr_id]) {
              $item->{$field_name} = $item->{$language_specific_field_name};
              unset($item->{$language_specific_field_name});
            }
          }
        }
      }
    }
    if (isset($data->facet_counts)) {
      $facet_set_helper = new FacetSet();
      foreach (get_object_vars($data->facet_counts->facet_fields) as $language_specific_field_name => $facet_terms) {
        $field_name = Utility::getSolrDynamicFieldNameForLanguageSpecificSolrDynamicFieldName($language_specific_field_name);
        if ($field_name != $language_specific_field_name) {
          if (isset($data->facet_counts->facet_fields->{$field_name})) {

            // @todo this simple merge of all language specific fields to one
            //   language unspecific fields should be configurable.
            $key_value = $facet_set_helper
              ->convertToKeyValueArray($data->facet_counts->facet_fields->{$field_name}) + $facet_set_helper
              ->convertToKeyValueArray($facet_terms);
            $facet_terms = [];
            foreach ($key_value as $key => $value) {

              // @todo check for NULL key of "missing facets".
              $facet_terms[] = $key;
              $facet_terms[] = $value;
            }
          }
          $data->facet_counts->facet_fields->{$field_name} = $facet_terms;
        }
      }
    }
    $body = json_encode($data);
  }

  /**
   * {@inheritdoc}
   */
  protected function postQuery(ResultSetInterface $results, QueryInterface $query, $response) {
    if ($this->origKeys) {
      $query
        ->keys($this->origKeys);
    }
    parent::postQuery($results, $query, $response);
  }

  /**
   * Replaces language unspecific fulltext fields by language specific ones.
   *
   * @param \Solarium\QueryType\Update\Query\Document\Document[] $documents
   *   An array of \Solarium\QueryType\Update\Query\Document\Document objects
   *   ready to be indexed, generated from $items array.
   * @param \Drupal\search_api\IndexInterface $index
   *   The search index for which items are being indexed.
   * @param array $items
   *   An array of items being indexed.
   *
   * @see hook_search_api_solr_documents_alter()
   */
  protected function alterSolrDocuments(array &$documents, IndexInterface $index, array $items) {
    parent::alterSolrDocuments($documents, $index, $items);
    $fulltext_fields = $index
      ->getFulltextFields();
    $multiple_field_names = $this
      ->getSolrFieldNames($index);
    $field_names = $this
      ->getSolrFieldNames($index, TRUE);
    $fulltext_field_names = array_filter(array_flip($multiple_field_names) + array_flip($field_names), function ($value) use ($fulltext_fields) {
      return in_array($value, $fulltext_fields);
    });
    $field_name_map_per_language = [];
    foreach ($documents as $document) {
      $fields = $document
        ->getFields();
      $language_id = $fields[$field_names[SEARCH_API_LANGUAGE_FIELD_NAME]];
      foreach ($fields as $monolingual_solr_field_name => $field_value) {
        if (array_key_exists($monolingual_solr_field_name, $fulltext_field_names)) {
          $multilingual_solr_field_name = Utility::getLanguageSpecificSolrDynamicFieldNameForSolrDynamicFieldName($monolingual_solr_field_name, $language_id);
          $field_name_map_per_language[$language_id][$monolingual_solr_field_name] = Utility::encodeSolrName($multilingual_solr_field_name);
        }
      }
    }
    foreach ($field_name_map_per_language as $language_id => $map) {
      $solr_field_type_name = Utility::encodeSolrName('text' . '_' . $language_id);
      if (!$this
        ->isPartOfSchema('fieldTypes', $solr_field_type_name) && !$this
        ->createSolrMultilingualFieldType($solr_field_type_name) && !$this
        ->hasLanguageUndefinedFallback()) {
        throw new SearchApiSolrException('Missing field type ' . $solr_field_type_name . ' in schema.');
      }

      // Handle dynamic fields for multilingual tm and ts.
      foreach ([
        'ts',
        'tm',
      ] as $prefix) {
        $multilingual_solr_field_name = Utility::encodeSolrName(Utility::getLanguageSpecificSolrDynamicFieldPrefix($prefix, $language_id)) . '*';
        if (!$this
          ->isPartOfSchema('dynamicFields', $multilingual_solr_field_name) && !$this
          ->createSolrDynamicField($multilingual_solr_field_name, $solr_field_type_name) && !$this
          ->hasLanguageUndefinedFallback()) {
          throw new SearchApiSolrException('Missing dynamic field ' . $multilingual_solr_field_name . ' in schema.');
        }
      }
    }
    foreach ($documents as $document) {
      $fields = $document
        ->getFields();
      foreach ($field_name_map_per_language as $language_id => $map) {
        if ($fields[$field_names[SEARCH_API_LANGUAGE_FIELD_NAME]] == $language_id) {
          foreach ($fields as $monolingual_solr_field_name => $value) {
            if (isset($map[$monolingual_solr_field_name])) {
              if ('twm_suggest' != $monolingual_solr_field_name) {
                $document
                  ->addField($map[$monolingual_solr_field_name], $value, $document
                  ->getFieldBoost($monolingual_solr_field_name));
                $document
                  ->removeField($monolingual_solr_field_name);
              }
            }
          }
        }
      }
    }
  }

  /**
   * Indicates if an 'element' is part of the Solr server's schema.
   *
   * @param string $kind
   *   The kind of the element, for example 'dynamicFields' or 'fieldTypes'.
   *
   * @param string $name
   *   The name of the element.
   *
   * @return bool
   *   True if an element of the given kind and name exists, false otherwise.
   *
   * @throws \Drupal\search_api_solr\SearchApiSolrException
   */
  protected function isPartOfSchema($kind, $name) {
    static $previous_calls;
    $state_key = 'sasm.' . $this
      ->getServer()
      ->id() . '.schema_parts';
    $state = \Drupal::state();
    $schema_parts = $state
      ->get($state_key);

    // @todo reset that drupal state from time to time
    if (!is_array($schema_parts) || empty($schema_parts[$kind]) || !in_array($name, $schema_parts[$kind]) && !isset($previous_calls[$kind])) {
      $response = $this
        ->getSolrConnector()
        ->coreRestGet('schema/' . strtolower($kind));
      if (empty($response[$kind])) {
        throw new SearchApiSolrException('Missing information about ' . $kind . ' in response to REST request.');
      }

      // Delete the old state.
      $schema_parts[$kind] = [];
      foreach ($response[$kind] as $row) {
        $schema_parts[$kind][] = $row['name'];
      }
      $state
        ->set($state_key, $schema_parts);
      $previous_calls[$kind] = TRUE;
    }
    return in_array($name, $schema_parts[$kind]);
  }

  /**
   * {@inheritdoc}
   */
  public function getSchemaLanguageStatistics() {
    $available = $this
      ->getSolrConnector()
      ->pingCore();
    $stats = [];
    foreach (\Drupal::languageManager()
      ->getLanguages() as $language) {
      $solr_field_type_name = Utility::encodeSolrName('text' . '_' . $language
        ->getId());
      $stats[$language
        ->getId()] = $available ? $this
        ->isPartOfSchema('fieldTypes', $solr_field_type_name) : FALSE;
    }
    return $stats;
  }

  /**
   * {@inheritdoc}
   */
  public function hasLanguageUndefinedFallback() {
    return isset($this->configuration['sasm_language_unspecific_fallback_on_schema_issues']) ? $this->configuration['sasm_language_unspecific_fallback_on_schema_issues'] : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  protected function setFacets(QueryInterface $query, Query $solarium_query, array $field_names) {
    parent::setFacets($query, $solarium_query, $field_names);
    if ($languages = $query
      ->getLanguages()) {
      foreach ($languages as $language) {
        $language_specific_field_names = $this
          ->getLanguageSpecificSolrFieldNames($language, $field_names, $query
          ->getIndex());
        parent::setFacets($query, $solarium_query, array_diff_assoc($language_specific_field_names, $field_names));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function getAutocompleteFields(QueryInterface $query) {
    $fl = [];
    $solr_field_names = $this
      ->getSolrFieldNames($query
      ->getIndex());
    foreach ($query
      ->getLanguages() as $langcode) {

      // We explicit allow to get terms from twm_suggest. Therefore we call
      // BackendPluginBase::getQueryFulltextFields() to not filter twm_suggest.
      $fulltext_fields = BackendPluginBase::getQueryFulltextFields($query);
      $language_specific_fulltext_fields = $this
        ->getLanguageSpecificSolrFieldNames($langcode, $solr_field_names, $query
        ->getIndex());
      foreach ($fulltext_fields as $fulltext_field) {
        $fl[] = $language_specific_fulltext_fields[$fulltext_field];
      }
    }
    return array_unique($fl);
  }

  /**
   * {@inheritdoc}
   */
  protected function setAutocompleteSuggesterQuery(QueryInterface $query, AutocompleteQuery $solarium_query, $user_input, $options = []) {
    if (isset($options['context_filter_tags']) && in_array('drupal/langcode:multilingual', $options['context_filter_tags'])) {
      $langcodes = $query
        ->getLanguages();
      if (count($langcodes) == 1) {
        $langcode = reset($langcodes);
        $options['context_filter_tags'] = str_replace('drupal/langcode:multilingual', 'drupal/langcode:' . $langcode, $options['context_filter_tags']);
        $options['dictionary'] = $langcode;
      }
      else {
        foreach ($options['context_filter_tags'] as $key => $tag) {
          if ('drupal/langcode:multilingual' == $tag) {
            unset($options['context_filter_tags'][$key]);
            break;
          }
        }
      }
    }
    parent::setAutocompleteSuggesterQuery($query, $solarium_query, $user_input, $options);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AbstractSearchApiSolrMultilingualBackend::$origKeys protected property The unprocessed search keys.
AbstractSearchApiSolrMultilingualBackend::alterSearchApiQuery protected function Adjusts the language filter before converting the query into a Solr query. Overrides SearchApiSolrBackend::alterSearchApiQuery
AbstractSearchApiSolrMultilingualBackend::alterSolrDocuments protected function Replaces language unspecific fulltext fields by language specific ones. Overrides SearchApiSolrBackend::alterSolrDocuments
AbstractSearchApiSolrMultilingualBackend::alterSolrResponseBody protected function @inheritdoc Overrides SearchApiSolrBackend::alterSolrResponseBody
AbstractSearchApiSolrMultilingualBackend::buildConfigurationForm public function Form constructor. Overrides SearchApiSolrBackend::buildConfigurationForm
AbstractSearchApiSolrMultilingualBackend::createSolrDynamicField abstract protected function Creates and deploys a missing dynamic Solr field if the server supports it. 2
AbstractSearchApiSolrMultilingualBackend::createSolrMultilingualFieldType abstract protected function Creates and deploys a missing Solr Field Type if the server supports it. 2
AbstractSearchApiSolrMultilingualBackend::getAutocompleteFields protected function Get the fields to search for autocomplete terms. Overrides SearchApiSolrBackend::getAutocompleteFields
AbstractSearchApiSolrMultilingualBackend::getFilterQueries protected function Serializes a query's conditions as Solr filter queries. Overrides SearchApiSolrBackend::getFilterQueries
AbstractSearchApiSolrMultilingualBackend::getLanguageSpecificSolrFieldNames protected function Gets a language-specific mapping from Drupal to Solr field names.
AbstractSearchApiSolrMultilingualBackend::getSchemaLanguageStatistics public function Gets schema language statistics for the multilingual Solr server. Overrides SolrMultilingualBackendInterface::getSchemaLanguageStatistics
AbstractSearchApiSolrMultilingualBackend::hasLanguageUndefinedFallback public function Indicates if the fallback for not supported languages is active. Overrides SolrMultilingualBackendInterface::hasLanguageUndefinedFallback
AbstractSearchApiSolrMultilingualBackend::isPartOfSchema protected function Indicates if an 'element' is part of the Solr server's schema.
AbstractSearchApiSolrMultilingualBackend::postQuery protected function Allow custom changes before search results are returned for subclasses. Overrides SearchApiSolrBackend::postQuery
AbstractSearchApiSolrMultilingualBackend::preQuery protected function Modify the query before it is sent to solr. Overrides SearchApiSolrBackend::preQuery
AbstractSearchApiSolrMultilingualBackend::setAutocompleteSuggesterQuery protected function Set the suggester parameters for the solarium autocomplete query. Overrides SearchApiSolrBackend::setAutocompleteSuggesterQuery
AbstractSearchApiSolrMultilingualBackend::setFacets protected function Helper method for creating the facet field parameters. Overrides SearchApiSolrBackend::setFacets
AbstractSearchApiSolrMultilingualBackend::submitConfigurationForm public function Form submission handler. Overrides SearchApiSolrBackend::submitConfigurationForm
AbstractSearchApiSolrMultilingualBackend::validateConfigurationForm public function Form validation handler. Overrides SearchApiSolrBackend::validateConfigurationForm
BackendPluginBase::$messenger protected property The messenger. Overrides MessengerTrait::$messenger
BackendPluginBase::$server protected property The server this backend is configured for.
BackendPluginBase::$serverId protected property The backend's server's ID.
BackendPluginBase::addIndex public function Adds a new index to this server. Overrides BackendSpecificInterface::addIndex 2
BackendPluginBase::getFieldsHelper public function Retrieves the fields helper.
BackendPluginBase::getMessenger public function Retrieves the messenger.
BackendPluginBase::getServer public function Retrieves the server entity for this backend. Overrides BackendInterface::getServer
BackendPluginBase::getSpecialFields protected function Creates dummy field objects for the "magic" fields present for every index. 1
BackendPluginBase::postInsert public function Reacts to the server's creation. Overrides BackendInterface::postInsert 1
BackendPluginBase::postUpdate public function Notifies the backend that its configuration was updated. Overrides BackendInterface::postUpdate 2
BackendPluginBase::preDelete public function Notifies the backend that the server is about to be deleted. Overrides BackendInterface::preDelete 1
BackendPluginBase::preUpdate public function Notifies the backend that its configuration is about to be updated. Overrides BackendInterface::preUpdate 1
BackendPluginBase::setFieldsHelper public function Sets the fields helper.
BackendPluginBase::setMessenger public function Sets the messenger. Overrides MessengerTrait::setMessenger
BackendPluginBase::setServer public function Sets the server entity for this backend. Overrides BackendInterface::setServer
BackendPluginBase::validateOperator protected function Verifies that the given condition operator is valid for this backend.
BackendPluginBase::__sleep public function Implements the magic __sleep() method. Overrides DependencySerializationTrait::__sleep 1
BackendPluginBase::__wakeup public function Implements the magic __wakeup() method. Overrides DependencySerializationTrait::__wakeup 1
ConfigurablePluginBase::calculatePluginDependencies Deprecated protected function Calculates and adds dependencies of a specific plugin instance.
ConfigurablePluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
ConfigurablePluginBase::getDescription public function Returns the plugin's description. Overrides ConfigurablePluginInterface::getDescription
ConfigurablePluginBase::getPluginDependencies Deprecated protected function Calculates and returns dependencies of a specific plugin instance.
ConfigurablePluginBase::label public function Returns the label for use on the administration pages. Overrides ConfigurablePluginInterface::label
ConfigurablePluginBase::moduleHandler Deprecated protected function Wraps the module handler.
ConfigurablePluginBase::onDependencyRemoval public function Informs the plugin that some of its dependencies are being removed. Overrides ConfigurablePluginInterface::onDependencyRemoval 5
ConfigurablePluginBase::themeHandler Deprecated protected function Wraps the theme handler.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencyTrait::$dependencies protected property The object's dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
HideablePluginBase::isHidden public function Determines whether this plugin should be hidden in the UI. Overrides HideablePluginInterface::isHidden 1
LoggerTrait::$logger protected property The logging channel to use.
LoggerTrait::getLogger public function Retrieves the logger.
LoggerTrait::logException protected function Logs an exception.
LoggerTrait::setLogger public function Sets the logger.
MessengerTrait::messenger public function Gets the messenger. 29
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginDependencyTrait::calculatePluginDependencies protected function Calculates and adds dependencies of a specific plugin instance. Aliased as: traitCalculatePluginDependencies 1
PluginDependencyTrait::getPluginDependencies protected function Calculates and returns dependencies of a specific plugin instance. Aliased as: traitGetPluginDependencies
PluginDependencyTrait::moduleHandler protected function Wraps the module handler. Aliased as: traitModuleHandler 1
PluginDependencyTrait::themeHandler protected function Wraps the theme handler. Aliased as: traitThemeHandler 1
PluginFormTrait::submitConfigurationForm public function Form submission handler. Aliased as: traitSubmitConfigurationForm 7
SearchApiSolrBackend::$dataTypeHelper protected property The data type helper.
SearchApiSolrBackend::$fieldNames protected property Metadata describing fields on the Solr/Lucene index.
SearchApiSolrBackend::$fieldsHelper protected property Overrides BackendPluginBase::$fieldsHelper
SearchApiSolrBackend::$languageManager protected property The language manager.
SearchApiSolrBackend::$moduleHandler protected property The module handler.
SearchApiSolrBackend::$queryHelper protected property The Solarium query helper.
SearchApiSolrBackend::$searchApiSolrSettings protected property A config object for 'search_api_solr.settings'.
SearchApiSolrBackend::$solrConnector protected property
SearchApiSolrBackend::$solrConnectorPluginManager protected property The backend plugin manager.
SearchApiSolrBackend::addIndexField protected function Helper method for indexing.
SearchApiSolrBackend::addLanguageConditions protected function Adds item language conditions to the condition group, if applicable.
SearchApiSolrBackend::alterSpellcheckAutocompleteQuery public function Allow custom changes to the Solarium Spellcheck autocomplete query. Overrides SolrAutocompleteInterface::alterSpellcheckAutocompleteQuery
SearchApiSolrBackend::alterSuggesterAutocompleteQuery public function Allow custom changes to the Solarium Suggester autocomplete query. Overrides SolrAutocompleteInterface::alterSuggesterAutocompleteQuery
SearchApiSolrBackend::alterTermsAutocompleteQuery public function Allow custom changes to the Solarium Terms autocomplete query. Overrides SolrAutocompleteInterface::alterTermsAutocompleteQuery
SearchApiSolrBackend::applySearchWorkarounds protected function Apply workarounds for special Solr versions before searching. 1
SearchApiSolrBackend::buildAjaxSolrConnectorConfigForm public static function Handles switching the selected Solr connector plugin.
SearchApiSolrBackend::buildConnectorConfigForm public function Builds the backend-specific configuration form.
SearchApiSolrBackend::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides ConfigurablePluginBase::calculateDependencies
SearchApiSolrBackend::create public static function Creates an instance of the plugin. Overrides BackendPluginBase::create
SearchApiSolrBackend::createFilterQueries protected function Recursively transforms conditions into a flat array of Solr filter queries.
SearchApiSolrBackend::createFilterQuery protected function Create a single search query string.
SearchApiSolrBackend::createId protected function Creates an ID used as the unique identifier at the Solr server.
SearchApiSolrBackend::createLocationFilterQuery protected function Create a single search query string.
SearchApiSolrBackend::defaultConfiguration public function Gets default configuration for this plugin. Overrides ConfigurablePluginBase::defaultConfiguration 1
SearchApiSolrBackend::deleteAllIndexItems public function Deletes all the items from the index. Overrides BackendSpecificInterface::deleteAllIndexItems
SearchApiSolrBackend::deleteItems public function Deletes the specified items from the index. Overrides BackendSpecificInterface::deleteItems
SearchApiSolrBackend::executeGraphStreamingExpression public function Executes a graph streaming expression. Overrides SolrBackendInterface::executeGraphStreamingExpression
SearchApiSolrBackend::executeStreamingExpression public function Executes a streaming expression. Overrides SolrBackendInterface::executeStreamingExpression
SearchApiSolrBackend::extractContentFromFile public function Extract a file's content using tika within a solr server. Overrides SolrBackendInterface::extractContentFromFile
SearchApiSolrBackend::extractFacets protected function Extracts facets from a Solarium result set.
SearchApiSolrBackend::extractResults protected function Extract results from a Solr response.
SearchApiSolrBackend::filterDuplicateAutocompleteSuggestions protected function
SearchApiSolrBackend::finalizeIndex public function Overrides SolrBackendInterface::finalizeIndex
SearchApiSolrBackend::flattenKeys protected function Flattens keys and fields into a single search string.
SearchApiSolrBackend::formatDate public function Tries to format given date with solarium query helper.
SearchApiSolrBackend::formatFilterValue protected function Format a value for filtering on a field of a specific type.
SearchApiSolrBackend::getAutocompleteQuery protected function
SearchApiSolrBackend::getAutocompleteSpellCheckSuggestions protected function Get the spellcheck suggestions from the autocomplete query result.
SearchApiSolrBackend::getAutocompleteSuggesterSuggestions protected function Get the term suggestions from the autocomplete query result.
SearchApiSolrBackend::getAutocompleteSuggestions public function Implements autocomplete compatible to AutocompleteBackendInterface.
SearchApiSolrBackend::getAutocompleteTermSuggestions protected function Get the term suggestions from the autocomplete query result.
SearchApiSolrBackend::getBackendDefinedFields public function Provides information on additional fields made available by the backend. Overrides BackendPluginBase::getBackendDefinedFields
SearchApiSolrBackend::getDiscouragedProcessors public function Limits the processors displayed in the UI for indexes on this server. Overrides BackendPluginBase::getDiscouragedProcessors
SearchApiSolrBackend::getDocument public function Retrieves a Solr document from an search api index item. Overrides SolrBackendInterface::getDocument
SearchApiSolrBackend::getDocuments public function Retrieves Solr documents from search api index items. Overrides SolrBackendInterface::getDocuments
SearchApiSolrBackend::getDomain public function Returns the targeted content domain of the server. Overrides SolrBackendInterface::getDomain
SearchApiSolrBackend::getHighlighting protected function Extract and format highlighting information for a specific item.
SearchApiSolrBackend::getIndexFilterQueryString public function Returns a ready to use query string to filter results by index and site. Overrides SolrBackendInterface::getIndexFilterQueryString
SearchApiSolrBackend::getIndexId protected function Prefixes an index ID as configured.
SearchApiSolrBackend::getMoreLikeThisQuery protected function Changes the query to a "More Like This" query.
SearchApiSolrBackend::getPropertyPathCardinality protected function Computes the cardinality of a complete property path.
SearchApiSolrBackend::getQueryFulltextFields protected function Don't return the big twm_suggest field. Overrides BackendPluginBase::getQueryFulltextFields
SearchApiSolrBackend::getSolrConnector public function Overrides SolrBackendInterface::getSolrConnector
SearchApiSolrBackend::getSolrConnectorOptions protected function Returns all available backend plugins, as an options list.
SearchApiSolrBackend::getSolrFieldNames public function Creates a list of all indexed field names mapped to their Solr field names. Overrides SolrBackendInterface::getSolrFieldNames 1
SearchApiSolrBackend::getSpellcheckSuggestions public function Autocompletion suggestions for some user input using Spellcheck component. Overrides SolrAutocompleteInterface::getSpellcheckSuggestions
SearchApiSolrBackend::getSuggesterSuggestions public function Autocompletion suggestions for some user input using Suggester component. Overrides SolrAutocompleteInterface::getSuggesterSuggestions
SearchApiSolrBackend::getSupportedFeatures public function Returns all features that this backend supports. Overrides BackendPluginBase::getSupportedFeatures
SearchApiSolrBackend::getTermsSuggestions public function Autocompletion suggestions for some user input using Terms component. Overrides SolrAutocompleteInterface::getTermsSuggestions
SearchApiSolrBackend::hasHierarchicalProperties protected function Checks if hierarchical properties are nested on an entity-typed property.
SearchApiSolrBackend::indexFieldsUpdated protected function Checks if the recently updated index had any fields changed.
SearchApiSolrBackend::indexItems public function Indexes the specified items. Overrides BackendSpecificInterface::indexItems
SearchApiSolrBackend::isAvailable public function Overrides BackendPluginBase::isAvailable
SearchApiSolrBackend::isHierarchicalField protected function Checks if a field is (potentially) hierarchical.
SearchApiSolrBackend::isManagedSchema public function Indicates if the Solr server uses a managed schema. Overrides SolrBackendInterface::isManagedSchema 1
SearchApiSolrBackend::reduceFilterQueries protected function Reduces an array of filter queries to an array containing one filter query.
SearchApiSolrBackend::removeIndex public function Removes an index from this server. Overrides BackendPluginBase::removeIndex
SearchApiSolrBackend::search public function 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: Overrides BackendSpecificInterface::search
SearchApiSolrBackend::setAutocompleteSpellCheckQuery protected function Set the spellcheck parameters for the solarium autocomplete query.
SearchApiSolrBackend::setAutocompleteTermQuery protected function Set the term parameters for the solarium autocomplete query.
SearchApiSolrBackend::setConfiguration public function Sets the configuration for this plugin instance. Overrides BackendPluginBase::setConfiguration
SearchApiSolrBackend::setFields protected function Set the list of fields Solr should return as result.
SearchApiSolrBackend::setGrouping protected function Sets grouping for the query.
SearchApiSolrBackend::setHighlighting protected function Sets the highlighting parameters.
SearchApiSolrBackend::setRpt protected function Adds rpt spatial features to the search query.
SearchApiSolrBackend::setSorts protected function Sets sorting for the query.
SearchApiSolrBackend::setSpatial protected function Adds spatial features to the search query.
SearchApiSolrBackend::supportsDataType public function Determines whether the backend supports a given add-on data type. Overrides BackendPluginBase::supportsDataType
SearchApiSolrBackend::updateIndex public function Notifies the server that an index attached to it has been changed. Overrides BackendPluginBase::updateIndex
SearchApiSolrBackend::viewSettings public function Overrides BackendPluginBase::viewSettings
SearchApiSolrBackend::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides BackendPluginBase::__construct
SolrCommitTrait::ensureCommit protected function Explicitly sent a commit command to a Solr server.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.