View source
<?php
namespace Drupal\search_api_solr\Plugin\search_api\backend;
use Composer\Semver\Comparator;
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Config;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\PluginDependencyTrait;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\ListDataDefinitionInterface;
use Drupal\Core\Url;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\search_api\Item\Field;
use Drupal\search_api\Item\FieldInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Plugin\PluginFormTrait;
use Drupal\search_api\Plugin\search_api\data_type\value\TextValue;
use Drupal\search_api\Processor\ProcessorInterface;
use Drupal\search_api\Query\ConditionGroup;
use Drupal\search_api\Query\ConditionInterface;
use Drupal\search_api\Query\ResultSetInterface;
use Drupal\search_api\SearchApiException;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Query\ConditionGroupInterface;
use Drupal\search_api\Query\QueryInterface;
use Drupal\search_api\Backend\BackendPluginBase;
use Drupal\search_api\Utility\DataTypeHelperInterface;
use Drupal\search_api\Utility\FieldsHelperInterface;
use Drupal\search_api\Utility\Utility as SearchApiUtility;
use Drupal\search_api_autocomplete\SearchInterface;
use Drupal\search_api_autocomplete\Suggestion\SuggestionFactory;
use Drupal\search_api_solr\Entity\SolrFieldType;
use Drupal\search_api_solr\Event\PostCreateIndexDocumentEvent;
use Drupal\search_api_solr\Event\PostCreateIndexDocumentsEvent;
use Drupal\search_api_solr\Event\PostExtractFacetsEvent;
use Drupal\search_api_solr\Event\PostSetFacetsEvent;
use Drupal\search_api_solr\Event\PreCreateIndexDocumentEvent;
use Drupal\search_api_solr\Event\PreExtractFacetsEvent;
use Drupal\search_api_solr\Event\PreSetFacetsEvent;
use Drupal\search_api_solr\SearchApiSolrException;
use Drupal\search_api_solr\Solarium\Autocomplete\Query as AutocompleteQuery;
use Drupal\search_api_solr\Solarium\EventDispatcher\Psr14Bridge;
use Drupal\search_api_solr\Solarium\Result\StreamDocument;
use Drupal\search_api_solr\SolrAutocompleteInterface;
use Drupal\search_api_solr\SolrBackendInterface;
use Drupal\search_api_solr\SolrCloudConnectorInterface;
use Drupal\search_api_solr\SolrConnector\SolrConnectorPluginManager;
use Drupal\search_api_solr\SolrConnectorInterface;
use Drupal\search_api_solr\SolrProcessorInterface;
use Drupal\search_api_solr\Utility\SolrCommitTrait;
use Drupal\search_api_solr\Utility\Utility;
use Solarium\Component\ComponentAwareQueryInterface;
use Solarium\Core\Client\Endpoint;
use Solarium\Core\Client\Response;
use Solarium\Core\Query\Helper;
use Solarium\Core\Query\QueryInterface as SolariumQueryInterface;
use Solarium\Core\Query\Result\ResultInterface;
use Solarium\Exception\ExceptionInterface;
use Solarium\Exception\OutOfBoundsException;
use Solarium\Exception\StreamException;
use Solarium\Exception\UnexpectedValueException;
use Solarium\QueryType\Select\Query\FilterQuery;
use Solarium\QueryType\Stream\ExpressionBuilder;
use Solarium\QueryType\Update\Query\Query as UpdateQuery;
use Solarium\QueryType\Select\Query\Query;
use Solarium\QueryType\Select\Result\Result;
use Solarium\QueryType\Update\Query\Document;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Laminas\Stdlib\ArrayUtils;
class SearchApiSolrBackend extends BackendPluginBase implements SolrBackendInterface, SolrAutocompleteInterface, PluginFormInterface {
use PluginFormTrait {
submitConfigurationForm as traitSubmitConfigurationForm;
}
use PluginDependencyTrait;
use DependencySerializationTrait;
use SolrCommitTrait;
protected $moduleHandler;
protected $searchApiSolrSettings;
protected $languageManager;
protected $solrConnectorPluginManager;
protected $solrConnector;
protected $fieldsHelper;
protected $dataTypeHelper;
protected $queryHelper;
protected $entityTypeManager;
protected $eventDispatcher;
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, Config $search_api_solr_settings, LanguageManagerInterface $language_manager, SolrConnectorPluginManager $solr_connector_plugin_manager, FieldsHelperInterface $fields_helper, DataTypeHelperInterface $dataTypeHelper, Helper $query_helper, EntityTypeManagerInterface $entityTypeManager, ContainerAwareEventDispatcher $eventDispatcher) {
$this->moduleHandler = $module_handler;
$this->searchApiSolrSettings = $search_api_solr_settings;
$this->languageManager = $language_manager;
$this->solrConnectorPluginManager = $solr_connector_plugin_manager;
$this->fieldsHelper = $fields_helper;
$this->dataTypeHelper = $dataTypeHelper;
$this->queryHelper = $query_helper;
$this->entityTypeManager = $entityTypeManager;
if (Comparator::greaterThanOrEqualTo(\Drupal::VERSION, '9.1.0')) {
$this->eventDispatcher = $eventDispatcher;
}
else {
$this->eventDispatcher = new Psr14Bridge($eventDispatcher);
}
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('module_handler'), $container
->get('config.factory')
->get('search_api_solr.settings'), $container
->get('language_manager'), $container
->get('plugin.manager.search_api_solr.connector'), $container
->get('search_api.fields_helper'), $container
->get('search_api.data_type_helper'), $container
->get('solarium.query_helper'), $container
->get('entity_type.manager'), $container
->get('event_dispatcher'));
}
public function defaultConfiguration() {
return [
'retrieve_data' => FALSE,
'highlight_data' => FALSE,
'site_hash' => FALSE,
'server_prefix' => '',
'domain' => 'generic',
'environment' => 'default',
'connector' => NULL,
'connector_config' => [],
'optimize' => FALSE,
'fallback_multiple' => FALSE,
'rows' => 10,
'index_single_documents_fallback_count' => 10,
];
}
protected function addDefaultConfigurationForConfigGeneration() {
if (!isset($this->configuration['disabled_field_types'])) {
$solr_field_type_list_builder = $this->entityTypeManager
->getListBuilder('solr_field_type');
$this->configuration['disabled_field_types'] = array_keys($solr_field_type_list_builder
->getAllNotRecommendedEntities());
}
if (!isset($this->configuration['disabled_caches'])) {
$solr_cache_list_builder = $this->entityTypeManager
->getListBuilder('solr_cache');
$this->configuration['disabled_caches'] = array_keys($solr_cache_list_builder
->getAllNotRecommendedEntities());
}
if (!isset($this->configuration['disabled_request_handlers'])) {
$solr_request_handler_list_builder = $this->entityTypeManager
->getListBuilder('solr_request_handler');
$this->configuration['disabled_request_handlers'] = array_keys($solr_request_handler_list_builder
->getAllNotRecommendedEntities());
}
if (!isset($this->configuration['disabled_request_dispatchers'])) {
$solr_request_dispatcher_list_builder = $this->entityTypeManager
->getListBuilder('solr_request_dispatcher');
$this->configuration['disabled_request_dispatchers'] = array_keys($solr_request_dispatcher_list_builder
->getAllNotRecommendedEntities());
}
}
public function setConfiguration(array $configuration) {
$configuration['retrieve_data'] = (bool) $configuration['retrieve_data'];
$configuration['highlight_data'] = (bool) $configuration['highlight_data'];
$configuration['site_hash'] = (bool) $configuration['site_hash'];
$configuration['optimize'] = (bool) $configuration['optimize'];
$configuration['fallback_multiple'] = (bool) $configuration['fallback_multiple'];
$configuration['rows'] = (int) ($configuration['rows'] ?? 10);
$configuration['index_single_documents_fallback_count'] = (int) ($configuration['index_single_documents_fallback_count'] ?? 10);
parent::setConfiguration($configuration);
$this->solrConnector = NULL;
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$solr_connector_options = $this
->getSolrConnectorOptions();
$form['connector'] = [
'#type' => 'radios',
'#title' => $this
->t('Solr Connector'),
'#description' => $this
->t('Choose a connector to use for this Solr server.'),
'#options' => $solr_connector_options,
'#default_value' => $this->configuration['connector'],
'#required' => TRUE,
'#ajax' => [
'callback' => [
get_class($this),
'buildAjaxSolrConnectorConfigForm',
],
'wrapper' => 'search-api-solr-connector-config-form',
'method' => 'replace',
'effect' => 'fade',
],
];
$this
->buildConnectorConfigForm($form, $form_state);
$form['advanced'] = [
'#type' => 'details',
'#title' => $this
->t('Advanced'),
];
$form['advanced']['rows'] = [
'#type' => 'number',
'#min' => 0,
'#max' => 2147483630,
'#title' => $this
->t('Default result rows'),
'#description' => $this
->t('Solr always requires to limit the search results. This default value will be set if the Search API query itself is not limited. 2147483630 is the theoretical maximum since the result pointer is an integer. But be careful! Especially in Solr Cloud setups too high values might cause an OutOfMemoryException because Solr reserves this rows limit per shard for sorting the combined result. This sum must not exceed the maximum integer value! And even if there is no exception any too high memory consumption per query on your server is a bad thing in general.'),
'#default_value' => $this->configuration['rows'] ?: 10,
'#required' => TRUE,
];
$form['advanced']['index_single_documents_fallback_count'] = [
'#type' => 'number',
'#min' => 0,
'#max' => 100,
'#title' => $this
->t('Index single documents fallback count'),
'#description' => $this
->t('In case of an erroneous document that causes a Solr exception, the entire batch of documents will not be indexed. In order to identify the erroneous document and to keep indexing the others, the indexing process falls back to index documents one by one instead of a batch. This setting limits the amount of single documents to be indexed per batch to avoid too many commits that might slow doen the Solr server. Setting the value to "0" disables the fallback.'),
'#default_value' => $this->configuration['index_single_documents_fallback_count'] ?: 10,
'#required' => TRUE,
];
$form['advanced']['retrieve_data'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Retrieve result data from Solr'),
'#description' => $this
->t('When checked, result data will be retrieved directly from the Solr server. This might make item loads unnecessary. Only indexed fields can be retrieved. Note also that the returned field data might not always be correct, due to preprocessing and caching issues.'),
'#default_value' => $this->configuration['retrieve_data'],
];
$form['advanced']['highlight_data'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Retrieve highlighted snippets'),
'#description' => $this
->t('Return a highlighted version of the indexed fulltext fields. These will be used by the "Highlighting Processor" directly instead of applying its own PHP algorithm.'),
'#default_value' => $this->configuration['highlight_data'],
];
$form['advanced']['fallback_multiple'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Fallback to multiValued field types'),
'#description' => $this
->t('If the cardinality of a field or a property could not be detected (due to incomplete custom module implementations), a single value field type will be used within the Solr index for better performance. If this leads to "multiple values encountered for non multiValued field" exceptions you can set this option to change the fallback to multiValued.'),
'#default_value' => $this->configuration['fallback_multiple'],
];
$form['advanced']['server_prefix'] = [
'#type' => 'textfield',
'#title' => t('All index prefix'),
'#description' => t("By default, the index ID in the Solr server is the same as the index's machine name in Drupal. This setting will let you specify an additional prefix. Only use alphanumeric characters and underscores. Since changing the prefix makes the currently indexed data inaccessible, you should not change this variable when no data is indexed."),
'#default_value' => $this->configuration['server_prefix'],
];
$domains = SolrFieldType::getAvailableDomains();
$form['advanced']['domain'] = [
'#type' => 'select',
'#options' => array_combine($domains, $domains),
'#title' => $this
->t('Targeted content domain'),
'#description' => $this
->t('For example "UltraBot3000" would be indexed as "Ultra" "Bot" "3000" in a generic domain, "CYP2D6" has to stay like it is in a scientific domain.'),
'#default_value' => isset($this->configuration['domain']) ? $this->configuration['domain'] : 'generic',
];
$environments = Utility::getAvailableEnvironments();
$form['advanced']['environment'] = [
'#type' => 'select',
'#options' => array_combine($environments, $environments),
'#title' => $this
->t('Targeted environment'),
'#description' => $this
->t('For example "dev", "stage" or "prod".'),
'#default_value' => isset($this->configuration['environment']) ? $this->configuration['environment'] : 'default',
];
$form['advanced']['i_know_what_i_do'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Optimize the Solr index'),
'#description' => $this
->t('Optimize the Solr index once a day. Even if this option "sounds good", think twice before activating it! For most Solr setups it\'s recommended to NOT enable this feature!'),
'#default_value' => $this->configuration['optimize'],
];
$form['advanced']['optimize'] = [
'#type' => 'checkbox',
'#title' => $this
->t("Yes, I know what I'm doing and want to enable a daily optimization!"),
'#default_value' => $this->configuration['optimize'],
'#states' => [
'invisible' => [
':input[name="backend_config[advanced][i_know_what_i_do]"]' => [
'checked' => FALSE,
],
],
],
];
$form['multisite'] = [
'#type' => 'details',
'#title' => $this
->t('Multi-site compatibility'),
'#description' => $this
->t("By default a single Solr backend based Search API server is able to index the data of multiple Drupal sites. But this is an expert-only and dangerous feature that mainly exists for backward compatibility. If you really index multiple sites in one index and don't activate 'Retrieve results for this site only' below you have to ensure that you enable 'Retrieve result data from Solr'! Otherwise it could lead to any kind of errors!"),
];
$description = $this
->t("Automatically filter all searches to only retrieve results from this Drupal site. The default and intended behavior is to display results from all sites. WARNING: Enabling this filter might break features like autocomplete, spell checking or suggesters!");
$form['multisite']['site_hash'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Retrieve results for this site only'),
'#description' => $description,
'#default_value' => $this->configuration['site_hash'],
];
$form['disabled_field_types'] = [
'#type' => 'value',
'#value' => $this
->getDisabledFieldTypes(),
];
$form['disabled_caches'] = [
'#type' => 'value',
'#value' => $this
->getDisabledCaches(),
];
$form['disabled_request_handlers'] = [
'#type' => 'value',
'#value' => $this
->getDisabledRequestHandlers(),
];
$form['disabled_request_dispatchers'] = [
'#type' => 'value',
'#value' => $this
->getDisabledRequestDispatchers(),
];
return $form;
}
protected function getSolrConnectorOptions() {
$options = [];
foreach ($this->solrConnectorPluginManager
->getDefinitions() as $plugin_id => $plugin_definition) {
$options[$plugin_id] = Html::escape($plugin_definition['label']);
}
return $options;
}
public function buildConnectorConfigForm(array &$form, FormStateInterface $form_state) {
$form['connector_config'] = [];
$connector_id = $this->configuration['connector'];
if ($connector_id) {
$connector = $this->solrConnectorPluginManager
->createInstance($connector_id, $this->configuration['connector_config']);
if ($connector instanceof PluginFormInterface) {
$form_state
->set('connector', $connector_id);
if ($form_state
->isRebuilding()) {
\Drupal::messenger()
->addWarning($this
->t('Please configure the selected Solr connector.'));
}
$connector_form_state = SubformState::createForSubform($form['connector_config'], $form, $form_state);
$form['connector_config'] = $connector
->buildConfigurationForm($form['connector_config'], $connector_form_state);
$form['connector_config']['#type'] = 'details';
$form['connector_config']['#title'] = $this
->t('Configure %plugin Solr connector', [
'%plugin' => $connector
->label(),
]);
$form['connector_config']['#description'] = $connector
->getDescription();
$form['connector_config']['#open'] = TRUE;
}
}
$form['connector_config'] += [
'#type' => 'container',
];
$form['connector_config']['#attributes'] = [
'id' => 'search-api-solr-connector-config-form',
];
$form['connector_config']['#tree'] = TRUE;
}
public static function buildAjaxSolrConnectorConfigForm(array $form, FormStateInterface $form_state) {
return $form['backend_config']['connector_config'];
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
if ($form_state
->getValue('connector') != $form_state
->get('connector')) {
$new_connector = $this->solrConnectorPluginManager
->createInstance($form_state
->getValue('connector'));
if ($new_connector instanceof PluginFormInterface) {
$form_state
->setRebuild();
}
else {
$form_state
->setError($form['connector'], $this
->t('The connector could not be activated.'));
}
}
else {
$this->configuration['connector'] = $form_state
->get('connector');
$connector = $this
->getSolrConnector();
if ($connector instanceof PluginFormInterface) {
$connector_form_state = SubformState::createForSubform($form['connector_config'], $form, $form_state);
$connector
->validateConfigurationForm($form['connector_config'], $connector_form_state);
}
else {
$form_state
->setError($form['connector'], $this
->t('The connector could not be activated.'));
}
}
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['connector'] = $form_state
->get('connector');
$connector = $this
->getSolrConnector();
if ($connector instanceof PluginFormInterface) {
$connector_form_state = SubformState::createForSubform($form['connector_config'], $form, $form_state);
$connector
->submitConfigurationForm($form['connector_config'], $connector_form_state);
$form_state
->setValue('connector_config', $connector
->getConfiguration());
}
$values = $form_state
->getValues();
$values += $values['advanced'];
$values += $values['multisite'];
$values['optimize'] &= $values['i_know_what_i_do'];
foreach ($values as $key => $value) {
$form_state
->setValue($key, $value);
}
$form_state
->unsetValue('advanced');
$form_state
->unsetValue('multisite');
$form_state
->unsetValue('server_description');
$form_state
->unsetValue('i_know_what_i_do');
$this
->traitSubmitConfigurationForm($form, $form_state);
\Drupal::state()
->delete('search_api_solr.endpoint.data');
}
public function getSolrConnector() {
if (!$this->solrConnector) {
if (!($this->solrConnector = $this->solrConnectorPluginManager
->createInstance($this->configuration['connector'], $this->configuration['connector_config']))) {
throw new SearchApiException("The Solr Connector with ID '{$this->configuration}['connector']' could not be retrieved.");
}
}
return $this->solrConnector
->setEventDispatcher($this->eventDispatcher);
}
public function isAvailable() {
try {
$connector = $this
->getSolrConnector();
$server_available = $connector
->pingServer() !== FALSE;
$core_available = $connector
->pingCore() !== FALSE;
if ($server_available && !$core_available) {
\Drupal::messenger()
->addWarning($this
->t('Server %server is reachable but the configured %core is not available.', [
'%server' => $this
->getServer()
->label(),
'%core' => $connector
->isCloud() ? 'collection' : 'core',
]));
}
return $server_available && $core_available;
} catch (\Exception $e) {
$this
->logException($e);
}
return FALSE;
}
public function getSupportedFeatures() {
$features = [
'search_api_autocomplete',
'search_api_facets',
'search_api_facets_operator_or',
'search_api_granular',
'search_api_mlt',
'search_api_random_sort',
'search_api_spellcheck',
'search_api_data_type_location',
];
if (!$this
->getSolrConnector()
->isCloud()) {
$features[] = 'search_api_grouping';
}
return $features;
}
public function supportsDataType($type) {
static $custom_codes = [];
if (strpos($type, 'solr_text_custom') === 0) {
[
,
$custom_code,
] = explode(':', $type);
if (empty($custom_codes)) {
$custom_codes = SolrFieldType::getAvailableCustomCodes();
}
return in_array($custom_code, $custom_codes);
}
return in_array($type, [
'location',
'rpt',
'solr_string_storage',
'solr_text_omit_norms',
'solr_text_suggester',
'solr_text_spellcheck',
'solr_text_unstemmed',
'solr_text_wstoken',
'solr_date_range',
]);
}
public function getDiscouragedProcessors() {
return [
'ignorecase',
'snowball_stemmer',
'stemmer',
'stopwords',
'tokenizer',
'transliteration',
];
}
public function viewSettings() {
$connector = $this
->getSolrConnector();
$cloud = $connector instanceof SolrCloudConnectorInterface;
$info[] = [
'label' => $this
->t('Solr connector plugin'),
'info' => $connector
->label(),
];
$info[] = [
'label' => $this
->t('Solr server URI'),
'info' => $connector
->getServerLink(),
];
if ($cloud) {
$info[] = [
'label' => $this
->t('Solr collection URI'),
'info' => $connector
->getCollectionLink(),
];
}
else {
$info[] = [
'label' => $this
->t('Solr core URI'),
'info' => $connector
->getCoreLink(),
];
}
$info = array_merge($info, $connector
->viewSettings());
if ($this->server
->status()) {
try {
$ping_server = $connector
->pingServer();
} catch (\Exception $e) {
$ping_server = FALSE;
}
if ($ping_server) {
$msg = $this
->t('The Solr server could be reached.');
}
else {
$msg = $this
->t('The Solr server could not be reached or is protected by your service provider.');
\Drupal::messenger()
->addWarning($msg);
}
$info[] = [
'label' => $this
->t('Server Connection'),
'info' => $msg,
'status' => $ping_server ? 'ok' : 'error',
];
try {
$ping = $connector
->pingCore();
} catch (\Exception $e) {
$ping = FALSE;
}
if ($ping) {
$msg = $this
->t('The Solr @core could be accessed (latency: @millisecs ms).', [
'@core' => $cloud ? 'collection' : 'core',
'@millisecs' => $ping * 1000,
]);
}
else {
$msg = $this
->t('The Solr @core could not be accessed. Further data is therefore unavailable.', [
'@core' => $cloud ? 'collection' : 'core',
]);
}
$info[] = [
'label' => $cloud ? $this
->t('Collection Connection') : $this
->t('Core Connection'),
'info' => $msg,
'status' => $ping ? 'ok' : 'error',
];
$version = $connector
->getSolrVersion();
$info[] = [
'label' => $this
->t('Configured Solr Version'),
'info' => $version,
'status' => version_compare($version, '0.0.0', '>') ? 'ok' : 'error',
];
if ($ping_server || $ping) {
$info[] = [
'label' => $this
->t('Detected Solr Version'),
'info' => $connector
->getSolrVersion(TRUE),
'status' => 'ok',
];
try {
$endpoints[0] = $connector
->getEndpoint();
$endpoints_queried = [];
foreach ($this
->getServer()
->getIndexes() as $index) {
$endpoints[$index
->id()] = $this
->getCollectionEndpoint($index);
}
foreach ($endpoints as $index_id => $endpoint) {
try {
$key = $endpoint
->getBaseUri();
} catch (UnexpectedValueException $e) {
if ($cloud && 0 === $index_id) {
$info[] = [
'label' => $this
->t('Default Collection'),
'info' => $this
->t("Default collection isn't set. Ensure that the collections are properly set on the indexes in their advanced section od the Solr specific index options."),
'status' => 'error',
];
}
else {
$info[] = [
'label' => $this
->t('Additional information'),
'info' => $this
->t('Collection or core configuration for index %index is wrong or missing: %msg', [
'%index' => $index_id,
'%msg' => $e
->getMessage(),
]),
'status' => 'error',
];
}
continue;
}
if (!in_array($key, $endpoints_queried)) {
$endpoints_queried[] = $key;
if ($cloud) {
$connector
->setCollectionNameFromEndpoint($endpoint);
}
$data = $connector
->getLuke();
if (isset($data['index']['numDocs'])) {
$stats_summary = $connector
->getStatsSummary();
$pending_msg = $stats_summary['@pending_docs'] ? $this
->t('(@pending_docs sent but not yet processed)', $stats_summary) : '';
$index_msg = $stats_summary['@index_size'] ? $this
->t('(@index_size on disk)', $stats_summary) : '';
$indexed_message = $this
->t('@num items @pending @index_msg', [
'@num' => $data['index']['numDocs'],
'@pending' => $pending_msg,
'@index_msg' => $index_msg,
]);
$info[] = [
'label' => $this
->t('%key: Indexed', [
'%key' => $key,
]),
'info' => $indexed_message,
];
if (!empty($stats_summary['@deletes_total'])) {
$info[] = [
'label' => $this
->t('%key: Pending Deletions', [
'%key' => $key,
]),
'info' => $stats_summary['@deletes_total'],
];
}
$info[] = [
'label' => $this
->t('%key: Delay', [
'%key' => $key,
]),
'info' => $this
->t('@autocommit_time before updates are processed.', $stats_summary),
];
$status = 'ok';
if (!$this
->isNonDrupalOrOutdatedConfigSetAllowed()) {
$variables[':url'] = Url::fromUri('internal:/' . drupal_get_path('module', 'search_api_solr') . '/README.md')
->toString();
if (preg_match('/^drupal-(.*?)-solr/', $stats_summary['@schema_version'], $matches)) {
if (Comparator::lessThan($matches[1], SolrBackendInterface::SEARCH_API_SOLR_MIN_SCHEMA_VERSION)) {
\Drupal::messenger()
->addError($this
->t('You are using outdated Solr configuration set. Please follow the instructions described in the <a href=":url">README.md</a> file for setting up Solr.', $variables));
$status = 'error';
}
}
else {
\Drupal::messenger()
->addError($this
->t('You are using an incompatible Solr schema. Please follow the instructions described in the <a href=":url">README.md</a> file for setting up Solr.', $variables));
$status = 'error';
}
}
$info[] = [
'label' => $this
->t('%key: Schema', [
'%key' => $key,
]),
'info' => $stats_summary['@schema_version'],
'status' => $status,
];
if (!empty($stats_summary['@collection_name'])) {
$info[] = [
'label' => $this
->t('%key: Solr Collection Name', [
'%key' => $key,
]),
'info' => $stats_summary['@collection_name'],
];
}
elseif (!empty($stats_summary['@core_name'])) {
$info[] = [
'label' => $this
->t('%key: Solr Core Name', [
'%key' => $key,
]),
'info' => $stats_summary['@core_name'],
];
}
}
}
}
try {
foreach ($this
->getMaxDocumentVersions() as $site_hash => $indexes) {
if ('#total' === $site_hash) {
$info[] = [
'label' => $this
->t('Max document _version_ for this server'),
'info' => $indexes,
];
}
else {
foreach ($indexes as $index => $datasources) {
foreach ($datasources as $datasource => $max_version) {
$info[] = [
'label' => $this
->t('Max _version_ for datasource %datasource in index %index on site %site', [
'%datasource' => $datasource,
'%index' => $index,
'%site' => $site_hash,
]),
'info' => $max_version,
];
}
}
}
}
} catch (UnexpectedValueException $e) {
$info[] = [
'label' => $this
->t('Max document _version_ for this server'),
'info' => $this
->t('Collection or core configuration for at least one index on this server is wrong or missing: %msg', [
'%index' => $index_id,
'%msg' => $e
->getMessage(),
]),
'status' => 'error',
];
}
} catch (SearchApiException $e) {
$info[] = [
'label' => $this
->t('Additional information'),
'info' => $this
->t('An error occurred while trying to retrieve additional information from the Solr server: %msg', [
'%msg' => $e
->getMessage(),
]),
'status' => 'error',
];
}
}
}
$info[] = [
'label' => $this
->t('Targeted content domain'),
'info' => $this
->getDomain(),
];
if (!empty($this->configuration['disabled_field_types'])) {
\Drupal::messenger()
->addWarning($this
->t('You disabled some Solr Field Types for this server.'));
$info[] = [
'label' => $this
->t('Disabled Solr Field Types'),
'info' => implode(', ', $this->configuration['disabled_field_types']),
];
}
$info[] = [
'label' => $this
->t('Targeted environment'),
'info' => $this
->getEnvironment(),
];
if (!empty($this->configuration['disabled_caches'])) {
\Drupal::messenger()
->addWarning($this
->t('You disabled some Solr Caches for this server.'));
$info[] = [
'label' => $this
->t('Disabled Solr Caches'),
'info' => implode(', ', $this->configuration['disabled_caches']),
];
}
return $info;
}
public function updateIndex(IndexInterface $index) {
if ($this
->indexFieldsUpdated($index)) {
$index
->reindex();
$this
->getSolrFieldNames($index, TRUE);
}
}
protected function indexFieldsUpdated(IndexInterface $index) {
if (!isset($index->original)) {
return TRUE;
}
$original = $index->original;
$old_fields = $original
->getFields();
$new_fields = $index
->getFields();
if (!$old_fields && !$new_fields) {
return FALSE;
}
if (array_diff_key($old_fields, $new_fields) || array_diff_key($new_fields, $old_fields)) {
return TRUE;
}
$old_field_names = $this
->getSolrFieldNames($original, TRUE);
$new_field_names = $this
->getSolrFieldNames($index, TRUE);
return $old_field_names != $new_field_names;
}
public function removeIndex($index) {
parent::removeIndex($index);
$this
->getLanguageSpecificSolrFieldNames(LanguageInterface::LANGCODE_NOT_SPECIFIED, NULL, TRUE);
}
public function indexItems(IndexInterface $index, array $items) {
$ret = [];
$connector = $this
->getSolrConnector();
$update_query = $connector
->getUpdateQuery();
$documents = $this
->getDocuments($index, $items, $update_query);
if ($documents) {
$field_names = $this
->getSolrFieldNames($index);
$endpoint = $this
->getCollectionEndpoint($index);
try {
$update_query
->addDocuments($documents);
$connector
->update($update_query, $endpoint);
foreach ($documents as $document) {
$ret[] = $document
->getFields()[$field_names['search_api_id']];
}
} catch (SearchApiSolrException $e) {
if ($this->configuration['index_single_documents_fallback_count']) {
$count = 0;
foreach ($documents as $document) {
if ($count++ < $this->configuration['index_single_documents_fallback_count']) {
$id = $document
->getFields()[$field_names['search_api_id']];
try {
$update_query = $connector
->getUpdateQuery();
$update_query
->addDocument($document);
$connector
->update($update_query, $endpoint);
$ret[] = $id;
} catch (\Exception $e) {
watchdog_exception('search_api_solr', $e, '%type while indexing item %id: @message in %function (line %line of %file).', [
'%id' => $id,
]);
}
}
else {
break;
}
}
}
else {
watchdog_exception('search_api_solr', $e, "%type while indexing: @message in %function (line %line of %file).");
throw $e;
}
} catch (\Exception $e) {
watchdog_exception('search_api_solr', $e, "%type while indexing: @message in %function (line %line of %file).");
throw new SearchApiSolrException($e
->getMessage(), $e
->getCode(), $e);
}
if ($ret) {
\Drupal::state()
->set('search_api_solr.' . $index
->id() . '.last_update', \Drupal::time()
->getCurrentTime());
}
}
return $ret;
}
public function getDocument(IndexInterface $index, ItemInterface $item) {
$documents = $this
->getDocuments($index, [
$item
->getId() => $item,
]);
return reset($documents);
}
public function getDocuments(IndexInterface $index, array $items, UpdateQuery $update_query = NULL) {
$documents = [];
$index_id = $this
->getTargetedIndexId($index);
$site_hash = $this
->getTargetedSiteHash($index);
$languages = $this->languageManager
->getLanguages();
$request_time = $this
->formatDate(\Drupal::time()
->getRequestTime());
$base_urls = [];
if (!$update_query) {
$connector = $this
->getSolrConnector();
$update_query = $connector
->getUpdateQuery();
}
foreach ($items as $id => $item) {
$language_id = $item
->getLanguage();
$field_names = $this
->getLanguageSpecificSolrFieldNames($language_id, $index);
$boost_terms = [];
$doc = $update_query
->createDocument();
$this->eventDispatcher
->dispatch(new PreCreateIndexDocumentEvent($item, $doc));
$doc
->setField('timestamp', $request_time);
$doc
->setField('id', $this
->createId($site_hash, $index_id, $id));
$doc
->setField('index_id', $index_id);
$doc
->setField('boost_document', $item
->getBoost());
$doc
->addField('sm_context_tags', Utility::encodeSolrName('search_api/index:' . $index_id));
$doc
->setField('hash', $site_hash);
$doc
->addField('sm_context_tags', Utility::encodeSolrName('search_api_solr/site_hash:' . $site_hash));
$doc
->addField('sm_context_tags', Utility::encodeSolrName('drupal/langcode:' . $language_id));
if (!isset($base_urls[$language_id])) {
$url_options = [
'absolute' => TRUE,
];
if (isset($languages[$language_id])) {
$url_options['language'] = $languages[$language_id];
}
$base_urls[$language_id] = Url::fromRoute('<front>', [], $url_options)
->toString(TRUE)
->getGeneratedUrl();
}
$doc
->setField('site', $base_urls[$language_id]);
$item_fields = $item
->getFields();
$item_fields += $special_fields = $this
->getSpecialFields($index, $item);
foreach ($item_fields as $name => $field) {
if (!isset($field_names[$name])) {
$vars = [
'%field' => $name,
'@id' => $id,
];
$this
->getLogger()
->warning('Error while indexing: Unknown field %field on the item with ID @id.', $vars);
$doc = NULL;
break;
}
$first_value = $this
->addIndexField($doc, $field_names[$name], $field
->getValues(), $field
->getType(), $boost_terms);
if ($first_value && !array_key_exists($name, $special_fields)) {
if (strpos($field_names[$name], 't') === 0 || strpos($field_names[$name], 's') === 0) {
if (strpos($field_names[$name], 'twm_suggest') !== 0 && strpos($field_names[$name], 'spellcheck') !== 0) {
if (mb_strlen($first_value) > 128) {
$first_value = Unicode::truncate($first_value, 128);
}
$sort_languages = array_keys(\Drupal::languageManager()
->getLanguages());
$sort_languages[] = LanguageInterface::LANGCODE_NOT_SPECIFIED;
foreach ($sort_languages as $sort_language_id) {
$key = Utility::encodeSolrName('sort' . SolrBackendInterface::SEARCH_API_SOLR_LANGUAGE_SEPARATOR . $sort_language_id . '_' . $name);
if (!$doc->{$key}) {
$doc
->addField($key, $first_value);
}
}
}
}
elseif (preg_match('/^([a-z]+)m(_.*)/', $field_names[$name], $matches) && strpos($field_names[$name], 'random_') !== 0) {
$key = $matches[1] . 's' . $matches[2];
if (!$doc->{$key}) {
$doc
->addField($key, $first_value);
}
}
}
}
foreach ($boost_terms as $term => $boost) {
$doc
->addField('boost_term', sprintf('%s|%.1F', $term, $boost));
}
if ($doc) {
$this->eventDispatcher
->dispatch(new PostCreateIndexDocumentEvent($item, $doc));
$documents[] = $doc;
}
}
$this->eventDispatcher
->dispatch(new PostCreateIndexDocumentsEvent($items, $documents));
$this->moduleHandler
->alter('search_api_solr_documents', $documents, $index, $items);
return $documents;
}
public function deleteItems(IndexInterface $index, array $ids) {
try {
$index_id = $this
->getTargetedIndexId($index);
$site_hash = $this
->getTargetedSiteHash($index);
$solr_ids = [];
foreach ($ids as $id) {
$solr_ids[] = $this
->createId($site_hash, $index_id, $id);
}
$connector = $this
->getSolrConnector();
$update_query = $connector
->getUpdateQuery();
$update_query
->addDeleteQuery('_root_:("' . implode('" OR "', $solr_ids) . '")');
$update_query
->addDeleteByIds($solr_ids);
$connector
->update($update_query, $this
->getCollectionEndpoint($index));
\Drupal::state()
->set('search_api_solr.' . $index
->id() . '.last_update', \Drupal::time()
->getCurrentTime());
} catch (ExceptionInterface $e) {
throw new SearchApiSolrException($e
->getMessage(), $e
->getCode(), $e);
}
}
public function deleteAllIndexItems(IndexInterface $index, $datasource_id = NULL) {
$connector = $this
->getSolrConnector();
$index_id = $this->queryHelper
->escapeTerm($this
->getTargetedIndexId($index));
$site_hash = $this->queryHelper
->escapeTerm($this
->getTargetedSiteHash($index));
$query = '+index_id:' . $index_id;
$query .= ' +hash:' . $site_hash;
if ($datasource_id) {
$query .= ' +' . $this
->getSolrFieldNames($index)['search_api_datasource'] . ':' . $this->queryHelper
->escapeTerm($datasource_id);
}
$update_query = $connector
->getUpdateQuery();
$update_query
->addDeleteQuery($query);
$connector
->update($update_query, $this
->getCollectionEndpoint($index));
if ($connector
->isCloud()) {
$connector
->deleteCheckpoints($index_id, $site_hash);
}
\Drupal::state()
->set('search_api_solr.' . $index
->id() . '.last_update', \Drupal::time()
->getCurrentTime());
}
public function getIndexFilterQueryString(IndexInterface $index) {
$fq = '+index_id:' . $this->queryHelper
->escapeTerm($this
->getTargetedIndexId($index));
if ($this->configuration['site_hash']) {
$fq .= ' +hash:' . $this->queryHelper
->escapeTerm($this
->getTargetedSiteHash($index));
}
return $fq;
}
public function getCollectionEndpoint(IndexInterface $index) {
$connector = $this
->getSolrConnector();
if ($this->solrConnector
->isCloud()) {
$endpoint_name = $this
->getIndexFilterQueryString($index);
try {
return $this->solrConnector
->getEndpoint($endpoint_name);
} catch (OutOfBoundsException $e) {
$additional_config = [];
if ($settings = Utility::getIndexSolrSettings($index)) {
if ($settings['advanced']['collection']) {
$additional_config['core'] = $settings['advanced']['collection'];
}
}
return $this->solrConnector
->createEndpoint($endpoint_name, $additional_config);
}
}
return $connector
->getEndpoint();
}
public function finalizeIndex(IndexInterface $index) {
static $finalization_in_progress = [];
if ($index
->status() && !isset($finalization_in_progress[$index
->id()]) && !$index
->isReadOnly()) {
$settings = Utility::getIndexSolrSettings($index);
if (!empty($settings['finalize']) && \Drupal::state()
->get('search_api_solr.' . $index
->id() . '.last_update', 0) >= \Drupal::state()
->get('search_api_solr.' . $index
->id() . '.last_finalization', 0)) {
$lock = \Drupal::lock();
$lock_name = 'search_api_solr.' . $index
->id() . '.finalization_lock';
if ($lock
->acquire($lock_name)) {
$vars = [
'%index_id' => $index
->id(),
'%pid' => getmypid(),
];
$this
->getLogger()
->debug('PID %pid, Index %index_id: Finalization lock acquired.', $vars);
$finalization_in_progress[$index
->id()] = TRUE;
$connector = $this
->getSolrConnector();
$previous_query_timeout = $connector
->adjustTimeout($connector
->getFinalizeTimeout(), SolrConnectorInterface::QUERY_TIMEOUT);
$previous_index_timeout = $connector
->adjustTimeout($connector
->getFinalizeTimeout(), SolrConnectorInterface::INDEX_TIMEOUT);
try {
if (!empty($settings['commit_before_finalize'])) {
$this
->ensureCommit($index);
}
$this->moduleHandler
->invokeAll('search_api_solr_finalize_index', [
$index,
]);
if (!empty($settings['commit_after_finalize'])) {
$this
->ensureCommit($index);
}
\Drupal::state()
->set('search_api_solr.' . $index
->id() . '.last_finalization', \Drupal::time()
->getRequestTime());
$lock
->release($lock_name);
$vars = [
'%index_id' => $index
->id(),
'%pid' => getmypid(),
];
$this
->getLogger()
->debug('PID %pid, Index %index_id: Finalization lock released.', $vars);
} catch (\Exception $e) {
unset($finalization_in_progress[$index
->id()]);
$lock
->release('search_api_solr.' . $index
->id() . '.finalization_lock');
if ($e instanceof StreamException) {
throw new SearchApiSolrException($e
->getMessage() . "\n" . ExpressionBuilder::indent($e
->getExpression()), $e
->getCode(), $e);
}
throw new SearchApiSolrException($e
->getMessage(), $e
->getCode(), $e);
}
unset($finalization_in_progress[$index
->id()]);
$connector
->adjustTimeout($previous_query_timeout, SolrConnectorInterface::QUERY_TIMEOUT);
$connector
->adjustTimeout($previous_index_timeout, SolrConnectorInterface::INDEX_TIMEOUT);
return TRUE;
}
if ($lock
->wait($lock_name)) {
$vars = [
'%index_id' => $index
->id(),
'%pid' => getmypid(),
];
$this
->getLogger()
->debug('PID %pid, Index %index_id: Waited unsuccessfully for finalization lock.', $vars);
throw new SearchApiSolrException('The search index currently being rebuilt. Try again later.');
}
$this
->finalizeIndex($index);
}
}
return FALSE;
}
public function search(QueryInterface $query) {
$index = $query
->getIndex();
$this
->finalizeIndex($index);
if ($query
->getOption('solr_streaming_expression', FALSE)) {
if ($solarium_result = $this
->executeStreamingExpression($query)) {
$search_api_result_set = $this
->extractResults($query, $solarium_result);
$this->moduleHandler
->alter('search_api_solr_search_results', $search_api_result_set, $query, $solarium_result);
$this
->postQuery($search_api_result_set, $query, $solarium_result);
}
else {
throw new SearchApiSolrException('Streaming expression has no result.');
}
}
else {
$mlt_options = $query
->getOption('search_api_mlt');
if (!empty($mlt_options)) {
$query
->addTag('mlt');
}
$language_ids = $this
->ensureLanguageCondition($query);
$connector = $this
->getSolrConnector();
$solarium_query = NULL;
$edismax = NULL;
$index_fields = $index
->getFields();
$index_fields += $this
->getSpecialFields($index);
if ($query
->hasTag('mlt')) {
$solarium_query = $this
->getMoreLikeThisQuery($query);
}
else {
$solarium_query = $connector
->getSelectQuery();
$edismax = $solarium_query
->getEDisMax();
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($language_ids, $index);
$search_fields = $this
->getQueryFulltextFields($query);
$query_fields_boosted = [];
foreach ($search_fields as $search_field) {
$field = $index_fields[$search_field];
$boost = $field
->getBoost() ? '^' . $field
->getBoost() : '';
$names = [];
$first_name = reset($field_names[$search_field]);
if (strpos($first_name, 't') === 0) {
$names = array_values($field_names[$search_field]);
}
else {
$names[] = $first_name;
}
foreach (array_unique($names) as $name) {
$query_fields_boosted[] = $name . $boost;
}
}
$edismax
->setQueryFields(implode(' ', $query_fields_boosted));
}
$options = $query
->getOptions();
$filter_queries = $this
->getFilterQueries($query, $options);
foreach ($filter_queries as $id => $filter_query) {
$solarium_query
->createFilterQuery('filters_' . $id)
->setQuery($filter_query['query'])
->addTags($filter_query['tags']);
}
if (!Utility::hasIndexJustSolrDocumentDatasource($index)) {
$solarium_query
->createFilterQuery('index_filter')
->setQuery($this
->getIndexFilterQueryString($index));
}
else {
$config = $index
->getDatasource('solr_document')
->getConfiguration();
if (!empty($config['request_handler'])) {
$solarium_query
->addParam('qt', $config['request_handler']);
}
if (!$solarium_query
->getQuery() && !empty($config['default_query'])) {
$solarium_query
->setQuery($config['default_query']);
}
$params = $solarium_query
->getParams();
if (!isset($params['q.op'])) {
$solarium_query
->addParam('q.op', 'OR');
}
}
$unspecific_field_names = $this
->getSolrFieldNames($index);
if (!empty($unspecific_field_names['search_api_language'])) {
$solarium_query
->createFilterQuery('language_filter')
->setQuery($this
->createFilterQuery($unspecific_field_names['search_api_language'], $language_ids, 'IN', new Field($index, 'search_api_language'), $options));
}
$search_api_retrieved_field_values = array_flip($query
->getOption('search_api_retrieved_field_values', []));
if (array_key_exists('search_api_solr_score_debugging', $search_api_retrieved_field_values)) {
unset($search_api_retrieved_field_values['search_api_solr_score_debugging']);
$solarium_query
->getDebug();
}
$search_api_retrieved_field_values = array_keys($search_api_retrieved_field_values);
if ($query
->hasTag('mlt')) {
$this
->setFields($solarium_query, $search_api_retrieved_field_values, $query, FALSE);
}
else {
$this
->setFields($solarium_query, $search_api_retrieved_field_values, $query);
$this
->setSorts($solarium_query, $query);
$this
->setFacets($query, $solarium_query);
if (isset($options['search_api_location'])) {
$this
->setSpatial($solarium_query, $options['search_api_location'], $query);
}
if (isset($options['search_api_rpt'])) {
$this
->setRpt($solarium_query, $options['search_api_rpt'], $query);
}
if (isset($options['search_api_grouping'])) {
$this
->setGrouping($solarium_query, $query, $options['search_api_grouping'], $index_fields, $field_names);
}
if (isset($options['search_api_spellcheck'])) {
$this
->setSpellcheck($solarium_query, $query, $options['search_api_spellcheck']);
}
}
if (isset($options['offset'])) {
$solarium_query
->setStart($options['offset']);
}
$solarium_query
->setRows($query
->getOption('limit') ?? $this->configuration['rows'] ?? 10);
foreach ($options as $option => $value) {
if (strpos($option, 'solr_param_') === 0) {
$solarium_query
->addParam(substr($option, 11), $value);
}
}
$this
->applySearchWorkarounds($solarium_query, $query);
try {
$this->moduleHandler
->alter('search_api_solr_query', $solarium_query, $query);
$this
->preQuery($solarium_query, $query);
if ($edismax) {
$parse_mode = $query
->getParseMode();
$parse_mode_id = $parse_mode
->getPluginId();
$params = $solarium_query
->getParams();
$keys = $query
->getKeys();
$query_fields_boosted = $edismax
->getQueryFields() ?? '';
if (isset($params['defType']) && 'edismax' === $params['defType']) {
$flatten_keys = 'direct' === $parse_mode_id ? $keys : Utility::flattenKeys($keys, [], 'keys');
}
else {
$settings = Utility::getIndexSolrSettings($index);
$flatten_keys = Utility::flattenKeys($keys, $query_fields_boosted ? explode(' ', $query_fields_boosted) : [], $parse_mode_id, $settings['term_modifiers']);
}
if ('direct' !== $parse_mode_id && strpos($flatten_keys, '-(') === 0) {
$flatten_keys = '*:* ' . $flatten_keys;
}
$flatten_query = [];
if (!Utility::hasIndexJustSolrDocumentDatasource($index) && (!isset($params['defType']) || 'edismax' !== $params['defType'])) {
$sorts = $solarium_query
->getSorts();
$relevance_field = reset($field_names['search_api_relevance']);
if (isset($sorts[$relevance_field])) {
if ($boosts = $query
->getOption('solr_document_boost_factors', [])) {
$sum[] = 'boost_document';
foreach ($boosts as $field_id => $boost) {
$solr_field_name = Utility::getSortableSolrField($field_id, $field_names, $query);
$sum[] = str_replace(self::FIELD_PLACEHOLDER, $solr_field_name, $boost);
}
$flatten_query[] = '{!boost b=sum(' . implode(',', $sum) . ')}';
}
else {
$flatten_query[] = '{!boost b=boost_document}';
}
if (version_compare($connector
->getSolrMajorVersion(), '6', '>=')) {
$flatten_query[] = Utility::flattenKeysToPayloadScore($keys, $parse_mode);
}
}
}
$flatten_query[] = trim($flatten_keys ?: '*:*');
$solarium_query
->setQuery(implode(' ', $flatten_query));
if (!isset($params['defType']) || 'edismax' !== $params['defType']) {
$solarium_query
->removeComponent(ComponentAwareQueryInterface::COMPONENT_EDISMAX);
}
else {
$solarium_query
->removeParam('defType');
}
}
$this->moduleHandler
->alter('search_api_solr_converted_query', $solarium_query, $query);
$response = $connector
->search($solarium_query, $this
->getCollectionEndpoint($index));
$body = $response
->getBody();
if (200 != $response
->getStatusCode()) {
throw new SearchApiSolrException(strip_tags($body), $response
->getStatusCode());
}
$search_api_response = new Response($body, $response
->getHeaders());
$solarium_result = $connector
->createSearchResult($solarium_query, $search_api_response);
$search_api_result_set = $this
->extractResults($query, $solarium_result);
if ($solarium_result instanceof Result) {
if ($solarium_facet_set = $solarium_result
->getFacetSet()) {
$search_api_result_set
->setExtraData('facet_set', $solarium_facet_set);
if ($search_api_facets = $this
->extractFacets($query, $solarium_result)) {
$search_api_result_set
->setExtraData('search_api_facets', $search_api_facets);
}
}
if (isset($options['search_api_spellcheck'])) {
$search_api_spellcheck['suggestions'] = $this
->extractSpellCheckSuggestions($solarium_result);
if (!empty($options['search_api_spellcheck']['collate'])) {
if ($spellcheck_result = $solarium_result
->getComponent(ComponentAwareQueryInterface::COMPONENT_SPELLCHECK)) {
if ($collation = $spellcheck_result
->getCollation()) {
$search_api_spellcheck['collation'] = $collation
->getQuery();
}
}
}
$search_api_result_set
->setExtraData('search_api_spellcheck', $search_api_spellcheck);
}
}
$this->moduleHandler
->alter('search_api_solr_search_results', $search_api_result_set, $query, $solarium_result);
$this
->postQuery($search_api_result_set, $query, $solarium_result);
} catch (\Exception $e) {
throw new SearchApiSolrException('An error occurred while trying to search with Solr: ' . $e
->getMessage(), $e
->getCode(), $e);
}
}
}
protected function ensureLanguageCondition(QueryInterface $query) {
$index = $query
->getIndex();
$settings = Utility::getIndexSolrSettings($index);
$language_ids = $query
->getLanguages();
if (empty($language_ids)) {
if (!$query
->hasTag('views') && $settings['multilingual']['limit_to_content_language']) {
$language_ids[] = \Drupal::languageManager()
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
}
else {
$language_ids = array_keys(\Drupal::languageManager()
->getLanguages());
}
}
if ($settings['multilingual']['include_language_independent']) {
$language_ids[] = LanguageInterface::LANGCODE_NOT_SPECIFIED;
}
$query
->setLanguages(array_unique($language_ids));
return $language_ids;
}
protected function applySearchWorkarounds(SolariumQueryInterface $solarium_query, QueryInterface $query) {
$solarium_query
->setTimeZone(Utility::getTimeZone($query
->getIndex()));
if ($query
->hasTag('server_index_status')) {
return;
}
}
protected function getRequiredFields(QueryInterface $query) {
$index = $query
->getIndex();
$field_names = $this
->getSolrFieldNames($index);
$required_fields = [
$field_names['search_api_id'],
$field_names['search_api_language'],
$field_names['search_api_relevance'],
];
if (!$this->configuration['site_hash']) {
$required_fields[] = 'hash';
}
if (Utility::hasIndexJustSolrDocumentDatasource($index)) {
$config = $this
->getDatasourceConfig($index);
$extra_fields = [
'label_field',
'url_field',
];
foreach ($extra_fields as $config_key) {
if (!empty($config[$config_key])) {
$required_fields[] = $config[$config_key];
}
}
}
return array_filter($required_fields);
}
protected function setFields(Query $solarium_query, array $fields_to_be_retrieved, QueryInterface $query, $highlight = TRUE) {
$required_fields = $this
->getRequiredFields($query);
$returned_fields = [];
$highlight_fields = [
'*',
];
if (!empty($this->configuration['retrieve_data'])) {
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($this
->ensureLanguageCondition($query), $query
->getIndex());
foreach ($fields_to_be_retrieved as $field_name) {
if (isset($field_names[$field_name])) {
$returned_fields[] = array_values($field_names[$field_name]);
}
}
if ($returned_fields) {
$highlight_fields = array_unique(array_merge(...$returned_fields));
$returned_fields = array_unique(array_merge($highlight_fields, $required_fields));
$highlight_fields = array_filter($highlight_fields, function ($v) {
return preg_match('/^t.?[sm]_/', $v) || preg_match('/^s[sm]_/', $v);
});
}
else {
$returned_fields = [
'*',
reset($field_names['search_api_relevance']),
];
}
}
else {
$returned_fields = $required_fields;
}
$solarium_query
->setFields(array_unique($returned_fields));
if ($highlight) {
try {
$highlight_config = $query
->getIndex()
->getProcessor('highlight')
->getConfiguration();
if ($highlight_config['highlight'] !== 'never') {
$this
->setHighlighting($solarium_query, $query, $highlight_fields);
}
} catch (SearchApiException $exception) {
$this
->setHighlighting($solarium_query, $query, $highlight_fields);
}
}
}
public function executeStreamingExpression(QueryInterface $query) {
$stream_expression = $query
->getOption('solr_streaming_expression', FALSE);
if (!$stream_expression) {
throw new SearchApiSolrException('Streaming expression missing.');
}
$connector = $this
->getSolrConnector();
if (!$connector instanceof SolrCloudConnectorInterface) {
throw new SearchApiSolrException('Streaming expression are only supported by a Solr Cloud connector.');
}
$index = $query
->getIndex();
$this
->finalizeIndex($index);
$stream = $connector
->getStreamQuery();
$stream
->setExpression($stream_expression);
$stream
->setOptions([
'documentclass' => StreamDocument::class,
]);
$this
->applySearchWorkarounds($stream, $query);
$result = NULL;
try {
$result = $connector
->stream($stream, $this
->getCollectionEndpoint($index));
if ($processors = $query
->getIndex()
->getProcessorsByStage(ProcessorInterface::STAGE_POSTPROCESS_QUERY)) {
foreach ($processors as $key => $processor) {
if (!$processor instanceof SolrProcessorInterface) {
unset($processors[$key]);
}
}
if (count($processors)) {
foreach ($processors as $processor) {
foreach ($result as $document) {
foreach ($document as $field_name => $field_value) {
if (is_string($field_value)) {
$document->{$field_name} = $processor
->decodeStreamingExpressionValue($field_value) ?: $field_value;
}
elseif (is_array($field_value)) {
foreach ($field_value as &$array_value) {
if (is_string($array_value)) {
$array_value = $processor
->decodeStreamingExpressionValue($array_value) ?: $array_value;
}
}
unset($array_value);
$document->{$field_name} = $field_value;
}
}
}
}
}
}
} catch (StreamException $e) {
$message = $e
->getMessage() . "\n" . ExpressionBuilder::indent($e
->getExpression());
if ($comment = $query
->getOption('solr_streaming_expression_comment', FALSE)) {
$message .= "\nComment: " . $comment;
}
throw new SearchApiSolrException($message, $e
->getCode(), $e);
} catch (\Exception $e) {
throw new SearchApiSolrException('An error occurred while trying execute a streaming expression on Solr: ' . $e
->getMessage(), $e
->getCode(), $e);
}
return $result;
}
public function executeGraphStreamingExpression(QueryInterface $query) {
$stream_expression = $query
->getOption('solr_streaming_expression', FALSE);
if (!$stream_expression) {
throw new SearchApiSolrException('Streaming expression missing.');
}
$connector = $this
->getSolrConnector();
if (!$connector instanceof SolrCloudConnectorInterface) {
throw new SearchApiSolrException('Streaming expression are only supported by a Solr Cloud connector.');
}
$index = $query
->getIndex();
$this
->finalizeIndex($index);
$graph = $connector
->getGraphQuery();
$graph
->setExpression($stream_expression);
$this
->applySearchWorkarounds($graph, $query);
try {
return $connector
->graph($graph, $this
->getCollectionEndpoint($index));
} catch (\Exception $e) {
throw new SearchApiSolrException('An error occurred while trying execute a streaming expression on Solr: ' . $e
->getMessage(), $e
->getCode(), $e);
}
}
protected function createId($site_hash, $index_id, $item_id) {
return "{$site_hash}-{$index_id}-{$item_id}";
}
protected function getDatasourceConfig(IndexInterface $index) {
$config = [];
if ($index
->isValidDatasource('solr_document')) {
$config = $index
->getDatasource('solr_document')
->getConfiguration();
}
elseif ($index
->isValidDatasource('solr_multisite_document')) {
$config = $index
->getDatasource('solr_multisite_document')
->getConfiguration();
}
return $config;
}
protected function hasIndexJustSolrDatasources(IndexInterface $index) {
return Utility::hasIndexJustSolrDatasources($index);
}
protected function hasIndexJustSolrDocumentDatasource(IndexInterface $index) {
return Utility::hasIndexJustSolrDocumentDatasource($index);
}
protected function formatSolrFieldNames($language_id, IndexInterface $index) {
$field_mapping = [
'search_api_relevance' => 'score',
'search_api_random' => 'random',
'boost_document' => 'boost_document',
];
$fields = $index
->getFields();
$fields += $this
->getSpecialFields($index);
foreach ($fields as $search_api_name => $field) {
switch ($field
->getDatasourceId()) {
case 'solr_document':
$field_mapping[$search_api_name] = $field
->getPropertyPath();
break;
case 'solr_multisite_document':
$field_mapping[$search_api_name] = Utility::encodeSolrName(preg_replace('/^(t[a-z0-9]*[ms]' . SolrBackendInterface::SEARCH_API_SOLR_LANGUAGE_SEPARATOR . ')' . LanguageInterface::LANGCODE_NOT_SPECIFIED . '(.+)/', '$1' . $language_id . '$2', Utility::decodeSolrName($field
->getPropertyPath())));
break;
default:
if (empty($field_mapping[$search_api_name])) {
$type = $field
->getType();
if ('solr_text_suggester' === $type) {
$field_mapping[$search_api_name] = 'twm_suggest';
break;
}
if ('solr_text_spellcheck' === $type) {
$field_mapping[$search_api_name] = 'spellcheck_' . $language_id;
break;
}
$type_info = Utility::getDataTypeInfo($type);
$pref = $type_info['prefix'] ?? '';
if (strpos($pref, 't') === 0) {
$pref .= 'm' . SolrBackendInterface::SEARCH_API_SOLR_LANGUAGE_SEPARATOR . $language_id;
}
else {
if ($this->fieldsHelper
->isFieldIdReserved($search_api_name)) {
$pref .= 's';
}
else {
if ($field
->getDataDefinition()
->isList() || $this
->isHierarchicalField($field)) {
$pref .= 'm';
}
else {
try {
$index_properties = $index
->getPropertyDefinitions($field
->getDatasourceId());
$pref .= $this
->getPropertyPathCardinality($field
->getPropertyPath(), $index_properties) != 1 ? 'm' : 's';
} catch (SearchApiException $e) {
$pref .= empty($this->configuration['fallback_multiple']) ? 's' : 'm';
}
}
}
}
$name = $pref . '_' . $search_api_name;
$field_mapping[$search_api_name] = Utility::encodeSolrName($name);
if ($type === 'location') {
$dist_info = Utility::getDataTypeInfo('decimal');
$field_mapping[$search_api_name . '__distance'] = Utility::encodeSolrName($dist_info['prefix'] . 's_' . $search_api_name . '__distance');
}
}
}
}
if (Utility::hasIndexJustSolrDatasources($index)) {
$config = $this
->getDatasourceConfig($index);
$field_mapping['search_api_id'] = $config['id_field'];
$field_mapping['search_api_language'] = $config['language_field'];
}
$this->moduleHandler
->alter('search_api_solr_field_mapping', $index, $field_mapping, $language_id);
return $field_mapping;
}
public function getLanguageSpecificSolrFieldNames($language_id, ?IndexInterface $index, $reset = FALSE) {
static $field_names = [];
if ($reset) {
$field_names = [];
}
if ($index) {
$index_id = $index
->id();
if (!isset($field_names[$index_id]) || !isset($field_names[$index_id][$language_id])) {
$field_names[$index_id][$language_id] = $this
->formatSolrFieldNames($language_id, $index);
}
return $field_names[$index_id][$language_id];
}
}
public function getSolrFieldNamesKeyedByLanguage(array $language_ids, IndexInterface $index, $reset = FALSE) {
$field_names = [];
foreach ($language_ids as $language_id) {
foreach ($this
->getLanguageSpecificSolrFieldNames($language_id, $index, $reset) as $name => $solr_name) {
$field_names[$name][$language_id] = $solr_name;
$reset = FALSE;
}
}
return $field_names;
}
public function getSolrFieldNames(IndexInterface $index, $reset = FALSE) {
return $this
->getLanguageSpecificSolrFieldNames(LanguageInterface::LANGCODE_NOT_SPECIFIED, $index, $reset);
}
protected function getPropertyPathCardinality($property_path, array $properties, $cardinality = 1) {
[
$key,
$nested_path,
] = SearchApiUtility::splitPropertyPath($property_path, FALSE);
if (isset($properties[$key])) {
$property = $properties[$key];
if ($property instanceof FieldDefinitionInterface) {
$storage = $property
->getFieldStorageDefinition();
if ($storage instanceof FieldStorageDefinitionInterface) {
if ($storage
->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
return FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED;
}
$cardinality *= $storage
->getCardinality();
}
}
elseif ($property
->isList() || $property instanceof ListDataDefinitionInterface) {
return FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED;
}
if (isset($nested_path)) {
$property = $this->fieldsHelper
->getInnerProperty($property);
if ($property instanceof ComplexDataDefinitionInterface) {
$cardinality = $this
->getPropertyPathCardinality($nested_path, $this->fieldsHelper
->getNestedProperties($property), $cardinality);
}
}
}
return $cardinality;
}
protected function isHierarchicalField(FieldInterface $field) {
$definition = $field
->getDataDefinition();
if ($definition instanceof ComplexDataDefinitionInterface) {
$properties = $this->fieldsHelper
->getNestedProperties($definition);
$properties[''] = $definition;
foreach ($properties as $property) {
$property = $this->fieldsHelper
->getInnerProperty($property);
if ($property instanceof EntityDataDefinitionInterface) {
if ($this
->hasHierarchicalProperties($property)) {
return TRUE;
}
}
}
}
return FALSE;
}
protected function hasHierarchicalProperties(EntityDataDefinitionInterface $property) {
$entity_type_id = $property
->getEntityTypeId();
foreach ($this->fieldsHelper
->getNestedProperties($property) as $property_2) {
$property_2 = $this->fieldsHelper
->getInnerProperty($property_2);
if ($property_2 instanceof EntityDataDefinitionInterface) {
if ($property_2
->getEntityTypeId() == $entity_type_id) {
return TRUE;
}
}
elseif ($property_2 instanceof ComplexDataDefinitionInterface) {
foreach ($property_2
->getPropertyDefinitions() as $property_3) {
$property_3 = $this->fieldsHelper
->getInnerProperty($property_3);
if ($property_3 instanceof EntityDataDefinitionInterface) {
if ($property_3
->getEntityTypeId() == $entity_type_id) {
return TRUE;
}
}
}
}
}
return FALSE;
}
protected function addIndexField(Document $doc, $key, array $values, $type, array &$boost_terms) {
if (empty($values)) {
return '';
}
if (strpos($type, 'solr_text_') === 0) {
$type = 'text';
}
$first_value = '';
foreach ($values as $value) {
if (NULL !== $value) {
switch ($type) {
case 'boolean':
$value = $value ? 'true' : 'false';
break;
case 'date':
$value = $this
->formatDate($value);
if ($value === FALSE) {
continue 2;
}
break;
case 'solr_date_range':
$start = $this
->formatDate($value
->getStart());
$end = $this
->formatDate($value
->getEnd());
$value = '[' . $start . ' TO ' . $end . ']';
break;
case 'integer':
$value = (int) $value;
break;
case 'decimal':
$value = (double) $value;
break;
case 'text':
$tokens = $value
->getTokens();
if (is_array($tokens) && !empty($tokens)) {
$legacy_solr_version = FALSE;
try {
$connector = $this
->getSolrConnector();
if ($legacy_solr_version = version_compare($connector
->getSolrMajorVersion(), '6', '<') && version_compare($connector
->getSolrMajorVersion(), '4', '>=')) {
$boost = 0.0;
}
} catch (\Exception $e) {
}
foreach ($tokens as $token) {
if ($value = $token
->getText()) {
if ($legacy_solr_version) {
if ($token
->getBoost() > $boost) {
$boost = $token
->getBoost();
}
$doc
->addField($key, $value, $boost);
}
else {
$doc
->addField($key, $value);
$boost = $token
->getBoost();
if (0.0 != $boost && 1.0 != $boost) {
$terms = preg_split('/\\s+/u', str_replace('|', ' ', $value));
foreach ($terms as $term) {
$len = mb_strlen($term);
if ($len >= 2 && $len <= 100) {
if (!array_key_exists($term, $boost_terms) || $boost_terms[$term] < $boost) {
$boost_terms[$term] = $boost;
}
}
}
}
}
if (!$first_value) {
$first_value = $value;
}
}
}
continue 2;
}
$value = $value
->getText();
case 'string':
default:
if (!$value) {
continue 2;
}
}
$doc
->addField($key, $value);
if (!$first_value) {
$first_value = $value;
}
}
}
return $first_value;
}
protected function alterSolrDocuments(array &$documents, IndexInterface $index, array $items) {
}
protected function extractResults(QueryInterface $query, ResultInterface $result) {
$index = $query
->getIndex();
$fields = $index
->getFields(TRUE);
$site_hash = $this
->getTargetedSiteHash($index);
$language_unspecific_field_names = $this
->getSolrFieldNames($index);
$id_field = $language_unspecific_field_names['search_api_id'];
$score_field = $language_unspecific_field_names['search_api_relevance'];
$language_field = $language_unspecific_field_names['search_api_language'];
$backend_defined_fields = [];
$explain = NULL;
$search_api_retrieved_field_values = $query
->getOption('search_api_retrieved_field_values', []);
if (in_array('search_api_solr_score_debugging', $search_api_retrieved_field_values)) {
if ($debug = $result
->getDebug()) {
$explain = $debug
->getExplain();
$backend_defined_fields = $this
->getBackendDefinedFields($query
->getIndex());
}
}
$result_set = $query
->getResults();
$result_set
->setExtraData('search_api_solr_response', $result
->getData());
$is_grouping = $result instanceof Result && $result
->getGrouping();
if (!$result
->getResponse() && !$is_grouping) {
$result_set
->setResultCount(0);
return $result_set;
}
$grouping = $query
->getOption('search_api_grouping');
if (!empty($grouping['use_grouping'])) {
$docs = [];
$resultCount = 0;
if ($result_set
->hasExtraData('search_api_solr_response')) {
$response = $result_set
->getExtraData('search_api_solr_response');
foreach ($grouping['fields'] as $field) {
$solr_field_name = $language_unspecific_field_names[$field];
if (!empty($response['grouped'][$solr_field_name])) {
$resultCount = count($response['grouped'][$solr_field_name]);
foreach ($response['grouped'][$solr_field_name]['groups'] as $group) {
foreach ($group['doclist']['docs'] as $doc) {
$docs[] = $doc;
}
}
}
}
$result_set
->setResultCount($resultCount);
if (count($grouping['fields']) == 1) {
$field = reset($grouping['fields']);
$solr_field_name = $language_unspecific_field_names[$field];
if (isset($response['grouped'][$solr_field_name]['ngroups'])) {
$result_set
->setResultCount($response['grouped'][$solr_field_name]['ngroups']);
}
}
}
}
else {
$result_set
->setResultCount($result
->getNumFound());
$docs = $result
->getDocuments();
}
foreach ($docs as $doc) {
if (is_array($doc)) {
$doc_fields = $doc;
}
else {
$doc_fields = $doc
->getFields();
}
if (empty($doc_fields[$id_field])) {
throw new SearchApiSolrException(sprintf('The result does not contain the essential ID field "%s".', $id_field));
}
$item_id = $doc_fields[$id_field];
if (isset($doc_fields['hash']) && !$this->configuration['site_hash'] && $doc_fields['hash'] != $site_hash) {
$item_id = $doc_fields['hash'] . '--' . $item_id;
}
$result_item = NULL;
if (Utility::hasIndexJustSolrDatasources($index)) {
$datasource = '';
if ($index
->isValidDatasource('solr_document')) {
$datasource = 'solr_document';
}
elseif ($index
->isValidDatasource('solr_multisite_document')) {
$datasource = 'solr_multisite_document';
}
$solr_document_factory = \Drupal::getContainer()
->get($datasource . '.factory');
$result_item = $this->fieldsHelper
->createItem($index, $datasource . '/' . $item_id);
$result_item
->setOriginalObject($solr_document_factory
->create($result_item));
}
else {
$result_item = $this->fieldsHelper
->createItem($index, $item_id);
}
if ($language_field && isset($doc_fields[$language_field])) {
$language_id = $doc_fields[$language_field];
$result_item
->setLanguage($language_id);
$field_names = $this
->getLanguageSpecificSolrFieldNames($language_id, $index);
}
else {
$field_names = $language_unspecific_field_names;
}
$result_item
->setExtraData('search_api_solr_document', $doc);
if (isset($doc_fields[$score_field])) {
$result_item
->setScore($doc_fields[$score_field]);
unset($doc_fields[$score_field]);
}
unset($doc_fields[$id_field]);
foreach ($field_names as $search_api_property => $solr_property) {
if (isset($doc_fields[$solr_property]) && isset($fields[$search_api_property])) {
$doc_field = is_array($doc_fields[$solr_property]) ? $doc_fields[$solr_property] : [
$doc_fields[$solr_property],
];
$field = clone $fields[$search_api_property];
foreach ($doc_field as &$value) {
$type_info = Utility::getDataTypeInfo($field
->getType()) + [
'prefix' => '_',
];
switch (substr($type_info['prefix'], 0, 1)) {
case 'd':
if (preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$/', $value)) {
$value = strtotime($value);
}
break;
case 't':
$value = new TextValue($value);
}
}
unset($value);
$field
->setValues($doc_field);
$result_item
->setField($search_api_property, $field);
}
}
$solr_id = Utility::hasIndexJustSolrDatasources($index) ? str_replace('solr_document/', '', $result_item
->getId()) : $this
->createId($this
->getTargetedSiteHash($index), $this
->getTargetedIndexId($index), $result_item
->getId());
$this
->getHighlighting($result
->getData(), $solr_id, $result_item, $field_names);
if ($explain) {
if ($explain_doc = $explain
->getDocument($solr_id)) {
$backend_defined_fields['search_api_solr_score_debugging']
->setValues([
$explain_doc
->__toString(),
]);
$result_item
->setField('search_api_solr_score_debugging', clone $backend_defined_fields['search_api_solr_score_debugging']);
}
}
$result_set
->addResultItem($result_item);
}
return $result_set;
}
protected function extractFacets(QueryInterface $query, Result $resultset) {
$this->eventDispatcher
->dispatch(new PreExtractFacetsEvent($query, $resultset));
if (!$resultset
->getFacetSet()) {
return [];
}
$field_names = $this
->getSolrFieldNames($query
->getIndex());
$connector = $this
->getSolrConnector();
$solr_version = $connector
->getSolrVersion();
$facets = [];
$index = $query
->getIndex();
$fields = $index
->getFields();
$extract_facets = $query
->getOption('search_api_facets', []);
if ($facet_fields = $resultset
->getFacetSet()
->getFacets()) {
foreach ($extract_facets as $delta => $info) {
$field = $field_names[$info['field']];
if (!empty($facet_fields[$field])) {
$min_count = $info['min_count'];
$terms = $facet_fields[$field]
->getValues();
if ($info['missing']) {
if (isset($terms[''])) {
if ($terms[''] < $min_count) {
unset($terms['']);
}
else {
arsort($terms);
if ($info['limit'] > 0 && count($terms) > $info['limit']) {
array_pop($terms);
}
}
}
}
elseif (isset($terms[''])) {
unset($terms['']);
}
$type = isset($fields[$info['field']]) ? $fields[$info['field']]
->getType() : 'string';
foreach ($terms as $term => $count) {
if ($count >= $min_count) {
if ($term === '') {
$term = '!';
}
elseif ($type === 'boolean') {
if ($term === 'true') {
$term = '"1"';
}
elseif ($term === 'false') {
$term = '"0"';
}
}
elseif ($type === 'date') {
$term = $term ? '"' . strtotime($term) . '"' : NULL;
}
else {
$term = "\"{$term}\"";
}
if ($term) {
$facets[$delta][] = [
'filter' => $term,
'count' => $count,
];
}
}
}
if (empty($facets[$delta])) {
unset($facets[$delta]);
}
}
}
}
$result_data = $resultset
->getData();
if (isset($result_data['facet_counts']['facet_queries'])) {
$spatials = $query
->getOption('search_api_location');
if ($spatials !== NULL) {
foreach ($result_data['facet_counts']['facet_queries'] as $key => $count) {
if (!preg_match('/^spatial-(.*)-(\\d+(?:\\.\\d+)?)-(\\d+(?:\\.\\d+)?)$/', $key, $matches)) {
continue;
}
if (empty($extract_facets[$matches[1]])) {
continue;
}
$facet = $extract_facets[$matches[1]];
if ($count >= $facet['min_count']) {
$facets[$matches[1]][] = [
'filter' => "[{$matches[2]} {$matches[3]}]",
'count' => $count,
];
}
}
}
}
if (isset($result_data['facet_counts']['facet_heatmaps'])) {
$spatials = $query
->getOption('search_api_rpt');
if ($spatials !== NULL) {
foreach ($result_data['facet_counts']['facet_heatmaps'] as $key => $value) {
if (!preg_match('/^rpts_(.*)$/', $key, $matches)) {
continue;
}
if (empty($extract_facets[$matches[1]])) {
continue;
}
$heatmaps = [];
if (version_compare($solr_version, '7.5', '>=')) {
$heatmaps = $value['counts_ints2D'];
}
else {
$heatmaps = array_slice($value, 15);
}
$heatmap = [];
array_walk_recursive($heatmaps, function ($heatmaps) use (&$heatmap) {
$heatmap[] = $heatmaps;
});
$count = array_sum($heatmap);
$facets[$matches[1]][] = [
'filter' => $value,
'count' => $count,
];
}
}
}
$this->eventDispatcher
->dispatch(new PostExtractFacetsEvent($query, $resultset, $facets));
return $facets;
}
protected function getFilterQueries(QueryInterface $query, array &$options) {
return $this
->createFilterQueries($query
->getConditionGroup(), $options, $query);
}
protected function createFilterQueries(ConditionGroupInterface $condition_group, array &$options, QueryInterface $query, array $language_ids = []) {
static $index_fields = [];
static $index_fulltext_fields = [];
$index = $query
->getIndex();
$index_id = $index
->id();
if (empty($language_ids)) {
unset($index_fields[$index_id]);
unset($index_fulltext_fields[$index_id]);
}
if (!isset($index_fields[$index_id])) {
$index_fields[$index_id] = $index
->getFields(TRUE) + $this
->getSpecialFields($index);
}
if (!isset($index_fulltext_fields[$index_id])) {
$index_fulltext_fields[$index_id] = $index
->getFulltextFields();
}
$conditions = $condition_group
->getConditions();
foreach ($conditions as $condition) {
if ($condition instanceof ConditionInterface) {
if ('search_api_language' === $condition
->getField()) {
$language_ids = $condition
->getValue();
if (!is_array($language_ids)) {
$language_ids = [
$language_ids,
];
}
}
}
}
if (!$language_ids) {
$language_ids = $this
->ensureLanguageCondition($query);
}
if (!$language_ids) {
throw new SearchApiSolrException('Unable to create filter queries if no language is set on any condition or the query itself.');
}
$solr_fields = $this
->getSolrFieldNamesKeyedByLanguage($language_ids, $index);
$fq = [];
foreach ($conditions as $condition) {
if ($condition instanceof ConditionInterface) {
$field = $condition
->getField();
if (!isset($solr_fields[$field])) {
throw new SearchApiException("Filter term on unknown or unindexed field {$field}.");
}
$value = $condition
->getValue();
$filter_query = '';
if (in_array($field, $index_fulltext_fields[$index_id])) {
if ($value) {
if (empty($language_ids)) {
throw new SearchApiException('Conditon on fulltext field without corresponding condition on search_api_language detected.');
}
$parse_mode_id = $query
->getParseMode()
->getPluginId();
$keys = [
'#conjunction' => 'OR',
'#negation' => $condition
->getOperator() === '<>',
];
switch ($parse_mode_id) {
case 'terms':
case 'phrase':
case 'sloppy_phrase':
case 'sloppy_terms':
case 'fuzzy_terms':
case 'edismax':
if (is_array($value)) {
$keys += $value;
}
else {
$keys[] = $value;
}
break;
case 'direct':
$keys = $value;
break;
default:
throw new SearchApiSolrException('Incompatible parse mode.');
}
$settings = Utility::getIndexSolrSettings($index);
$filter_query = Utility::flattenKeys($keys, $solr_fields[$field], $parse_mode_id, $settings['term_modifiers']);
}
else {
$nested_fqs = [];
foreach ($solr_fields[$field] as $solr_field) {
$nested_fqs[] = [
'query' => $this
->createFilterQuery($solr_field, $value, $condition
->getOperator(), $index_fields[$index_id][$field], $options),
'tags' => $condition_group
->getTags(),
];
}
$fq[] = $this
->reduceFilterQueries($nested_fqs, new ConditionGroup('=' === $condition
->getOperator() ? 'AND' : 'OR', $condition_group
->getTags()));
}
}
else {
$filter_query = $this
->createFilterQuery(reset($solr_fields[$field]), $value, $condition
->getOperator(), $index_fields[$index_id][$field], $options);
}
if ($filter_query) {
$fq[] = [
[
'query' => $filter_query,
'tags' => $condition_group
->getTags(),
],
];
}
}
else {
$nested_fqs = $this
->createFilterQueries($condition, $options, $query, $language_ids);
$fq[] = $this
->reduceFilterQueries($nested_fqs, $condition);
}
}
if ($fq) {
return array_merge(...$fq);
}
return [];
}
protected function reduceFilterQueries(array $filter_queries, ConditionGroupInterface $condition_group, $last = FALSE) {
$fq = [];
if (count($filter_queries) > 1) {
$queries = [];
$tags = [];
$pre = $condition_group
->getConjunction() === 'OR' ? '' : '+';
foreach ($filter_queries as $nested_fq) {
if (strpos($nested_fq['query'], '-') !== 0) {
$queries[] = $pre . $nested_fq['query'];
}
elseif (!$pre) {
$queries[] = '(' . $nested_fq['query'] . ')';
}
else {
$queries[] = $nested_fq['query'];
}
$tags += $nested_fq['tags'];
}
$fq[] = [
'query' => (!$last ? '(' : '') . implode(' ', $queries) . (!$last ? ')' : ''),
'tags' => array_unique($tags + $condition_group
->getTags()),
];
}
elseif (!empty($filter_queries)) {
$fq[] = [
'query' => $filter_queries[0]['query'],
'tags' => array_unique($filter_queries[0]['tags'] + $condition_group
->getTags()),
];
}
return $fq;
}
protected function createFilterQuery($field, $value, $operator, FieldInterface $index_field, array &$options) {
if (!is_array($value)) {
$value = [
$value,
];
}
foreach ($value as &$v) {
if (NULL !== $v || !in_array($operator, [
'=',
'<>',
'IN',
'NOT IN',
])) {
$v = $this
->formatFilterValue($v, $index_field
->getType());
}
}
unset($v);
if (1 == count($value)) {
$value = array_shift($value);
switch ($operator) {
case 'IN':
$operator = '=';
break;
case 'NOT IN':
$operator = '<>';
break;
}
}
if (NULL !== $value && isset($options['search_api_location'])) {
foreach ($options['search_api_location'] as &$spatial) {
if (!empty($spatial['field']) && $index_field
->getFieldIdentifier() == $spatial['field']) {
$spatial['filter_query_conditions'] = [
'field' => $field,
'value' => $value,
'operator' => $operator,
];
return NULL;
}
}
unset($spatial);
}
switch ($operator) {
case '<>':
if (NULL === $value) {
if ('location' === $index_field
->getType()) {
return $field . ':[-90,-180 TO 90,180]';
}
return $this->queryHelper
->rangeQuery($field, NULL, NULL);
}
return '(*:* -' . $field . ':' . $this->queryHelper
->escapePhrase($value) . ')';
case '<':
return $this->queryHelper
->rangeQuery($field, NULL, $value, FALSE);
case '<=':
return $this->queryHelper
->rangeQuery($field, NULL, $value);
case '>=':
return $this->queryHelper
->rangeQuery($field, $value, NULL);
case '>':
return $this->queryHelper
->rangeQuery($field, $value, NULL, FALSE);
case 'BETWEEN':
if ('location' === $index_field
->getType()) {
return $this->queryHelper
->rangeQuery($field, array_shift($value), array_shift($value), TRUE, FALSE);
}
return $this->queryHelper
->rangeQuery($field, array_shift($value), array_shift($value));
case 'NOT BETWEEN':
if ('location' === $index_field
->getType()) {
return '(+' . $field . ':[-90,-180 TO 90,180] -' . $this->queryHelper
->rangeQuery($field, array_shift($value), array_shift($value), TRUE, FALSE) . ')';
}
return '(*:* -' . $this->queryHelper
->rangeQuery($field, array_shift($value), array_shift($value)) . ')';
case 'IN':
$parts = [];
$null = FALSE;
foreach ($value as $v) {
if (NULL === $v) {
$null = TRUE;
break;
}
$parts[] = $this->queryHelper
->escapePhrase($v);
}
if ($null) {
return '(*:* -' . $this->queryHelper
->rangeQuery($field, NULL, NULL) . ')';
}
return $field . ':(' . implode(' ', $parts) . ')';
case 'NOT IN':
$parts = [];
$null = FALSE;
foreach ($value as $v) {
if (NULL === $v) {
$null = TRUE;
}
else {
$parts[] = $this->queryHelper
->escapePhrase($v);
}
}
return '(' . ($null ? $this->queryHelper
->rangeQuery($field, NULL, NULL) : '*:*') . ($parts ? ' -' . $field . ':(' . implode(' ', $parts) . ')' : '') . ')';
case '=':
default:
if (NULL === $value) {
return '(*:* -' . $this->queryHelper
->rangeQuery($field, NULL, NULL) . ')';
}
return $field . ':' . $this->queryHelper
->escapePhrase($value);
}
}
protected function createLocationFilterQuery(&$spatial) {
$spatial_method = isset($spatial['method']) && in_array($spatial['method'], [
'geofilt',
'bbox',
]) ? $spatial['method'] : 'geofilt';
$value = $spatial['filter_query_conditions']['value'];
switch ($spatial['filter_query_conditions']['operator']) {
case '<':
case '<=':
$spatial['radius'] = $value;
return '{!' . $spatial_method . '}';
case '>':
case '>=':
$spatial['min_radius'] = $value;
return "{!frange l={$value}}geodist()";
case 'BETWEEN':
$spatial['min_radius'] = array_shift($value);
$spatial['radius'] = array_shift($value);
return '{!frange l=' . $spatial['min_radius'] . ' u=' . $spatial['radius'] . '}geodist()';
case '=':
case '<>':
case 'NOT BETWEEN':
case 'IN':
case 'NOT IN':
default:
throw new SearchApiSolrException('Unsupported operator for location queries');
}
}
protected function formatFilterValue($value, $type) {
$value = trim($value);
switch ($type) {
case 'boolean':
$value = $value ? 'true' : 'false';
break;
case 'date':
$value = $this
->formatDate($value);
if ($value === FALSE) {
return 0;
}
break;
}
return $value ?? '';
}
public function formatDate($input) {
try {
$input = is_numeric($input) ? (int) $input : new \DateTime($input, timezone_open(DateTimeItemInterface::STORAGE_TIMEZONE));
} catch (\Exception $e) {
return FALSE;
}
return $this->queryHelper
->formatDate($input);
}
protected function setFacets(QueryInterface $query, Query $solarium_query) {
static $index_fulltext_fields = [];
$this->eventDispatcher
->dispatch(new PreSetFacetsEvent($query, $solarium_query));
$facets = $query
->getOption('search_api_facets', []);
if (empty($facets)) {
return;
}
$index = $query
->getIndex();
$index_id = $index
->id();
$field_names = $this
->getSolrFieldNames($index);
$facet_set = $solarium_query
->getFacetSet();
$facet_set
->setSort('count');
$facet_set
->setLimit(10);
$facet_set
->setMinCount(1);
$facet_set
->setMissing(FALSE);
foreach ($facets as $info) {
if (empty($field_names[$info['field']])) {
continue;
}
$solr_field = $field_names[$info['field']];
$facet_field = NULL;
$info += [
'query_type' => 'search_api_string',
];
switch ($info['query_type']) {
case 'search_api_granular':
$facet_field = $facet_set
->createFacetRange([
'local_key' => $solr_field,
'field' => $solr_field,
'start' => $info['min_value'],
'end' => $info['max_value'],
'gap' => $info['granularity'],
]);
$includes = [];
if ($info['include_lower']) {
$includes[] = 'lower';
}
if ($info['include_upper']) {
$includes[] = 'upper';
}
if ($info['include_edges']) {
$includes[] = 'edge';
}
$facet_field
->setInclude($includes);
break;
case 'search_api_string':
default:
if (!isset($index_fulltext_fields[$index_id])) {
$index_fulltext_fields[$index_id] = $index
->getFulltextFields();
}
if (in_array($info['field'], $index_fulltext_fields[$index_id])) {
throw new SearchApiSolrException('Facetting on fulltext fields is not yet supported. Consider to add a string field to the index for that purpose.');
}
else {
$facet_field = $facet_set
->createFacetField($solr_field)
->setField($solr_field);
}
if ($info['limit'] != 10) {
$limit = $info['limit'] ? $info['limit'] : -1;
$facet_field
->setLimit($limit);
}
if ($info['missing']) {
$facet_field
->setMissing(TRUE);
}
else {
$facet_field
->setMissing(FALSE);
}
}
if (isset($info['operator']) && strtolower($info['operator']) === 'or') {
$facet_field
->getLocalParameters()
->clearExcludes()
->addExcludes([
'facet:' . $info['field'],
]);
}
if ($info['min_count'] != 1) {
$facet_field
->setMinCount($info['min_count']);
}
}
$this->eventDispatcher
->dispatch(new PostSetFacetsEvent($query, $solarium_query));
}
protected function preQuery(SolariumQueryInterface $solarium_query, QueryInterface $query) {
}
protected function postQuery(ResultSetInterface $results, QueryInterface $query, $response) {
}
public function getAutocompleteSuggestions(QueryInterface $query, $search, $incomplete_key, $user_input) {
$suggestions = [];
if ($solarium_query = $this
->getAutocompleteQuery($incomplete_key, $user_input)) {
try {
$suggestion_factory = new SuggestionFactory($user_input);
$this
->ensureLanguageCondition($query);
$this
->setAutocompleteTermQuery($query, $solarium_query, $incomplete_key);
$result = $this
->getSolrConnector()
->autocomplete($solarium_query, $this
->getCollectionEndpoint($query
->getIndex()));
$suggestions = $this
->getAutocompleteTermSuggestions($result, $suggestion_factory, $incomplete_key);
$this
->filterDuplicateAutocompleteSuggestions($suggestions);
} catch (SearchApiException $e) {
watchdog_exception('search_api_solr', $e);
}
}
return $suggestions;
}
protected function getAutocompleteQuery(&$incomplete_key, &$user_input) {
$incomplete_key = mb_strtolower($incomplete_key);
$user_input = mb_strtolower($user_input);
$connector = $this
->getSolrConnector();
$solr_version = $connector
->getSolrVersion();
if (version_compare($solr_version, '6.5', '=')) {
$this
->getLogger()
->error('Solr 6.5.x contains a bug that breaks the autocomplete feature. Downgrade to 6.4.x or upgrade to 6.6.x at least.');
return NULL;
}
return $connector
->getAutocompleteQuery();
}
protected function getAutocompleteFields(QueryInterface $query) {
$fl = [];
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($this
->ensureLanguageCondition($query), $query
->getIndex());
foreach (parent::getQueryFulltextFields($query) as $fulltext_field) {
$fl[] = array_values($field_names[$fulltext_field]);
}
return array_unique(array_merge(...$fl));
}
protected function filterDuplicateAutocompleteSuggestions(array &$suggestions) {
$added_suggestions = [];
$added_urls = [];
foreach ($suggestions as $key => $suggestion) {
if (!in_array($suggestion
->getSuggestedKeys(), $added_suggestions, TRUE) || !in_array($suggestion
->getUrl(), $added_urls, TRUE)) {
$added_suggestions[] = $suggestion
->getSuggestedKeys();
$added_urls[] = $suggestion
->getUrl();
}
else {
unset($suggestions[$key]);
}
}
}
public function getTermsSuggestions(QueryInterface $query, SearchInterface $search, $incomplete_key, $user_input) {
$this->moduleHandler
->alter('search_api_solr_terms_autocomplete_query', $query);
return $this
->getAutocompleteSuggestions($query, $search, $incomplete_key, $user_input);
}
public function getSpellcheckSuggestions(QueryInterface $query, SearchInterface $search, $incomplete_key, $user_input) {
$suggestions = [];
if ($solarium_query = $this
->getAutocompleteQuery($incomplete_key, $user_input)) {
try {
$suggestion_factory = new SuggestionFactory($user_input);
$this
->setAutocompleteSpellCheckQuery($query, $solarium_query, $user_input);
$this->moduleHandler
->alter('search_api_solr_spellcheck_autocomplete_query', $solarium_query, $query);
$result = $this
->getSolrConnector()
->autocomplete($solarium_query, $this
->getCollectionEndpoint($query
->getIndex()));
$suggestions = $this
->getAutocompleteSpellCheckSuggestions($result, $suggestion_factory);
$this
->filterDuplicateAutocompleteSuggestions($suggestions);
} catch (SearchApiException $e) {
watchdog_exception('search_api_solr', $e);
}
}
return $suggestions;
}
public function getSuggesterSuggestions(QueryInterface $query, SearchInterface $search, $incomplete_key, $user_input, array $options = []) {
$suggestions = [];
if ($solarium_query = $this
->getAutocompleteQuery($incomplete_key, $user_input)) {
try {
$suggestion_factory = new SuggestionFactory($user_input);
$this
->setAutocompleteSuggesterQuery($query, $solarium_query, $user_input, $options);
$this->moduleHandler
->alter('search_api_solr_suggester_autocomplete_query', $solarium_query, $query);
$result = $this
->getSolrConnector()
->autocomplete($solarium_query, $this
->getCollectionEndpoint($query
->getIndex()));
$suggestions = $this
->getAutocompleteSuggesterSuggestions($result, $suggestion_factory);
$this
->filterDuplicateAutocompleteSuggestions($suggestions);
} catch (SearchApiException $e) {
watchdog_exception('search_api_solr', $e);
}
}
return $suggestions;
}
protected function setAutocompleteSpellCheckQuery(QueryInterface $query, AutocompleteQuery $solarium_query, $user_input) {
$this
->setSpellcheck($solarium_query, $query, [
'keys' => [
$user_input,
],
'count' => $query
->getOption('limit') ?? 1,
]);
}
protected function setAutocompleteTermQuery(QueryInterface $query, AutocompleteQuery $solarium_query, $incomplete_key) {
$fl = $this
->getAutocompleteFields($query);
$terms_component = $solarium_query
->getTerms();
$terms_component
->setFields($fl);
$terms_component
->setPrefix($incomplete_key);
$terms_component
->setLimit($query
->getOption('limit') ?? 10);
}
protected function setAutocompleteSuggesterQuery(QueryInterface $query, AutocompleteQuery $solarium_query, $user_input, array $options = []) {
$langcodes = $this
->ensureLanguageCondition($query);
if (isset($options['context_filter_tags'])) {
if (in_array('drupal/langcode:multilingual', $options['context_filter_tags'])) {
if ($langcodes && 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 {
$tag_name = Utility::encodeSolrName('drupal/langcode:');
$options['context_filter_tags'] = str_replace('drupal/langcode:multilingual', '(' . $tag_name . implode(' ' . $tag_name, $langcodes) . ')', $options['context_filter_tags']);
$options['dictionary'] = $langcodes;
}
}
else {
foreach ($options['context_filter_tags'] as $key => $tag) {
if (strpos($tag, 'drupal/langcode:') === 0) {
$langcode_array = explode(':', $tag);
if (isset($langcode_array[1]) && 'any' !== $langcode_array[1]) {
$options['dictionary'] = $langcode_array[1] ?: LanguageInterface::LANGCODE_NOT_SPECIFIED;
break;
}
}
}
}
if (empty($options['dictionary'])) {
foreach ($options['context_filter_tags'] as $key => $tag) {
if (strpos($tag, 'drupal/langcode:') === 0) {
unset($options['context_filter_tags'][$key]);
break;
}
}
}
}
$suggester_component = $solarium_query
->getSuggester();
$suggester_component
->setQuery($user_input);
$suggester_component
->setDictionary(!empty($options['dictionary']) ? $options['dictionary'] : LanguageInterface::LANGCODE_NOT_SPECIFIED);
if (!empty($options['context_filter_tags'])) {
$suggester_component
->setContextFilterQuery(Utility::buildSuggesterContextFilterQuery($options['context_filter_tags']));
}
$suggester_component
->setCount($query
->getOption('limit') ?? 10);
$solarium_query
->addParam('suggest.highlight', FALSE);
}
protected function getAutocompleteSpellCheckSuggestions(ResultInterface $result, SuggestionFactory $suggestion_factory) {
$suggestions = [];
foreach ($this
->extractSpellCheckSuggestions($result) as $spellcheck_suggestions) {
foreach ($spellcheck_suggestions as $keys) {
$suggestions[] = $suggestion_factory
->createFromSuggestedKeys($keys);
}
}
return $suggestions;
}
protected function extractSpellCheckSuggestions(ResultInterface $result) {
$suggestions = [];
if ($spellcheck_results = $result
->getComponent(ComponentAwareQueryInterface::COMPONENT_SPELLCHECK)) {
foreach ($spellcheck_results as $term_result) {
$keys = [];
foreach ($term_result
->getWords() as $correction) {
$keys[] = $correction['word'];
}
if ($keys) {
$suggestions[$term_result
->getOriginalTerm()] = $keys;
}
}
}
return $suggestions;
}
protected function getAutocompleteTermSuggestions(ResultInterface $result, SuggestionFactory $suggestion_factory, $incomplete_key) {
$suggestions = [];
if ($terms_results = $result
->getComponent(ComponentAwareQueryInterface::COMPONENT_TERMS)) {
$autocomplete_terms = [];
foreach ($terms_results as $fields) {
foreach ($fields as $term => $count) {
$autocomplete_terms[$term] = $count;
}
}
foreach ($autocomplete_terms as $term => $count) {
$suggestion_suffix = mb_substr($term, mb_strlen($incomplete_key));
$suggestions[] = $suggestion_factory
->createFromSuggestionSuffix($suggestion_suffix, $count);
}
}
return $suggestions;
}
protected function getAutocompleteSuggesterSuggestions(ResultInterface $result, SuggestionFactory $suggestion_factory) {
$suggestions = [];
if ($phrases_result = $result
->getComponent(ComponentAwareQueryInterface::COMPONENT_SUGGESTER)) {
$dictionaries = array_keys($phrases_result
->getResults());
foreach ($phrases_result
->getAll() as $dictionary_index => $phrases) {
foreach ($phrases
->getSuggestions() as $phrase) {
$suggestion = $suggestion_factory
->createFromSuggestedKeys($phrase['term']);
if (method_exists($suggestion, 'setDictionary')) {
$suggestion
->setDictionary($dictionaries[$dictionary_index]);
}
$suggestions[] = $suggestion;
}
}
}
return $suggestions;
}
public function getIndexId(IndexInterface $index) {
$settings = Utility::getIndexSolrSettings($index);
return $this->configuration['server_prefix'] . $settings['advanced']['index_prefix'] . $index
->id();
}
public function getTargetedIndexId(IndexInterface $index) {
static $targeted_index = [];
if (!isset($targeted_index[$index
->id()])) {
$config = $this
->getDatasourceConfig($index);
$targeted_index[$index
->id()] = $config['target_index'] ?? $this
->getIndexId($index);
}
return $targeted_index[$index
->id()];
}
public function getTargetedSiteHash(IndexInterface $index) {
static $targeted_site_hash = [];
if (!isset($targeted_site_hash[$index
->id()])) {
$config = $this
->getDatasourceConfig($index);
$targeted_site_hash[$index
->id()] = $config['target_hash'] ?? Utility::getSiteHash();
}
return $targeted_site_hash[$index
->id()];
}
public function calculateDependencies() {
$connector = $this
->getSolrConnector();
$this
->calculatePluginDependencies($connector);
$entity_type_manager = \Drupal::entityTypeManager();
$field_type_list_builder = $entity_type_manager
->getListBuilder('solr_field_type');
$field_type_list_builder
->setBackend($this);
$solr_field_types = $field_type_list_builder
->getEnabledEntities();
foreach ($solr_field_types as $solr_field_type) {
$this
->addDependency('config', $solr_field_type
->getConfigDependencyName());
}
$cache_list_builder = $entity_type_manager
->getListBuilder('solr_cache');
$cache_list_builder
->setBackend($this);
$solr_caches = $cache_list_builder
->load();
foreach ($solr_caches as $solr_cache) {
if (!$solr_cache->disabledOnServer) {
$this
->addDependency('config', $solr_cache
->getConfigDependencyName());
}
}
$request_handler_list_builder = $entity_type_manager
->getListBuilder('solr_request_handler');
$request_handler_list_builder
->setBackend($this);
$solr_request_handlers = $request_handler_list_builder
->load();
foreach ($solr_request_handlers as $request_handler) {
if (!$request_handler->disabledOnServer) {
$this
->addDependency('config', $request_handler
->getConfigDependencyName());
}
}
$request_dispatcher_list_builder = $entity_type_manager
->getListBuilder('solr_request_dispatcher');
$request_dispatcher_list_builder
->setBackend($this);
$solr_request_dispatchers = $request_dispatcher_list_builder
->load();
foreach ($solr_request_dispatchers as $request_dispatcher) {
if (!$request_dispatcher->disabledOnServer) {
$this
->addDependency('config', $request_dispatcher
->getConfigDependencyName());
}
}
return $this->dependencies;
}
protected function getHighlighting(array $data, $solr_id, ItemInterface $item, array $field_mapping) {
if (isset($data['highlighting'][$solr_id]) && !empty($this->configuration['highlight_data'])) {
$prefix = '<strong>';
$suffix = '</strong>';
try {
$highlight_config = $item
->getIndex()
->getProcessor('highlight')
->getConfiguration();
if ($highlight_config['highlight'] === 'never') {
return;
}
$prefix = $highlight_config['prefix'];
$suffix = $highlight_config['suffix'];
} catch (SearchApiException $exception) {
}
$snippets = [];
$keys = [];
foreach ($field_mapping as $search_api_property => $solr_property) {
if (!empty($data['highlighting'][$solr_id][$solr_property])) {
foreach ($data['highlighting'][$solr_id][$solr_property] as $value) {
$keys[] = Utility::getHighlightedKeys($value);
$snippets[$search_api_property][] = Utility::formatHighlighting($value, $prefix, $suffix);
}
}
}
if ($snippets) {
$item
->setExtraData('highlighted_fields', $snippets);
$item
->setExtraData('highlighted_keys', array_unique(array_merge(...$keys)));
}
}
}
protected function setHighlighting(Query $solarium_query, QueryInterface $query, array $highlighted_fields = []) {
if (!empty($this->configuration['highlight_data'])) {
$settings = Utility::getIndexSolrSettings($query
->getIndex());
$highlighter = $settings['highlighter'];
$hl = $solarium_query
->getHighlighting();
$hl
->setSimplePrefix('[HIGHLIGHT]');
$hl
->setSimplePostfix('[/HIGHLIGHT]');
$hl
->setSnippets($highlighter['highlight']['snippets']);
$hl
->setFragSize($highlighter['highlight']['fragsize']);
$hl
->setMergeContiguous($highlighter['highlight']['mergeContiguous']);
$hl
->setRequireFieldMatch($highlighter['highlight']['requireFieldMatch']);
if (51200 != $highlighter['maxAnalyzedChars']) {
$hl
->setMaxAnalyzedChars($highlighter['maxAnalyzedChars']);
}
if ('gap' !== $highlighter['fragmenter']) {
$hl
->setFragmenter($highlighter['fragmenter']);
if ('regex' !== $highlighter['fragmenter']) {
$hl
->setRegexPattern($highlighter['regex']['pattern']);
if (0.5 != $highlighter['regex']['slop']) {
$hl
->setRegexSlop($highlighter['regex']['slop']);
}
if (10000 != $highlighter['regex']['maxAnalyzedChars']) {
$hl
->setRegexMaxAnalyzedChars($highlighter['regex']['maxAnalyzedChars']);
}
}
}
if (!$highlighter['usePhraseHighlighter']) {
$hl
->setUsePhraseHighlighter(FALSE);
}
if (!$highlighter['highlightMultiTerm']) {
$hl
->setHighlightMultiTerm(FALSE);
}
if ($highlighter['preserveMulti']) {
$hl
->setPreserveMulti(TRUE);
}
foreach ($highlighted_fields as $highlighted_field) {
$hl
->addField($highlighted_field);
}
}
}
protected function getMoreLikeThisQuery(QueryInterface $query) {
$connector = $this
->getSolrConnector();
$solarium_query = $connector
->getMoreLikeThisQuery();
$mlt_options = $query
->getOption('search_api_mlt');
$language_ids = $this
->ensureLanguageCondition($query);
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($language_ids, $query
->getIndex());
$ids = [];
foreach ($query
->getIndex()
->getDatasources() as $datasource) {
if ($entity_type_id = $datasource
->getEntityTypeId()) {
$entity = \Drupal::entityTypeManager()
->getStorage($entity_type_id)
->load($mlt_options['id']);
if ($entity instanceof ContentEntityInterface) {
$translated = FALSE;
if ($entity
->isTranslatable()) {
foreach ($language_ids as $language_id) {
if ($entity
->hasTranslation($language_id)) {
$ids[] = SearchApiUtility::createCombinedId($datasource
->getPluginId(), $datasource
->getItemId($entity
->getTranslation($language_id)
->getTypedData()));
$translated = TRUE;
}
}
}
if (!$translated) {
$ids[] = SearchApiUtility::createCombinedId($datasource
->getPluginId(), $datasource
->getItemId($entity
->getTypedData()));
}
}
else {
$ids[] = $mlt_options['id'];
}
}
}
if (!empty($ids)) {
$index = $query
->getIndex();
$index_id = $this
->getTargetedIndexId($index);
$site_hash = $this
->getTargetedSiteHash($index);
if (!Utility::hasIndexJustSolrDatasources($index)) {
array_walk($ids, function (&$id, $key) use ($site_hash, $index_id) {
$id = $this
->createId($site_hash, $index_id, $id);
$id = $this->queryHelper
->escapePhrase($id);
});
}
$solarium_query
->setQuery('id:' . implode(' id:', $ids));
}
$mlt_fl = [];
foreach ($mlt_options['fields'] as $mlt_field) {
$first_field = reset($field_names[$mlt_field]);
if (strpos($first_field, 'd') !== 0) {
if (strpos($first_field, 't') !== 0) {
$mlt_fl[] = [
$first_field,
];
}
else {
$mlt_fl[] = array_values($field_names[$mlt_field]);
}
}
}
$settings = Utility::getIndexSolrSettings($query
->getIndex());
$solarium_query
->setMltFields(array_merge(...$mlt_fl))
->setMinimumTermFrequency($settings['mlt']['mintf'])
->setMinimumDocumentFrequency($settings['mlt']['mindf'])
->setMaximumQueryTerms($settings['mlt']['maxqt'])
->setMaximumNumberOfTokens($settings['mlt']['maxntp'])
->setBoost($settings['mlt']['boost'])
->setInterestingTerms($settings['mlt']['interestingTerms']);
if ($settings['mlt']['maxdf']) {
$solarium_query
->addParam('mlt.maxdf', $settings['mlt']['maxdf']);
}
if ($settings['mlt']['maxdfpct']) {
$solarium_query
->addParam('mlt.maxdf', $settings['mlt']['maxdfpct']);
}
if ($settings['mlt']['minwl']) {
$solarium_query
->setMinimumWordLength($settings['mlt']['minwl']);
}
if ($settings['mlt']['maxwl']) {
$solarium_query
->setMaximumWordLength($settings['mlt']['maxwl']);
}
return $solarium_query;
}
protected function setSpatial(Query $solarium_query, array $spatial_options, QueryInterface $query) {
if (count($spatial_options) > 1) {
throw new SearchApiSolrException('Only one spatial search can be handled per query.');
}
$field_names = $this
->getSolrFieldNames($query
->getIndex());
$spatial = reset($spatial_options);
$solr_field = $field_names[$spatial['field']];
$distance_field = $spatial['field'] . '__distance';
$solr_distance_field = $field_names[$distance_field];
$spatial['lat'] = (double) $spatial['lat'];
$spatial['lon'] = (double) $spatial['lon'];
$spatial['radius'] = isset($spatial['radius']) ? (double) $spatial['radius'] : 0.0;
$spatial['min_radius'] = isset($spatial['min_radius']) ? (double) $spatial['min_radius'] : 0.0;
if (!isset($spatial['filter_query_conditions'])) {
$spatial['filter_query_conditions'] = [];
}
$spatial['filter_query_conditions'] += [
'field' => $solr_field,
'value' => $spatial['radius'],
'operator' => '<',
];
$solarium_query
->addField($solr_distance_field . ':geodist()');
$spatial_query = $solarium_query
->getSpatial();
$spatial_query
->setDistance($spatial['radius']);
$spatial_query
->setField($solr_field);
$spatial_query
->setPoint($spatial['lat'] . ',' . $spatial['lon']);
$solarium_query
->createFilterQuery($solr_field)
->setQuery($this
->createLocationFilterQuery($spatial));
$sorts = $solarium_query
->getSorts();
if (isset($sorts[$solr_distance_field])) {
$new_sorts = [];
foreach ($sorts as $key => $order) {
if ($key == $solr_distance_field) {
$new_sorts['geodist()'] = $order;
}
else {
$new_sorts[$key] = $order;
}
}
$solarium_query
->clearSorts();
$solarium_query
->setSorts($new_sorts);
}
$facet_set = $solarium_query
->getFacetSet();
$facets = $facet_set
->getFacets();
foreach ($facets as $delta => $facet) {
$facet_options = $facet
->getOptions();
if ($facet_options['field'] != $solr_distance_field) {
continue;
}
$facet_set
->removeFacet($delta);
$limit = $facet
->getLimit();
$steps = $limit > 0 ? $limit : 5;
$step = ($spatial['radius'] - $spatial['min_radius']) / $steps;
for ($i = 0; $i < $steps; $i++) {
$distance_min = $spatial['min_radius'] + $step * $i;
$distance_max = $distance_min + $step - 1;
$key = "spatial-{$distance_field}-{$distance_min}-{$distance_max}";
$facet_set
->createFacetQuery($key . ' frange l=' . $distance_min . ' u=' . $distance_max)
->setQuery('geodist()');
}
}
}
protected function setRpt(Query $solarium_query, array $rpt_options, QueryInterface $query) {
if (count($rpt_options) > 1) {
throw new SearchApiSolrException('Only one spatial search can be handled per query.');
}
$field_names = $this
->getSolrFieldNames($query
->getIndex());
$rpt = reset($rpt_options);
$solr_field = $field_names[$rpt['field']];
$rpt['geom'] = isset($rpt['geom']) ? $rpt['geom'] : '["-180 -90" TO "180 90"]';
$solarium_query
->createFilterQuery($solr_field)
->setQuery($solr_field . ':' . $rpt['geom']);
$solarium_query
->addParam('facet', 'on');
$solarium_query
->addParam('facet.heatmap', $solr_field);
$solarium_query
->addParam('facet.heatmap.geom', $rpt['geom']);
$solarium_query
->addParam('facet.heatmap.format', $rpt['format']);
$solarium_query
->addParam('facet.heatmap.maxCells', $rpt['maxCells']);
$solarium_query
->addParam('facet.heatmap.gridLevel', $rpt['gridLevel']);
}
protected function setSorts(Query $solarium_query, QueryInterface $query) {
$field_names = $this
->getSolrFieldNamesKeyedByLanguage($this
->ensureLanguageCondition($query), $query
->getIndex());
foreach ($query
->getSorts() as $field => $order) {
$solarium_query
->addSort(Utility::getSortableSolrField($field, $field_names, $query), strtolower($order));
}
}
protected function setGrouping(Query $solarium_query, QueryInterface $query, $grouping_options = [], $index_fields = [], $field_names = []) {
if (!empty($grouping_options['use_grouping'])) {
$group_fields = [];
foreach ($grouping_options['fields'] as $collapse_field) {
$first_name = reset($field_names[$collapse_field]);
$field = $index_fields[$collapse_field];
$type = $field
->getType();
if ($this->dataTypeHelper
->isTextType($type) || 's' !== Utility::getSolrFieldCardinality($first_name)) {
$this
->getLogger()
->error('Grouping is not supported for field @field. Only single-valued fields not indexed as "Fulltext" are supported.', [
'@field' => $index_fields[$collapse_field]['name'],
]);
}
else {
$group_fields[] = $first_name;
}
}
if (!empty($group_fields)) {
$grouping_component = $solarium_query
->getGrouping();
$grouping_component
->setFields($group_fields)
->setNumberOfGroups(TRUE)
->setTruncate(!empty($grouping_options['truncate']))
->setFacet(!empty($grouping_options['group_facet']));
if (!empty($grouping_options['group_limit']) && $grouping_options['group_limit'] != 1) {
$grouping_component
->setLimit($grouping_options['group_limit']);
}
if (!empty($grouping_options['group_sort'])) {
$sorts = [];
foreach ($grouping_options['group_sort'] as $group_sort_field => $order) {
$sorts[] = Utility::getSortableSolrField($group_sort_field, $field_names, $query) . ' ' . strtolower($order);
}
$grouping_component
->setSort(implode(', ', $sorts));
}
}
}
}
protected function setSpellcheck(ComponentAwareQueryInterface $solarium_query, QueryInterface $query, array $spellcheck_options = []) {
$spellcheck = $solarium_query
->getSpellcheck();
$schema_languages = $this
->getSchemaLanguageStatistics();
$dictionaries = [];
foreach ($this
->ensureLanguageCondition($query) as $language_id) {
if (isset($schema_languages[$language_id]) && $schema_languages[$language_id]) {
$dictionaries[] = $schema_languages[$language_id];
}
}
if ($dictionaries) {
$spellcheck
->setDictionary($dictionaries);
}
else {
$spellcheck
->setDictionary(LanguageInterface::LANGCODE_NOT_SPECIFIED);
}
if (!empty($spellcheck_options['keys'])) {
$spellcheck
->setQuery(implode(' ', $spellcheck_options['keys']));
}
if (!empty($spellcheck_options['count'])) {
$spellcheck
->setCount($spellcheck_options['count']);
}
if (!empty($spellcheck_options['collate'])) {
$spellcheck
->setCollate($spellcheck_options['collate']);
}
}
public function extractContentFromFile($filepath) {
$connector = $this
->getSolrConnector();
$solr_version = $connector
->getSolrVersion();
if (version_compare($solr_version, '8.6', '>=') && version_compare($solr_version, '8.6.3', '<')) {
$this
->getLogger()
->error('Solr 8.6.0, 8.6.1 and 8.6.2 contain a bug that breaks content extraction form files. Upgrade to 8.6.3 at least.');
return '';
}
$query = $connector
->getExtractQuery();
$query
->setExtractOnly(TRUE);
$query
->setFile($filepath);
$result = $connector
->extract($query);
return $connector
->getContentFromExtractResult($result, $filepath);
}
public function getBackendDefinedFields(IndexInterface $index) {
$backend_defined_fields = [];
foreach ($index
->getFields() as $field) {
if ($field
->getType() === 'location') {
$distance_field_name = $field
->getFieldIdentifier() . '__distance';
$property_path_name = $field
->getPropertyPath() . '__distance';
$distance_field = new Field($index, $distance_field_name);
$distance_field
->setLabel($field
->getLabel() . ' (distance)');
$distance_field
->setDataDefinition(DataDefinition::create('decimal'));
$distance_field
->setType('decimal');
$distance_field
->setDatasourceId($field
->getDatasourceId());
$distance_field
->setPropertyPath($property_path_name);
$backend_defined_fields[$distance_field_name] = $distance_field;
}
}
$backend_defined_fields['search_api_solr_score_debugging'] = $this
->getFieldsHelper()
->createField($index, 'search_api_solr_score_debugging', [
'label' => 'Solr score debugging',
'description' => $this
->t('Detailed information about the score calculation.'),
'type' => 'string',
'property_path' => 'search_api_solr_score_debugging',
'data_definition' => DataDefinition::create('string'),
]);
return $backend_defined_fields;
}
public function getDomain() {
return isset($this->configuration['domain']) && !empty($this->configuration['domain']) ? $this->configuration['domain'] : 'generic';
}
public function getEnvironment() {
return isset($this->configuration['environment']) && !empty($this->configuration['environment']) ? $this->configuration['environment'] : 'default';
}
public function isManagedSchema() {
return FALSE;
}
public function isOptimizeEnabled() {
return isset($this->configuration['optimize']) ? $this->configuration['optimize'] : FALSE;
}
protected function getQueryFulltextFields(QueryInterface $query) {
$fulltext_fields = parent::getQueryFulltextFields($query);
$solr_field_names = $this
->getSolrFieldNames($query
->getIndex());
return array_filter($fulltext_fields, function ($value) use ($solr_field_names) {
return 'twm_suggest' !== $solr_field_names[$value] & strpos($solr_field_names[$value], 'spellcheck') !== 0;
});
}
public function getSchemaLanguageStatistics(?Endpoint $endpoint = NULL) {
$available = $this
->getSolrConnector()
->pingServer();
$stats = [];
foreach (\Drupal::languageManager()
->getLanguages() as $language) {
$language_id = $language
->getId();
$converted_language_id = str_replace('-', '_', $language_id);
$stats[$language_id] = $available ? $this
->isPartOfSchema('fieldTypes', 'text_' . $converted_language_id, $endpoint) ? $converted_language_id : FALSE : FALSE;
if (!$stats[$language_id]) {
$converted_language_id = preg_replace('/-.+$/', '', $language_id);
$stats[$language_id] = $available ? $this
->isPartOfSchema('fieldTypes', 'text_' . $converted_language_id, $endpoint) ? $converted_language_id : FALSE : FALSE;
}
}
return $stats;
}
protected function isPartOfSchema($kind, $name, ?Endpoint $endpoint = NULL) {
static $previous_calls;
$endpoint_key = $endpoint ? $endpoint
->getKey() : $this
->getServer()
->id();
$state = \Drupal::state();
$schema_parts = $state
->get('search_api_solr.endpoint.schema_parts');
if (!is_array($schema_parts) || empty($schema_parts[$endpoint_key]) || empty($schema_parts[$endpoint_key][$kind]) || !in_array($name, $schema_parts[$endpoint_key][$kind]) && !isset($previous_calls[$endpoint_key][$kind])) {
$response = $this
->getSolrConnector()
->coreRestGet('schema/' . strtolower($kind), $endpoint);
if (empty($response[$kind])) {
throw new SearchApiSolrException('Missing information about ' . $kind . ' in response to REST request.');
}
$schema_parts[$endpoint_key][$kind] = [];
foreach ($response[$kind] as $row) {
$schema_parts[$endpoint_key][$kind][] = $row['name'];
}
$state
->set('search_api_solr.endpoint.schema_parts', $schema_parts);
$previous_calls[$endpoint_key][$kind] = TRUE;
}
return in_array($name, $schema_parts[$endpoint_key][$kind]);
}
public function getIndexSolrSettings(IndexInterface $index) {
return Utility::getIndexSolrSettings($index);
}
public function getDocumentCounts() {
$document_counts = [
'#total' => 0,
];
if ($indexes = $this
->getServer()
->getIndexes()) {
$connector_endpoints_queried = [];
foreach ($indexes as $index) {
$collection_endpoint = $this
->getCollectionEndpoint($index);
$key = $collection_endpoint
->getBaseUri();
if (!in_array($key, $connector_endpoints_queried)) {
$connector_endpoints_queried[] = $key;
$collection_document_counts = $this
->doDocumentCounts($collection_endpoint);
$collection_document_counts['#total'] += $document_counts['#total'];
$document_counts = ArrayUtils::merge($document_counts, $collection_document_counts, TRUE);
}
}
}
else {
$connector = $this
->getSolrConnector();
$connector_endpoint = $connector
->getEndpoint();
return $this
->doDocumentCounts($connector_endpoint);
}
return $document_counts;
}
protected function doDocumentCounts(Endpoint $endpoint) : array {
$connector = $this
->getSolrConnector();
if (version_compare($connector
->getSolrVersion(), '5.0.0', '<')) {
return [
'#total' => 0,
];
}
try {
$query = $connector
->getSelectQuery()
->addFilterQuery(new FilterQuery([
'local_key' => 'search_api',
'query' => '+hash:* +index_id:*',
]))
->setRows(1)
->setFields('id');
$facet_set = $query
->getFacetSet();
$json_facet_query = $facet_set
->createJsonFacetTerms([
'local_key' => 'siteHashes',
'limit' => -1,
'field' => 'hash',
]);
$nested_json_facet_terms = $facet_set
->createJsonFacetTerms([
'local_key' => 'numDocsPerIndex',
'limit' => -1,
'field' => 'index_id',
], FALSE);
$json_facet_query
->addFacet($nested_json_facet_terms);
$result = $connector
->execute($query, $endpoint);
} catch (\Exception $e) {
$query = $connector
->getSelectQuery()
->setRows(1);
$facet_set = $query
->getFacetSet();
$facet_set
->createJsonFacetAggregation([
'local_key' => 'maxVersion',
'function' => 'max(_version_)',
]);
if (version_compare($connector
->getSolrVersion(), '8.1.0', '>=')) {
$query
->setOmitHeader(FALSE);
}
$result = $connector
->execute($query, $endpoint);
}
$facet_set = $result
->getFacetSet();
$count = $facet_set
->getFacet('count');
$document_counts = [
'#total' => $count
->getValue(),
];
if ($site_hashes = $facet_set
->getFacet('siteHashes')) {
foreach ($site_hashes
->getBuckets() as $site_hash_bucket) {
$site_hash = $site_hash_bucket
->getValue();
foreach ($site_hash_bucket
->getFacetSet()
->getFacet('numDocsPerIndex') as $index_bucket) {
$index = $index_bucket
->getValue();
$document_counts[$site_hash][$index] = $index_bucket
->getCount();
}
}
}
return $document_counts;
}
public function getMaxDocumentVersions() {
$document_versions = [
'#total' => 0,
];
if ($indexes = $this
->getServer()
->getIndexes()) {
$connector_endpoints_queried = [];
foreach ($indexes as $index) {
$collection_endpoint = $this
->getCollectionEndpoint($index);
$key = $collection_endpoint
->getBaseUri();
if (!in_array($key, $connector_endpoints_queried)) {
$connector_endpoints_queried[] = $key;
$collection_document_versions = $this
->doGetMaxDocumentVersions($collection_endpoint);
$collection_document_versions['#total'] += $document_versions['#total'];
$document_versions = ArrayUtils::merge($document_versions, $collection_document_versions, TRUE);
}
}
}
else {
try {
$connector = $this
->getSolrConnector();
$connector_endpoint = $connector
->getEndpoint();
return $this
->doGetMaxDocumentVersions($connector_endpoint);
} catch (\Exception $e) {
}
}
return $document_versions;
}
protected function doGetMaxDocumentVersions(Endpoint $endpoint) : array {
$connector = $this
->getSolrConnector();
$document_versions = [
'#total' => 0,
];
try {
$query = $connector
->getSelectQuery()
->addFilterQuery(new FilterQuery([
'local_key' => 'search_api',
'query' => '+hash:* +index_id:*',
]))
->setRows(1)
->setFields('id');
$facet_set = $query
->getFacetSet();
$facet_set
->createJsonFacetAggregation([
'local_key' => 'maxVersion',
'function' => 'max(_version_)',
]);
$siteHashes = $facet_set
->createJsonFacetTerms([
'local_key' => 'siteHashes',
'limit' => -1,
'field' => 'hash',
]);
$indexes = $facet_set
->createJsonFacetTerms([
'local_key' => 'indexes',
'limit' => -1,
'field' => 'index_id',
], FALSE);
$dataSources = $facet_set
->createJsonFacetTerms([
'local_key' => 'dataSources',
'limit' => -1,
'field' => 'ss_search_api_datasource',
], FALSE);
$maxVersionPerDataSource = $facet_set
->createJsonFacetAggregation([
'local_key' => 'maxVersionPerDataSource',
'function' => 'max(_version_)',
], FALSE);
$dataSources
->addFacet($maxVersionPerDataSource);
$indexes
->addFacet($dataSources);
$siteHashes
->addFacet($indexes);
$result = $connector
->execute($query, $endpoint);
} catch (\Exception $e) {
$query = $connector
->getSelectQuery()
->setRows(1);
$facet_set = $query
->getFacetSet();
$facet_set
->createJsonFacetAggregation([
'local_key' => 'maxVersion',
'function' => 'max(_version_)',
]);
$result = $connector
->execute($query, $endpoint);
}
$facet_set = $result
->getFacetSet();
if ($maxVersion = $facet_set
->getFacet('maxVersion')) {
$document_versions = [
'#total' => $maxVersion
->getValue(),
];
if ($site_hashes = $facet_set
->getFacet('siteHashes')) {
foreach ($site_hashes
->getBuckets() as $site_hash_bucket) {
$site_hash = $site_hash_bucket
->getValue();
foreach ($site_hash_bucket
->getFacetSet()
->getFacet('indexes') as $index_bucket) {
$index = $index_bucket
->getValue();
if ($datsources_facet = $index_bucket
->getFacetSet()
->getFacet('dataSources')) {
foreach ($datsources_facet as $datasource_bucket) {
$datasource = $datasource_bucket
->getValue();
if ($maxVersionPerDataSource = $datasource_bucket
->getFacetSet()
->getFacet('maxVersionPerDataSource')) {
$document_versions[$site_hash][$index][$datasource] = $maxVersionPerDataSource
->getValue();
}
}
}
}
}
}
}
return $document_versions;
}
public function getDisabledFieldTypes() : array {
$this
->addDefaultConfigurationForConfigGeneration();
return $this->configuration['disabled_field_types'];
}
public function getDisabledCaches() : array {
$this
->addDefaultConfigurationForConfigGeneration();
return $this->configuration['disabled_caches'];
}
public function getDisabledRequestHandlers() : array {
$this
->addDefaultConfigurationForConfigGeneration();
return $this->configuration['disabled_request_handlers'];
}
public function getDisabledRequestDispatchers() : array {
$this
->addDefaultConfigurationForConfigGeneration();
return $this->configuration['disabled_request_dispatchers'];
}
public function isNonDrupalOrOutdatedConfigSetAllowed() : bool {
$connector = $this
->getSolrConnector();
$configuration = $connector
->getConfiguration();
return (bool) ($configuration['skip_schema_check'] ?? FALSE);
}
public function __sleep() {
$properties = array_flip(parent::__sleep());
unset($properties['solrConnector']);
if (isset($properties['eventDispatcher'])) {
unset($properties['eventDispatcher']);
}
return array_keys($properties);
}
public function __wakeup() {
parent::__wakeup();
if (!$this->eventDispatcher) {
$this->eventDispatcher = new Psr14Bridge(\Drupal::service('event_dispatcher'));
}
}
}