You are here

abstract class SolrConnectorPluginBase in Search API Solr 8

Same name and namespace in other branches
  1. 8.3 src/SolrConnector/SolrConnectorPluginBase.php \Drupal\search_api_solr\SolrConnector\SolrConnectorPluginBase
  2. 8.2 src/SolrConnector/SolrConnectorPluginBase.php \Drupal\search_api_solr\SolrConnector\SolrConnectorPluginBase
  3. 4.x src/SolrConnector/SolrConnectorPluginBase.php \Drupal\search_api_solr\SolrConnector\SolrConnectorPluginBase

Defines a base class for Solr connector plugins.

Plugins extending this class need to define a plugin definition array through annotation. These definition arrays may be altered through hook_search_api_solr_connector_info_alter(). The definition includes the following keys:

  • id: The unique, system-wide identifier of the backend class.
  • label: The human-readable name of the backend class, translated.
  • description: A human-readable description for the backend class, translated.

A complete plugin definition should be written as in this example:


@SolrConnector(
  id = "my_connector",
  label = @Translation("My connector"),
  description = @Translation("Authenticates with SuperAuth™.")
)

Hierarchy

Expanded class hierarchy of SolrConnectorPluginBase

See also

\Drupal\search_api_solr\Annotation\SolrConnector

\Drupal\search_api_solr\SolrConnector\SolrConnectorPluginManager

\Drupal\search_api_solr\SolrConnectorInterface

Plugin API

1 file declares its use of SolrConnectorPluginBase
StandardSolrConnector.php in src/Plugin/SolrConnector/StandardSolrConnector.php

File

src/SolrConnector/SolrConnectorPluginBase.php, line 58

Namespace

Drupal\search_api_solr\SolrConnector
View source
abstract class SolrConnectorPluginBase extends ConfigurablePluginBase implements SolrConnectorInterface, PluginFormInterface {
  use PluginFormTrait {
    submitConfigurationForm as traitSubmitConfigurationForm;
  }

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * A connection to the Solr server.
   *
   * @var \Solarium\Client
   */
  protected $solr;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $plugin->eventDispatcher = new Psr14Bridge();
    return $plugin;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'scheme' => 'http',
      'host' => 'localhost',
      'port' => '8983',
      'path' => '/solr',
      'core' => '',
      'timeout' => 5,
      'index_timeout' => 5,
      'optimize_timeout' => 10,
      'solr_version' => '',
      'http_method' => 'AUTO',
      'commit_within' => 1000,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form['scheme'] = array(
      '#type' => 'select',
      '#title' => $this
        ->t('HTTP protocol'),
      '#description' => $this
        ->t('The HTTP protocol to use for sending queries.'),
      '#default_value' => isset($this->configuration['scheme']) ? $this->configuration['scheme'] : 'http',
      '#options' => array(
        'http' => 'http',
        'https' => 'https',
      ),
    );
    $form['host'] = array(
      '#type' => 'textfield',
      '#title' => $this
        ->t('Solr host'),
      '#description' => $this
        ->t('The host name or IP of your Solr server, e.g. <code>localhost</code> or <code>www.example.com</code>.'),
      '#default_value' => isset($this->configuration['host']) ? $this->configuration['host'] : '',
      '#required' => TRUE,
    );
    $form['port'] = array(
      '#type' => 'textfield',
      '#title' => $this
        ->t('Solr port'),
      '#description' => $this
        ->t('The Jetty example server is at port 8983, while Tomcat uses 8080 by default.'),
      '#default_value' => isset($this->configuration['port']) ? $this->configuration['port'] : '',
      '#required' => TRUE,
    );
    $form['path'] = array(
      '#type' => 'textfield',
      '#title' => $this
        ->t('Solr path'),
      '#description' => $this
        ->t('The path that identifies the Solr instance to use on the server.'),
      '#default_value' => isset($this->configuration['path']) ? $this->configuration['path'] : '',
    );
    $form['core'] = array(
      '#type' => 'textfield',
      '#title' => $this
        ->t('Solr core'),
      '#description' => $this
        ->t('The name that identifies the Solr core to use on the server.'),
      '#default_value' => isset($this->configuration['core']) ? $this->configuration['core'] : '',
    );
    $form['timeout'] = array(
      '#type' => 'number',
      '#min' => 1,
      '#max' => 180,
      '#title' => $this
        ->t('Query timeout'),
      '#description' => $this
        ->t('The timeout in seconds for search queries sent to the Solr server.'),
      '#default_value' => isset($this->configuration['timeout']) ? $this->configuration['timeout'] : 5,
      '#required' => TRUE,
    );
    $form['index_timeout'] = array(
      '#type' => 'number',
      '#min' => 1,
      '#max' => 180,
      '#title' => $this
        ->t('Index timeout'),
      '#description' => $this
        ->t('The timeout in seconds for indexing requests to the Solr server.'),
      '#default_value' => isset($this->configuration['index_timeout']) ? $this->configuration['index_timeout'] : 5,
      '#required' => TRUE,
    );
    $form['optimize_timeout'] = array(
      '#type' => 'number',
      '#min' => 1,
      '#max' => 180,
      '#title' => $this
        ->t('Optimize timeout'),
      '#description' => $this
        ->t('The timeout in seconds for background index optimization queries on a Solr server.'),
      '#default_value' => isset($this->configuration['optimize_timeout']) ? $this->configuration['optimize_timeout'] : 10,
      '#required' => TRUE,
    );
    $form['commit_within'] = array(
      '#type' => 'number',
      '#min' => 0,
      '#title' => $this
        ->t('Commit within'),
      '#description' => $this
        ->t('The limit in milliseconds within a (soft) commit on Solr is forced after any updating the index in any way. Setting the value to "0" turns off this dynamic enforcement and lets Solr behave like configured solrconf.xml.'),
      '#default_value' => isset($this->configuration['commit_within']) ? $this->configuration['commit_within'] : 1000,
      '#required' => TRUE,
    );
    $form['workarounds'] = array(
      '#type' => 'fieldset',
      '#title' => $this
        ->t('Connector Workarounds'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['workarounds']['solr_version'] = array(
      '#type' => 'select',
      '#title' => $this
        ->t('Solr version override'),
      '#description' => $this
        ->t('Specify the Solr version manually in case it cannot be retrived automatically. The version can be found in the Solr admin interface under "Solr Specification Version" or "solr-spec"'),
      '#options' => array(
        '' => $this
          ->t('Determine automatically'),
        '4' => '4.x',
        '5' => '5.x',
        '6' => '6.x',
      ),
      '#default_value' => isset($this->configuration['solr_version']) ? $this->configuration['solr_version'] : '',
    );
    $form['workarounds']['http_method'] = array(
      '#type' => 'select',
      '#title' => $this
        ->t('HTTP method'),
      '#description' => $this
        ->t('The HTTP method to use for sending queries. GET will often fail with larger queries, while POST should not be cached. AUTO will use GET when possible, and POST for queries that are too large.'),
      '#default_value' => isset($this->configuration['http_method']) ? $this->configuration['http_method'] : 'AUTO',
      '#options' => array(
        'AUTO' => $this
          ->t('AUTO'),
        'POST' => 'POST',
        'GET' => 'GET',
      ),
    );
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state
      ->getValues();
    if (isset($values['port']) && (!is_numeric($values['port']) || $values['port'] < 0 || $values['port'] > 65535)) {
      $form_state
        ->setError($form['port'], $this
        ->t('The port has to be an integer between 0 and 65535.'));
    }
    if (!empty($values['path']) && strpos($values['path'], '/') !== 0) {
      $form_state
        ->setError($form['path'], $this
        ->t('If provided the path has to start with "/".'));
    }
    if (!empty($values['core']) && strpos($values['core'], '/') === 0) {
      $form_state
        ->setError($form['core'], $this
        ->t('The core must not start with "/".'));
    }
    if (!$form_state
      ->hasAnyErrors()) {

      // Try to orchestrate a server link from form values.
      $values_copied = $values;

      // Solr 3 doesn't have the core name in the path. But solarium 6 needs it.
      // The period is a workaround that gives us URLs like "solr/./select".
      if (!$values_copied['core']) {
        $values_copied['core'] = '.';
      }
      $solr = $this
        ->createClient($values_copied);
      $solr
        ->createEndpoint($values + [
        'key' => 'core',
      ], TRUE);
      try {
        $this
          ->getServerLink();
      } catch (\InvalidArgumentException $e) {
        foreach ([
          'scheme',
          'host',
          'port',
          'path',
          'core',
        ] as $part) {
          $form_state
            ->setError($form[$part], $this
            ->t('The server link generated from the form values is illegal.'));
        }
      }
    }
  }

  /**
   * {@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['workarounds'] as $key => $value) {
      $form_state
        ->setValue($key, $value);
    }

    // Clean-up the form to avoid redundant entries in the stored configuration.
    $form_state
      ->unsetValue('workarounds');
    $this
      ->traitSubmitConfigurationForm($form, $form_state);
  }

  /**
   * Prepares the connection to the Solr server.
   */
  protected function connect() {
    if (!$this->solr) {
      $configuration = $this->configuration;

      // Solr 3 doesn't have the core name in the path. But solarium 6 needs it.
      // The period is a workaround that gives us URLs like "solr/./select".
      if (!$configuration['core']) {
        $configuration['core'] = '.';
      }
      $this->solr = $this
        ->createClient($configuration);
      $this->solr
        ->createEndpoint($this->configuration + [
        'key' => 'core',
      ], TRUE);
      $this
        ->attachServerEndpoint();
    }
  }

  /**
   * Attaches an endpoint to the Solr connection to communicate with the server.
   *
   * This endpoint is different from the core endpoint which is the default one.
   * The default endpoint for the core is used to communicate with the index.
   * But for some administrative tasks the server itself needs to be contacted.
   * This function is meant to be overwritten as soon as we deal with Solr
   * service provider specific implementations of SolrHelper.
   */
  protected function attachServerEndpoint() {
    $this
      ->connect();
    $configuration = $this->configuration;
    $configuration['core'] = '.';
    $configuration['key'] = 'server';
    $this->solr
      ->createEndpoint($configuration);
  }

  /**
   * Create a Client.
   */
  protected function createClient(array &$configuration) {
    $configuration[self::QUERY_TIMEOUT] = $configuration['timeout'] ?? 5;
    $adapter = NULL;
    if (extension_loaded('curl')) {
      $adapter = new Curl($configuration);
    }
    else {
      $adapter = new Http();
      $adapter
        ->setTimeout($configuration[self::QUERY_TIMEOUT]);
    }
    unset($configuration['timeout']);
    return new Client($adapter, $this->eventDispatcher);
  }

  /**
   * Returns a the Solr server URI.
   */
  protected function getServerUri() {
    $this
      ->connect();
    $url_path = $this->solr
      ->getEndpoint()
      ->getServerUri();
    if ($this->configuration['host'] === 'localhost' && !empty($_SERVER['SERVER_NAME'])) {
      $url_path = str_replace('localhost', $_SERVER['SERVER_NAME'], $url_path);
    }
    return $url_path;
  }

  /**
   * {@inheritdoc}
   */
  public function getServerLink() {
    $url_path = $this
      ->getServerUri();
    $url = Url::fromUri($url_path);
    return Link::fromTextAndUrl($url_path, $url);
  }

  /**
   * {@inheritdoc}
   */
  public function getCoreLink() {
    $url_path = $this
      ->getServerUri() . '#/' . $this->configuration['core'];
    $url = Url::fromUri($url_path);
    return Link::fromTextAndUrl($url_path, $url);
  }

  /**
   * {@inheritdoc}
   */
  public function getSolrVersion($force_auto_detect = FALSE) {

    // Allow for overrides by the user.
    if (!$force_auto_detect && !empty($this->configuration['solr_version'])) {

      // In most cases the already stored solr_version is just the major version
      // number as integer. In this case we will expand it to the minimum
      // corresponding full version string.
      $min_version = [
        '0',
        '0',
        '0',
      ];
      $version = implode('.', explode('.', $this->configuration['solr_version']) + $min_version);
      return '4.0.0' === $version ? '4.5.0' : $version;
    }
    $info = [];
    try {
      $info = $this
        ->getCoreInfo();
    } catch (SearchApiSolrException $e) {
      try {
        $info = $this
          ->getServerInfo();
      } catch (SearchApiSolrException $e) {
      }
    }

    // Get our solr version number.
    if (isset($info['lucene']['solr-spec-version'])) {
      return $info['lucene']['solr-spec-version'];
    }
    return '0.0.0';
  }

  /**
   * {@inheritdoc}
   */
  public function getSolrMajorVersion($version = '') {
    [
      $major,
      ,
    ] = explode('.', $version ?: $this
      ->getSolrVersion());
    return $major;
  }

  /**
   * {@inheritdoc}
   */
  public function getSolrBranch($version = '') {
    return $this
      ->getSolrMajorVersion($version) . '.x';
  }

  /**
   * {@inheritdoc}
   */
  public function getLuceneMatchVersion($version = '') {
    [
      $major,
      $minor,
    ] = explode('.', $version ?: $this
      ->getSolrVersion());
    return $major . '.' . $minor;
  }

  /**
   * {@inheritdoc}
   */
  public function getServerInfo($reset = FALSE) {
    $this
      ->useTimeout();
    return $this
      ->getDataFromHandler('admin/info/system', $reset);
  }

  /**
   * {@inheritdoc}
   */
  public function getCoreInfo($reset = FALSE) {
    $this
      ->useTimeout();
    return $this
      ->getDataFromHandler($this->configuration['core'] . '/admin/system', $reset);
  }

  /**
   * {@inheritdoc}
   */
  public function getLuke() {
    $this
      ->useTimeout();
    return $this
      ->getDataFromHandler($this->configuration['core'] . '/admin/luke', TRUE);
  }

  /**
   * {@inheritdoc}
   */
  public function getSchemaVersionString($reset = FALSE) {
    return $this
      ->getCoreInfo($reset)['core']['schema'];
  }

  /**
   * {@inheritdoc}
   */
  public function getSchemaVersion($reset = FALSE) {
    $parts = explode('-', $this
      ->getSchemaVersionString($reset));
    return $parts[1];
  }

  /**
   * Gets data from a Solr endpoint using a given handler.
   *
   * @param string $handler
   *   The handler used for the API query.
   * @param bool $reset
   *   If TRUE the server will be asked regardless if a previous call is cached.
   *
   * @return array
   *   Response data with system information.
   *
   * @throws \Drupal\search_api_solr\SearchApiSolrException
   */
  protected function getDataFromHandler($handler, $reset = FALSE) {
    static $previous_calls = [];
    $this
      ->connect();

    // We keep the results in a state instead of a cache because we want to
    // access parts of this data even if Solr is temporarily not reachable and
    // caches have been cleared.
    $state_key = 'search_api_solr.endpoint.data';
    $state = \Drupal::state();
    $endpoint_data = $state
      ->get($state_key);
    $server_uri = $this
      ->getServerUri();
    if (!isset($previous_calls[$server_uri][$handler]) || !isset($endpoint_data[$server_uri][$handler]) || $reset) {

      // Don't retry multiple times in case of an exception.
      $previous_calls[$server_uri][$handler] = TRUE;
      if (!is_array($endpoint_data) || !isset($endpoint_data[$server_uri][$handler]) || $reset) {
        $query = $this->solr
          ->createApi([
          'handler' => $handler,
          'version' => Request::API_V1,
        ]);
        $endpoint_data[$server_uri][$handler] = $this
          ->execute($query)
          ->getData();
        $state
          ->set($state_key, $endpoint_data);
      }
    }
    return $endpoint_data[$server_uri][$handler];
  }

  /**
   * {@inheritdoc}
   */
  public function pingCore() {
    return $this
      ->doPing();
  }

  /**
   * {@inheritdoc}
   */
  public function pingServer() {
    return $this
      ->doPing([
      'handler' => 'admin/info/system',
    ], 'server');
  }

  /**
   * Pings the Solr server to tell whether it can be accessed.
   *
   * @param string $endpoint_name
   *   The endpoint to be pinged on the Solr server.
   *
   * @return mixed
   *   The latency in milliseconds if the core can be accessed,
   *   otherwise FALSE.
   */
  protected function doPing($options = [], $endpoint_name = 'core') {
    $this
      ->connect();

    // Default is ['handler' => 'admin/ping'].
    $query = $this->solr
      ->createPing($options);
    try {
      $start = microtime(TRUE);
      $result = $this->solr
        ->execute($query, $endpoint_name);
      if ($result
        ->getResponse()
        ->getStatusCode() == 200) {

        // Add 1 µs to the ping time so we never return 0.
        return microtime(TRUE) - $start + 1.0E-6;
      }
    } catch (HttpException $e) {

      // Don't handle the exception. Just return FALSE below.
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getStatsSummary() {
    $this
      ->connect();
    $this
      ->useTimeout();
    $summary = array(
      '@pending_docs' => '',
      '@autocommit_time_seconds' => '',
      '@autocommit_time' => '',
      '@deletes_by_id' => '',
      '@deletes_by_query' => '',
      '@deletes_total' => '',
      '@schema_version' => '',
      '@core_name' => '',
      '@index_size' => '',
    );
    $query = $this->solr
      ->createPing();
    $query
      ->setResponseWriter(Query::WT_PHPS);
    $query
      ->setHandler('admin/mbeans?stats=true');
    $stats = $this
      ->execute($query)
      ->getData();
    if (!empty($stats)) {
      $update_handler_stats = $stats['solr-mbeans']['UPDATEHANDLER']['updateHandler']['stats'];
      $summary['@pending_docs'] = (int) $update_handler_stats['docsPending'];
      $max_time = (int) $update_handler_stats['autocommit maxTime'];

      // Convert to seconds.
      $summary['@autocommit_time_seconds'] = $max_time / 1000;
      $summary['@autocommit_time'] = \Drupal::service('date.formatter')
        ->formatInterval($max_time / 1000);
      $summary['@deletes_by_id'] = (int) $update_handler_stats['deletesById'];
      $summary['@deletes_by_query'] = (int) $update_handler_stats['deletesByQuery'];
      $summary['@deletes_total'] = $summary['@deletes_by_id'] + $summary['@deletes_by_query'];
      $summary['@schema_version'] = $this
        ->getSchemaVersionString(TRUE);
      $summary['@core_name'] = $stats['solr-mbeans']['CORE']['core']['stats']['coreName'];
      if (version_compare($this
        ->getSolrVersion(TRUE), '6.4', '>=')) {

        // @see https://issues.apache.org/jira/browse/SOLR-3990
        $summary['@index_size'] = $stats['solr-mbeans']['CORE']['core']['stats']['size'];
      }
      else {
        $summary['@index_size'] = $stats['solr-mbeans']['QUERYHANDLER']['/replication']['stats']['indexSize'];
      }
    }
    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function coreRestGet($path) {
    $this
      ->useTimeout();
    return $this
      ->restRequest('core', $path);
  }

  /**
   * {@inheritdoc}
   */
  public function coreRestPost($path, $command_json = '') {
    $this
      ->useTimeout(self::INDEX_TIMEOUT);
    return $this
      ->restRequest('core', $path, Request::METHOD_POST, $command_json);
  }

  /**
   * {@inheritdoc}
   */
  public function serverRestGet($path) {
    $this
      ->useTimeout();
    return $this
      ->restRequest('server', $path);
  }

  /**
   * {@inheritdoc}
   */
  public function serverRestPost($path, $command_json = '') {
    $this
      ->useTimeout(self::INDEX_TIMEOUT);
    return $this
      ->restRequest('server', $path, Request::METHOD_POST, $command_json);
  }

  /**
   * Sends a REST request to the Solr server endpoint and returns the result.
   *
   * @param string $endpoint_key
   *   The endpoint that refelcts the base URI.
   * @param string $path
   *   The path to append to the base URI.
   * @param string $method
   *   The HTTP request method.
   * @param string $command_json
   *   The command to send encoded as JSON.
   *
   * @return string
   *   The decoded response.
   */
  protected function restRequest($endpoint_key, $path, $method = Request::METHOD_GET, $command_json = '') {
    $this
      ->connect();
    $request = new Request();
    $request
      ->setMethod($method);
    $request
      ->addHeader('Accept: application/json');
    if (Request::METHOD_POST == $method) {
      $request
        ->addHeader('Content-type: application/json');
      $request
        ->setRawData($command_json);
    }
    $request
      ->setHandler($path);
    $endpoint = $this->solr
      ->getEndpoint($endpoint_key);
    $response = $this
      ->executeRequest($request, $endpoint);
    $output = Json::decode($response
      ->getBody());

    // \Drupal::logger('search_api_solr')->info(print_r($output, true));.
    if (!empty($output['errors'])) {
      throw new SearchApiSolrException('Error trying to send a REST request.' . "\nError message(s):" . print_r($output['errors'], TRUE));
    }
    return $output;
  }

  /**
   * {@inheritdoc}
   */
  public function getUpdateQuery() {
    $this
      ->connect();
    return $this->solr
      ->createUpdate();
  }

  /**
   * {@inheritdoc}
   */
  public function getSelectQuery() {
    $this
      ->connect();
    return $this->solr
      ->createSelect();
  }

  /**
   * {@inheritdoc}
   */
  public function getMoreLikeThisQuery() {
    $this
      ->connect();
    return $this->solr
      ->createMoreLikeThis();
  }

  /**
   * {@inheritdoc}
   */
  public function getTermsQuery() {
    $this
      ->connect();
    return $this->solr
      ->createTerms();
  }

  /**
   * {@inheritdoc}
   */
  public function getAutocompleteQuery() {
    $this
      ->connect();
    $this->solr
      ->registerQueryType('autocomplete', AutocompleteQuery::class);
    return $this->solr
      ->createQuery('autocomplete');
  }

  /**
   * {@inheritdoc}
   */
  public function getQueryHelper(QueryInterface $query = NULL) {
    if ($query) {
      return $query
        ->getHelper();
    }
    return new Helper();
  }

  /**
   * {@inheritdoc}
   */
  public function getExtractQuery() {
    $this
      ->connect();
    return $this->solr
      ->createExtract();
  }

  /**
   * @return \Solarium\Plugin\CustomizeRequest\CustomizeRequest
   */
  protected function customizeRequest() {
    $this
      ->connect();
    return $this->solr
      ->getPlugin('customizerequest');
  }

  /**
   * {@inheritdoc}
   */
  public function search(Query $query, Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint('core');
    }
    $this
      ->useTimeout(self::QUERY_TIMEOUT, $endpoint);

    // Use the 'postbigrequest' plugin if no specific http method is
    // configured. The plugin needs to be loaded before the request is
    // created.
    if ($this->configuration['http_method'] == 'AUTO') {
      $this->solr
        ->getPlugin('postbigrequest');
    }

    // Use the manual method of creating a Solarium request so we can control
    // the HTTP method.
    $request = $this->solr
      ->createRequest($query);

    // Set the configured HTTP method.
    if ($this->configuration['http_method'] == 'POST') {
      $request
        ->setMethod(Request::METHOD_POST);
    }
    elseif ($this->configuration['http_method'] == 'GET') {
      $request
        ->setMethod(Request::METHOD_GET);
    }
    return $this
      ->executeRequest($request, $endpoint);
  }

  /**
   * {@inheritdoc}
   */
  public function createSearchResult(Query $query, Response $response) {
    return $this->solr
      ->createResult($query, $response);
  }

  /**
   * {@inheritdoc}
   */
  public function update(UpdateQuery $query, Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint('core');
    }

    // The default timeout is set for search queries. The configured timeout
    // might differ and needs to be set now because solarium doesn't
    // distinguish between these types.
    $this
      ->useTimeout(self::INDEX_TIMEOUT, $endpoint);
    if ($this->configuration['commit_within']) {

      // Do a commitWithin since that is automatically a softCommit since Solr 4
      // and a delayed hard commit with Solr 3.4+.
      // By default we wait 1 second after the request arrived for solr to parse
      // the commit. This allows us to return to Drupal and let Solr handle what
      // it needs to handle.
      // @see http://wiki.apache.org/solr/NearRealtimeSearch

      /** @var \Solarium\Plugin\CustomizeRequest\CustomizeRequest $request */
      $request = $this
        ->customizeRequest();
      $request
        ->createCustomization('id')
        ->setType('param')
        ->setName('commitWithin')
        ->setValue($this->configuration['commit_within']);
    }
    $result = $this
      ->execute($query, $endpoint);
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function autocomplete(AutocompleteQuery $query, ?Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint();
    }
    $this
      ->useTimeout(self::QUERY_TIMEOUT, $endpoint);

    // Use the 'postbigrequest' plugin if no specific http method is
    // configured. The plugin needs to be loaded before the request is
    // created.
    if ($this->configuration['http_method'] === 'AUTO') {
      $this->solr
        ->getPlugin('postbigrequest');
    }
    return $this
      ->execute($query, $endpoint);
  }

  /**
   * {@inheritdoc}
   */
  public function execute(QueryInterface $query, Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint('core');
    }
    try {
      return $this->solr
        ->execute($query, $endpoint);
    } catch (HttpException $e) {
      $this
        ->handleHttpException($e, $endpoint);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function executeRequest(Request $request, Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint('core');
    }
    try {
      return $this->solr
        ->executeRequest($request, $endpoint);
    } catch (HttpException $e) {
      $this
        ->handleHttpException($e, $endpoint);
    }
  }

  /**
   * Converts a HttpException in an easier to read SearchApiSolrException.
   *
   * @param \Solarium\Exception\HttpException $e
   * @param \Solarium\Core\Client\Endpoint $endpoint
   *
   * @throws \Drupal\search_api_solr\SearchApiSolrException
   */
  protected function handleHttpException(HttpException $e, Endpoint $endpoint) {
    $response_code = $e
      ->getCode();
    switch ($response_code) {
      case 404:
        $description = $this
          ->t('not found');
        break;
      case 401:
      case 403:
        $description = $this
          ->t('access denied');
        break;
      default:
        $description = $this
          ->t('unreachable');
    }
    throw new SearchApiSolrException($this
      ->t('Solr endpoint @endpoint @description.', [
      '@endpoint' => $endpoint
        ->getBaseUri(),
      '@description' => $description,
    ]), $response_code, $e);
  }

  /**
   * {@inheritdoc}
   */
  public function optimize(Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint('core');
    }

    // The default timeout is set for search queries. The configured timeout
    // might differ and needs to be set now because solarium doesn't
    // distinguish between these types.
    $this
      ->useTimeout(self::OPTIMIZE_TIMEOUT, $endpoint);
    $update_query = $this->solr
      ->createUpdate();
    $update_query
      ->addOptimize(TRUE, FALSE);
    $this
      ->execute($update_query, $endpoint);
  }

  /**
   * {@inheritdoc}
   */
  public function useTimeout(string $timeout = self::QUERY_TIMEOUT, ?Endpoint $endpoint = NULL) {
    $this
      ->connect();
    if (!$endpoint) {
      $endpoint = $this->solr
        ->getEndpoint();
    }
    $adpater = $this->solr
      ->getAdapter();
    if ($adpater instanceof TimeoutAwareInterface && ($seconds = $endpoint
      ->getOption($timeout))) {
      $adpater
        ->setTimeout($seconds);
    }
    else {
      \Drupal::logger('search_api')
        ->warning('The function SolrConnectorPluginBase::useTimeout() has no affect because you use a HTTP adapter that is not implementing TimeoutAwareInterface. You need to adjust your SolrConnector accordingly.');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function extract(QueryInterface $query) {
    $this
      ->useTimeout(self::INDEX_TIMEOUT);
    return $this
      ->execute($query);
  }

  /**
   * {@inheritdoc}
   */
  public function getContentFromExtractResult(ExtractResult $result, $filepath) {
    $response = $result
      ->getResponse();
    $json_data = $response
      ->getBody();
    $array_data = Json::decode($json_data);
    return $array_data[$filepath];
  }

  /**
   * {@inheritdoc}
   */
  public function getEndpoint($key = 'core') {
    $this
      ->connect();
    return $this->solr
      ->getEndpoint($key);
  }

  /**
   * {@inheritdoc}
   */
  public function getFile($file = NULL) {
    $this
      ->connect();
    $query = $this->solr
      ->createPing();
    $query
      ->setHandler('admin/file');
    $query
      ->addParam('contentType', 'text/xml;charset=utf-8');
    if ($file) {
      $query
        ->addParam('file', $file);
    }
    return $this
      ->execute($query)
      ->getResponse();
  }

  /**
   * {@inheritdoc}
   */
  public function viewSettings() {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function __sleep() {

    // It's safe to unset the solr client completely before serialization
    // because connect() will set it up again correctly after deserialization.
    unset($this->solr);
    return parent::__sleep();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigurablePluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 6
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::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration 3
ConfigurablePluginBase::themeHandler Deprecated protected function Wraps the theme handler.
ConfigurablePluginBase::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides PluginBase::__construct 2
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.
DependencySerializationTrait::__wakeup public function 2
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
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
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
SolrConnectorInterface::INDEX_TIMEOUT constant
SolrConnectorInterface::OPTIMIZE_TIMEOUT constant
SolrConnectorInterface::QUERY_TIMEOUT constant
SolrConnectorPluginBase::$eventDispatcher protected property The event dispatcher.
SolrConnectorPluginBase::$solr protected property A connection to the Solr server.
SolrConnectorPluginBase::attachServerEndpoint protected function Attaches an endpoint to the Solr connection to communicate with the server.
SolrConnectorPluginBase::autocomplete public function
SolrConnectorPluginBase::buildConfigurationForm public function Form constructor. Overrides PluginFormInterface::buildConfigurationForm 1
SolrConnectorPluginBase::connect protected function Prepares the connection to the Solr server.
SolrConnectorPluginBase::coreRestGet public function Sends a REST GET request to the Solr core and returns the result. Overrides SolrConnectorInterface::coreRestGet
SolrConnectorPluginBase::coreRestPost public function Sends a REST POST request to the Solr core and returns the result. Overrides SolrConnectorInterface::coreRestPost
SolrConnectorPluginBase::create public static function Creates an instance of the plugin. Overrides ConfigurablePluginBase::create
SolrConnectorPluginBase::createClient protected function Create a Client.
SolrConnectorPluginBase::createSearchResult public function Creates a result from a response. Overrides SolrConnectorInterface::createSearchResult
SolrConnectorPluginBase::customizeRequest protected function
SolrConnectorPluginBase::defaultConfiguration public function Gets default configuration for this plugin. Overrides ConfigurablePluginBase::defaultConfiguration 1
SolrConnectorPluginBase::doPing protected function Pings the Solr server to tell whether it can be accessed.
SolrConnectorPluginBase::execute public function Executes any query. Overrides SolrConnectorInterface::execute
SolrConnectorPluginBase::executeRequest public function Executes a request and returns the response. Overrides SolrConnectorInterface::executeRequest
SolrConnectorPluginBase::extract public function Executes an extract query. Overrides SolrConnectorInterface::extract
SolrConnectorPluginBase::getAutocompleteQuery public function
SolrConnectorPluginBase::getContentFromExtractResult public function Gets the content from an extract query result. Overrides SolrConnectorInterface::getContentFromExtractResult
SolrConnectorPluginBase::getCoreInfo public function Gets information about the Solr Core. Overrides SolrConnectorInterface::getCoreInfo
SolrConnectorPluginBase::getCoreLink public function Returns a link to the Solr core, if the necessary options are set. Overrides SolrConnectorInterface::getCoreLink
SolrConnectorPluginBase::getDataFromHandler protected function Gets data from a Solr endpoint using a given handler.
SolrConnectorPluginBase::getEndpoint public function Returns an endpoint. Overrides SolrConnectorInterface::getEndpoint
SolrConnectorPluginBase::getExtractQuery public function Creates a new Solarium extract query. Overrides SolrConnectorInterface::getExtractQuery
SolrConnectorPluginBase::getFile public function Retrieves a config file or file list from the Solr server. Overrides SolrConnectorInterface::getFile
SolrConnectorPluginBase::getLuceneMatchVersion public function Gets the LuceneMatchVersion string. Overrides SolrConnectorInterface::getLuceneMatchVersion
SolrConnectorPluginBase::getLuke public function Gets meta-data about the index. Overrides SolrConnectorInterface::getLuke
SolrConnectorPluginBase::getMoreLikeThisQuery public function Creates a new Solarium more like this query. Overrides SolrConnectorInterface::getMoreLikeThisQuery
SolrConnectorPluginBase::getQueryHelper public function Returns a Solarium query helper object. Overrides SolrConnectorInterface::getQueryHelper
SolrConnectorPluginBase::getSchemaVersion public function Gets the schema version number. Overrides SolrConnectorInterface::getSchemaVersion
SolrConnectorPluginBase::getSchemaVersionString public function Gets the full schema version string the core is using. Overrides SolrConnectorInterface::getSchemaVersionString
SolrConnectorPluginBase::getSelectQuery public function Creates a new Solarium update query. Overrides SolrConnectorInterface::getSelectQuery
SolrConnectorPluginBase::getServerInfo public function Gets information about the Solr server. Overrides SolrConnectorInterface::getServerInfo
SolrConnectorPluginBase::getServerLink public function Returns a link to the Solr server. Overrides SolrConnectorInterface::getServerLink
SolrConnectorPluginBase::getServerUri protected function Returns a the Solr server URI.
SolrConnectorPluginBase::getSolrBranch public function Gets the current Solr branch name. Overrides SolrConnectorInterface::getSolrBranch
SolrConnectorPluginBase::getSolrMajorVersion public function Gets the current Solr major version. Overrides SolrConnectorInterface::getSolrMajorVersion
SolrConnectorPluginBase::getSolrVersion public function Gets the current Solr version. Overrides SolrConnectorInterface::getSolrVersion
SolrConnectorPluginBase::getStatsSummary public function Gets summary information about the Solr Core. Overrides SolrConnectorInterface::getStatsSummary
SolrConnectorPluginBase::getTermsQuery public function Creates a new Solarium terms query. Overrides SolrConnectorInterface::getTermsQuery
SolrConnectorPluginBase::getUpdateQuery public function Creates a new Solarium update query. Overrides SolrConnectorInterface::getUpdateQuery
SolrConnectorPluginBase::handleHttpException protected function Converts a HttpException in an easier to read SearchApiSolrException.
SolrConnectorPluginBase::optimize public function Optimizes the Solr index. Overrides SolrConnectorInterface::optimize
SolrConnectorPluginBase::pingCore public function Pings the Solr core to tell whether it can be accessed. Overrides SolrConnectorInterface::pingCore
SolrConnectorPluginBase::pingServer public function Pings the Solr server to tell whether it can be accessed. Overrides SolrConnectorInterface::pingServer
SolrConnectorPluginBase::restRequest protected function Sends a REST request to the Solr server endpoint and returns the result.
SolrConnectorPluginBase::search public function Executes a search query and returns the raw response. Overrides SolrConnectorInterface::search 1
SolrConnectorPluginBase::serverRestGet public function Sends a REST GET request to the Solr server and returns the result. Overrides SolrConnectorInterface::serverRestGet
SolrConnectorPluginBase::serverRestPost public function Sends a REST POST request to the Solr server and returns the result. Overrides SolrConnectorInterface::serverRestPost
SolrConnectorPluginBase::submitConfigurationForm public function Form submission handler. Overrides PluginFormInterface::submitConfigurationForm 1
SolrConnectorPluginBase::update public function Executes an update query and applies some tweaks. Overrides SolrConnectorInterface::update
SolrConnectorPluginBase::useTimeout public function
SolrConnectorPluginBase::validateConfigurationForm public function Form validation handler. Overrides PluginFormTrait::validateConfigurationForm
SolrConnectorPluginBase::viewSettings public function Returns additional, connector-specific information about this server. Overrides SolrConnectorInterface::viewSettings 1
SolrConnectorPluginBase::__sleep public function Overrides DependencySerializationTrait::__sleep
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.