View source
<?php
namespace Drupal\search_api_solr\SolrConnector;
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Url;
use Drupal\search_api\LoggerTrait;
use Drupal\search_api\Plugin\ConfigurablePluginBase;
use Drupal\search_api\Plugin\PluginFormTrait;
use Drupal\search_api_solr\SearchApiSolrException;
use Drupal\search_api_solr\Solarium\Autocomplete\Query as AutocompleteQuery;
use Drupal\search_api_solr\SolrConnectorInterface;
use Solarium\Client;
use Solarium\Core\Client\Adapter\Curl;
use Solarium\Core\Client\Adapter\Http;
use Solarium\Core\Client\Adapter\TimeoutAwareInterface;
use Solarium\Core\Client\Endpoint;
use Solarium\Core\Client\Request;
use Solarium\Core\Client\Response;
use Solarium\Core\Query\QueryInterface;
use Solarium\Exception\HttpException;
use Solarium\QueryType\Extract\Result as ExtractResult;
use Solarium\QueryType\Select\Query\Query;
use Solarium\QueryType\Update\Query\Query as UpdateQuery;
use ZipStream\ZipStream;
abstract class SolrConnectorPluginBase extends ConfigurablePluginBase implements SolrConnectorInterface, PluginFormInterface {
use PluginFormTrait {
submitConfigurationForm as traitSubmitConfigurationForm;
}
use LoggerTrait;
protected $eventDispatcher;
protected $solr;
public function setEventDispatcher(ContainerAwareEventDispatcher $eventDispatcher) : SolrConnectorInterface {
$this->eventDispatcher = $eventDispatcher;
return $this;
}
public function defaultConfiguration() {
return [
'scheme' => 'http',
'host' => 'localhost',
'port' => 8983,
'path' => '/',
'core' => '',
'timeout' => 5,
self::INDEX_TIMEOUT => 5,
self::OPTIMIZE_TIMEOUT => 10,
self::FINALIZE_TIMEOUT => 30,
'solr_version' => '',
'http_method' => 'AUTO',
'commit_within' => 1000,
'jmx' => FALSE,
'jts' => FALSE,
'solr_install_dir' => '',
'skip_schema_check' => FALSE,
];
}
public function setConfiguration(array $configuration) {
$configuration['port'] = (int) $configuration['port'];
$configuration['timeout'] = (int) $configuration['timeout'];
$configuration[self::INDEX_TIMEOUT] = (int) $configuration[self::INDEX_TIMEOUT];
$configuration[self::OPTIMIZE_TIMEOUT] = (int) $configuration[self::OPTIMIZE_TIMEOUT];
$configuration[self::FINALIZE_TIMEOUT] = (int) $configuration[self::FINALIZE_TIMEOUT];
$configuration['commit_within'] = (int) $configuration['commit_within'];
$configuration['jmx'] = (bool) $configuration['jmx'];
$configuration['jts'] = (bool) $configuration['jts'];
$configuration['skip_schema_check'] = (bool) $configuration['skip_schema_check'];
parent::setConfiguration($configuration);
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['scheme'] = [
'#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' => [
'http' => 'http',
'https' => 'https',
],
];
$form['host'] = [
'#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'] = [
'#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'] = [
'#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'] = [
'#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'] : '',
'#required' => TRUE,
];
$form['timeout'] = [
'#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[self::INDEX_TIMEOUT] = [
'#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[self::INDEX_TIMEOUT]) ? $this->configuration[self::INDEX_TIMEOUT] : 5,
'#required' => TRUE,
];
$form[self::OPTIMIZE_TIMEOUT] = [
'#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[self::OPTIMIZE_TIMEOUT]) ? $this->configuration[self::OPTIMIZE_TIMEOUT] : 10,
'#required' => TRUE,
];
$form[self::FINALIZE_TIMEOUT] = [
'#type' => 'number',
'#min' => 1,
'#max' => 180,
'#title' => $this
->t('Finalize timeout'),
'#description' => $this
->t('The timeout in seconds for index finalization queries on a Solr server.'),
'#default_value' => isset($this->configuration[self::FINALIZE_TIMEOUT]) ? $this->configuration[self::FINALIZE_TIMEOUT] : 30,
'#required' => TRUE,
];
$form['commit_within'] = [
'#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'] = [
'#type' => 'details',
'#title' => $this
->t('Connector Workarounds'),
];
$form['workarounds']['solr_version'] = [
'#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' => [
'' => $this
->t('Determine automatically'),
'6' => '6.x',
'7' => '7.x',
'8' => '8.x',
],
'#default_value' => $this->configuration['solr_version'] ?? '',
];
$form['workarounds']['http_method'] = [
'#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' => $this->configuration['http_method'] ?? 'AUTO',
'#options' => [
'AUTO' => $this
->t('AUTO'),
'POST' => 'POST',
'GET' => 'GET',
],
];
$form['workarounds']['skip_schema_check'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Skip schema verification'),
'#description' => $this
->t('Skip the automatic check for schema-compatibillity. Use this override if you are seeing an error-message about an incompatible schema.xml configuration file, and you are sure the configuration is compatible.'),
'#default_value' => $this->configuration['skip_schema_check'] ?? FALSE,
];
$form['advanced'] = [
'#type' => 'details',
'#title' => $this
->t('Advanced server configuration'),
];
$form['advanced']['jmx'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Enable JMX'),
'#description' => $this
->t('Enable JMX based monitoring.'),
'#default_value' => $this->configuration['jmx'] ?? FALSE,
];
$form['advanced']['jts'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Enable JTS'),
'#description' => $this
->t('Enable JTS (java topographic suite). Be sure to follow instructions in last solr reference guide about how to use spatial search.'),
'#default_value' => $this->configuration['jts'] ?? FALSE,
];
$form['advanced']['solr_install_dir'] = [
'#type' => 'textfield',
'#title' => $this
->t('solr.install.dir'),
'#description' => $this
->t('The path where Solr is installed on the server, relative to the configuration or absolute. Some examples are "../../.." for Solr downloaded from apache.org, "/usr/local/opt/solr" for installations via homebrew on macOS or "/opt/solr" for some linux distributions and for the official Solr docker container. If you use different systems for development, testing and production you can use drupal config overwrites to adjust the value per environment or adjust the generated solrcore.properties per environment or use java virtual machine options (-D) to set the property. Modern Solr installations should set that virtual machine option correctly in their start script by themselves. In this case this field should be left empty!'),
'#default_value' => isset($this->configuration['solr_install_dir']) ? $this->configuration['solr_install_dir'] : '',
];
return $form;
}
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('Core or collection must not start with "/".'));
}
if (!$form_state
->hasAnyErrors()) {
$values_copied = $values;
$solr = $this
->createClient($values_copied);
$solr
->createEndpoint($values_copied + [
'key' => 'search_api_solr',
], 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.'));
}
}
}
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$values = $form_state
->getValues();
foreach ($values['workarounds'] as $key => $value) {
$form_state
->setValue($key, $value);
}
foreach ($values['advanced'] as $key => $value) {
$form_state
->setValue($key, $value);
}
$form_state
->unsetValue('workarounds');
$form_state
->unsetValue('advanced');
$this
->traitSubmitConfigurationForm($form, $form_state);
}
protected function connect() {
if (!$this->solr) {
$configuration = $this->configuration;
$this->solr = $this
->createClient($configuration);
$this->solr
->createEndpoint($configuration + [
'key' => 'search_api_solr',
], TRUE);
}
}
protected function createClient(array &$configuration) {
$configuration[self::QUERY_TIMEOUT] = $configuration['timeout'] ?? 5;
unset($configuration['timeout']);
$adapter = extension_loaded('curl') ? new Curl() : new Http();
$adapter
->setTimeout($configuration[self::QUERY_TIMEOUT]);
return new Client($adapter, $this->eventDispatcher);
}
public function isCloud() {
return FALSE;
}
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;
}
public function getServerLink() {
$url_path = $this
->getServerUri();
$url = Url::fromUri($url_path);
return Link::fromTextAndUrl($url_path, $url);
}
public function getCoreLink() {
$url_path = $this
->getServerUri() . 'solr/#/' . $this->configuration['core'];
$url = Url::fromUri($url_path);
return Link::fromTextAndUrl($url_path, $url);
}
public function getSolrVersion($force_auto_detect = FALSE) {
if (!$force_auto_detect && !empty($this->configuration['solr_version'])) {
$min_version = [
'0',
'0',
'0',
];
$version = implode('.', explode('.', $this->configuration['solr_version']) + $min_version);
switch ($version) {
case '3.0.0':
$version = '3.6.0';
break;
case '4.0.0':
$version = '4.5.0';
break;
case '6.0.0':
$version = '6.4.0';
break;
}
return $version;
}
$info = [];
try {
$info = $this
->getCoreInfo();
} catch (\Exception $e) {
try {
$info = $this
->getServerInfo();
} catch (SearchApiSolrException $e) {
}
}
if (isset($info['lucene']['solr-spec-version'])) {
if (preg_match('/^(\\d+\\.\\d+\\.\\d+)/', $info['lucene']['solr-spec-version'], $matches)) {
return $matches[1];
}
}
return '0.0.0';
}
public function getSolrMajorVersion($version = '') : int {
[
$major,
] = explode('.', $version ?: $this
->getSolrVersion());
return (int) $major;
}
public function getSolrBranch($version = '') {
return $this
->getSolrMajorVersion($version) . '.x';
}
public function getLuceneMatchVersion($version = '') {
[
$major,
$minor,
] = explode('.', $version ?: $this
->getSolrVersion());
return $major . '.' . $minor;
}
public function getServerInfo($reset = FALSE) {
$this
->useTimeout();
return $this
->getDataFromHandler('admin/info/system', $reset);
}
public function getCoreInfo($reset = FALSE) {
$this
->useTimeout();
return $this
->getDataFromHandler($this->configuration['core'] . '/admin/system', $reset);
}
public function getLuke() {
$this
->useTimeout();
return $this
->getDataFromHandler($this->configuration['core'] . '/admin/luke', TRUE);
}
public function getConfigSetName() : ?string {
return NULL;
}
public function getSchemaVersionString($reset = FALSE) {
return $this
->getCoreInfo($reset)['core']['schema'];
}
public function getSchemaVersion($reset = FALSE) {
$parts = explode('-', $this
->getSchemaVersionString($reset));
return $parts[1];
}
public function getSchemaTargetedSolrBranch($reset = FALSE) {
$parts = explode('-', $this
->getSchemaVersionString($reset));
return $parts[3];
}
public function isJumpStartConfigSet(bool $reset = FALSE) : bool {
$parts = explode('-', $this
->getSchemaVersionString($reset));
return (bool) ($parts[4] ?? 0);
}
protected function getDataFromHandler($handler, $reset = FALSE) {
static $previous_calls = [];
$this
->connect();
$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) {
$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];
}
public function pingCore(array $options = []) {
return $this
->pingEndpoint(NULL, $options);
}
public function pingServer() {
return $this
->getServerInfo(TRUE);
}
public function pingEndpoint(?Endpoint $endpoint = NULL, array $options = []) {
$this
->connect();
$this
->useTimeout(self::QUERY_TIMEOUT, $endpoint);
$query = $this->solr
->createPing($options);
try {
$start = microtime(TRUE);
$result = $this->solr
->execute($query, $endpoint);
if ($result
->getResponse()
->getStatusCode() == 200) {
return microtime(TRUE) - $start + 1.0E-6;
}
} catch (HttpException $e) {
}
return FALSE;
}
public function getStatsSummary() {
$this
->connect();
$this
->useTimeout();
$summary = [
'@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)) {
$solr_version = $this
->getSolrVersion(TRUE);
$max_time = -1;
if (version_compare($solr_version, '7.0', '>=')) {
$update_handler_stats = $stats['solr-mbeans']['UPDATE']['updateHandler']['stats'];
$summary['@pending_docs'] = (int) $update_handler_stats['UPDATE.updateHandler.docsPending'];
if (isset($update_handler_stats['UPDATE.updateHandler.softAutoCommitMaxTime'])) {
$max_time = (int) $update_handler_stats['UPDATE.updateHandler.softAutoCommitMaxTime'];
}
$summary['@deletes_by_id'] = (int) $update_handler_stats['UPDATE.updateHandler.deletesById'];
$summary['@deletes_by_query'] = (int) $update_handler_stats['UPDATE.updateHandler.deletesByQuery'];
$summary['@core_name'] = $stats['solr-mbeans']['CORE']['core']['stats']['CORE.coreName'] ?? $this
->t('No information available.');
$summary['@index_size'] = $stats['solr-mbeans']['CORE']['core']['stats']['INDEX.size'] ?? $this
->t('No information available.');
}
else {
$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'];
$summary['@deletes_by_id'] = (int) $update_handler_stats['deletesById'];
$summary['@deletes_by_query'] = (int) $update_handler_stats['deletesByQuery'];
$summary['@core_name'] = $stats['solr-mbeans']['CORE']['core']['stats']['coreName'] ?? $this
->t('No information available.');
if (version_compare($solr_version, '6.4', '>=')) {
$summary['@index_size'] = $stats['solr-mbeans']['CORE']['core']['stats']['size'] ?? $this
->t('No information available.');
}
else {
$summary['@index_size'] = $stats['solr-mbeans']['QUERYHANDLER']['/replication']['stats']['indexSize'] ?? $this
->t('No information available.');
}
}
$summary['@autocommit_time_seconds'] = $max_time / 1000;
$summary['@autocommit_time'] = \Drupal::service('date.formatter')
->formatInterval($max_time / 1000);
$summary['@deletes_total'] = $summary['@deletes_by_id'] + $summary['@deletes_by_query'];
$summary['@schema_version'] = $this
->getSchemaVersionString(TRUE);
}
return $summary;
}
public function coreRestGet($path, ?Endpoint $endpoint = NULL) {
$this
->useTimeout();
return $this
->restRequest($this->configuration['core'] . '/' . ltrim($path, '/'), Request::METHOD_GET, '', $endpoint);
}
public function coreRestPost($path, $command_json = '', ?Endpoint $endpoint = NULL) {
$this
->useTimeout(self::INDEX_TIMEOUT);
return $this
->restRequest($this->configuration['core'] . '/' . ltrim($path, '/'), Request::METHOD_POST, $command_json, $endpoint);
}
public function serverRestGet($path) {
$this
->useTimeout();
return $this
->restRequest($path);
}
public function serverRestPost($path, $command_json = '') {
$this
->useTimeout(self::INDEX_TIMEOUT);
return $this
->restRequest($path, Request::METHOD_POST, $command_json);
}
protected function restRequest($handler, $method = Request::METHOD_GET, $command_json = '', ?Endpoint $endpoint = NULL) {
$this
->connect();
$query = $this->solr
->createApi([
'handler' => $handler,
'accept' => 'application/json',
'contenttype' => 'application/json',
'method' => $method,
'rawdata' => Request::METHOD_POST == $method ? $command_json : NULL,
]);
$response = $this
->execute($query, $endpoint);
$output = $response
->getData();
if (!empty($output['errors'])) {
throw new SearchApiSolrException('Error trying to send a REST request.' . "\nError message(s):" . print_r($output['errors'], TRUE));
}
return $output;
}
public function getUpdateQuery() {
$this
->connect();
return $this->solr
->createUpdate();
}
public function getSelectQuery() {
$this
->connect();
return $this->solr
->createSelect();
}
public function getMoreLikeThisQuery() {
$this
->connect();
return $this->solr
->createMoreLikeThis();
}
public function getTermsQuery() {
$this
->connect();
return $this->solr
->createTerms();
}
public function getSpellcheckQuery() {
$this
->connect();
return $this->solr
->createSpellcheck();
}
public function getSuggesterQuery() {
$this
->connect();
return $this->solr
->createSuggester();
}
public function getAutocompleteQuery() {
$this
->connect();
$this->solr
->registerQueryType('autocomplete', AutocompleteQuery::class);
return $this->solr
->createQuery('autocomplete');
}
public function getQueryHelper(QueryInterface $query = NULL) {
if ($query) {
return $query
->getHelper();
}
return \Drupal::service('solarium.query_helper');
}
public function getExtractQuery() {
$this
->connect();
return $this->solr
->createExtract();
}
protected function customizeRequest() {
$this
->connect();
return $this->solr
->getPlugin('customizerequest');
}
public function search(Query $query, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$this
->useTimeout(self::QUERY_TIMEOUT, $endpoint);
if ($this->configuration['http_method'] === 'AUTO') {
$this->solr
->getPlugin('postbigrequest');
}
$request = $this->solr
->createRequest($query);
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);
}
public function createSearchResult(QueryInterface $query, Response $response) {
return $this->solr
->createResult($query, $response);
}
public function update(UpdateQuery $query, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$this
->useTimeout(self::INDEX_TIMEOUT, $endpoint);
if ($this->configuration['commit_within']) {
$request = $this
->customizeRequest();
if (!$request
->getCustomization('commitWithin')) {
$request
->createCustomization('commitWithin')
->setType('param')
->setName('commitWithin')
->setValue($this->configuration['commit_within']);
}
}
return $this
->execute($query, $endpoint);
}
public function autocomplete(AutocompleteQuery $query, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$this
->useTimeout(self::QUERY_TIMEOUT, $endpoint);
if ($this->configuration['http_method'] === 'AUTO') {
$this->solr
->getPlugin('postbigrequest');
}
return $this
->execute($query, $endpoint);
}
public function execute(QueryInterface $query, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
try {
return $this->solr
->execute($query, $endpoint);
} catch (HttpException $e) {
$this
->handleHttpException($e, $endpoint);
}
}
public function executeRequest(Request $request, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
try {
return $this->solr
->executeRequest($request, $endpoint);
} catch (HttpException $e) {
$this
->handleHttpException($e, $endpoint);
}
}
protected final function handleHttpException(HttpException $e, Endpoint $endpoint) {
$body = $e
->getBody();
$response_code = (int) $e
->getCode();
switch ((string) $response_code) {
case '400':
$description = 'bad request';
$response_decoded = Json::decode($body);
if ($response_decoded && isset($response_decoded['error'])) {
$body = $response_decoded['error']['msg'] ?? $body;
}
break;
case '404':
$description = 'not found';
break;
case '401':
case '403':
$description = 'access denied';
break;
case '500':
case '0':
$description = 'internal Solr server error';
break;
default:
$description = 'unreachable or returned unexpected response code';
}
throw new SearchApiSolrException(sprintf('Solr endpoint %s %s (code: %d, body: %s, message: %s).', $this
->getEndpointUri($endpoint), $description, $response_code, $body, $e
->getMessage()), $response_code, $e);
}
protected function getEndpointUri(Endpoint $endpoint) : string {
return $endpoint
->getServerUri();
}
public function optimize(?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$this
->useTimeout(self::OPTIMIZE_TIMEOUT, $endpoint);
$update_query = $this->solr
->createUpdate();
$update_query
->addOptimize(TRUE, FALSE);
$this
->execute($update_query, $endpoint);
}
public function adjustTimeout(int $seconds, string $timeout = self::QUERY_TIMEOUT, ?Endpoint &$endpoint = NULL) : int {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$previous_timeout = $endpoint
->getOption($timeout);
$options = $endpoint
->getOptions();
$options[$timeout] = $seconds;
$endpoint = new Endpoint($options);
return $previous_timeout;
}
protected function useTimeout(string $timeout = self::QUERY_TIMEOUT, ?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
$seconds = $endpoint
->getOption($timeout);
if ($seconds) {
$adapter = $this->solr
->getAdapter();
if ($adapter instanceof TimeoutAwareInterface) {
$adapter
->setTimeout($seconds);
}
else {
$this
->getLogger()
->warning('The function SolrConnectorPluginBase::useTimeout() has no effect because you use a HTTP adapter that is not implementing TimeoutAwareInterface. You need to adjust your SolrConnector accordingly.');
}
}
}
public function getTimeout(?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
return $endpoint
->getOption(self::QUERY_TIMEOUT);
}
public function getIndexTimeout(?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
return $endpoint
->getOption(self::INDEX_TIMEOUT);
}
public function getOptimizeTimeout(?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
return $endpoint
->getOption(self::OPTIMIZE_TIMEOUT);
}
public function getFinalizeTimeout(?Endpoint $endpoint = NULL) {
$this
->connect();
if (!$endpoint) {
$endpoint = $this->solr
->getEndpoint();
}
return $endpoint
->getOption(self::FINALIZE_TIMEOUT);
}
public function extract(QueryInterface $query, ?Endpoint $endpoint = NULL) {
$this
->useTimeout(self::INDEX_TIMEOUT, $endpoint);
return $this
->execute($query, $endpoint);
}
public function getContentFromExtractResult(ExtractResult $result, $filepath) {
$array_data = $result
->getData();
if (isset($array_data[basename($filepath)])) {
return $array_data[basename($filepath)];
}
throw new SearchApiSolrException('Unable to find extracted files within the Solr response body.');
}
public function getEndpoint($key = 'search_api_solr') {
$this
->connect();
return $this->solr
->getEndpoint($key);
}
public function createEndpoint(string $key, array $additional_configuration = []) {
$this
->connect();
$configuration = [
'key' => $key,
self::QUERY_TIMEOUT => $this->configuration['timeout'],
] + $additional_configuration + $this->configuration;
unset($configuration['timeout']);
return $this->solr
->createEndpoint($configuration, TRUE);
}
public function getFile($file = NULL) {
$this
->connect();
$query = $this->solr
->createApi([
'handler' => $this->configuration['core'] . '/admin/file',
]);
if ($file) {
$query
->addParam('file', $file);
}
return $this
->execute($query)
->getResponse();
}
public function viewSettings() {
return [];
}
public function __sleep() {
unset($this->solr);
return parent::__sleep();
}
public function alterConfigFiles(array &$files, string $lucene_match_version, string $server_id = '') {
if (!empty($this->configuration['jmx'])) {
$files['solrconfig_extra.xml'] .= "<jmx />\n";
}
if (!empty($this->configuration['jts'])) {
$jts_arguments = 'spatialContextFactory="org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory" autoIndex="true" validationRule="repairBuffer0"';
$files['schema.xml'] = preg_replace("#\\sclass\\s*=\\s*\"solr\\.SpatialRecursivePrefixTreeFieldType\"#ms", "\\0\n " . $jts_arguments, $files['schema.xml']);
}
}
public function alterConfigZip(ZipStream $zip, string $lucene_match_version, string $server_id = '') {
}
}