diff --git a/composer.json b/composer.json index 97007233277cd3e14e8060bd726e706f47b4fdb0..c3bfb4082f7c3039f04fc2d6905e846113225750 100644 --- a/composer.json +++ b/composer.json @@ -162,8 +162,8 @@ "drupal/redis": "1.0", "drupal/roleassign": "1.0.0-beta1", "drupal/scheduler": "1.3", - "drupal/search_api": "1.18", - "drupal/search_api_db": "1.18", + "drupal/search_api": "1.19", + "drupal/search_api_db": "1.19", "drupal/simple_gmap": "3.0", "drupal/simple_megamenu": "1.0-beta3", "drupal/simple_sitemap": "3.8", diff --git a/composer.lock b/composer.lock index 3f5bcca64b133e977bdca278117bcf288ceda350..84b2db9f87cf9e0a04147bf7f9d417ffaa546744 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cf9dbe7bb1771287f68ffec2f43f12ce", + "content-hash": "f6c9d59e64838eb70131caf7be60b0d1", "packages": [ { "name": "alchemy/zippy", @@ -7275,17 +7275,17 @@ }, { "name": "drupal/search_api", - "version": "1.18.0", + "version": "1.19.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/search_api.git", - "reference": "8.x-1.18" + "reference": "8.x-1.19" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/search_api-8.x-1.18.zip", - "reference": "8.x-1.18", - "shasum": "6cf1d6820ba55891e204bac40b6031ed15db482a" + "url": "https://ftp.drupal.org/files/projects/search_api-8.x-1.19.zip", + "reference": "8.x-1.19", + "shasum": "5654e9d02117e28c585d89a25ea3cc40d20c5019" }, "require": { "drupal/core": "^8.8 || ^9" @@ -7306,8 +7306,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-1.18", - "datestamp": "1605204423", + "version": "8.x-1.19", + "datestamp": "1612192040", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -7347,7 +7347,7 @@ }, { "name": "drupal/search_api_db", - "version": "1.18.0", + "version": "1.19.0", "require": { "drupal/core": "^8.8 || ^9", "drupal/search_api": "*" @@ -7355,8 +7355,8 @@ "type": "metapackage", "extra": { "drupal": { - "version": "8.x-1.18", - "datestamp": "1605204423", + "version": "8.x-1.19", + "datestamp": "1612192040", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index f42a9ad2d804fd18186a8fe5567ba185e91b3695..7c7d590d761c56d020d209bf598286b6074609c2 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -7499,18 +7499,18 @@ }, { "name": "drupal/search_api", - "version": "1.18.0", - "version_normalized": "1.18.0.0", + "version": "1.19.0", + "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/search_api.git", - "reference": "8.x-1.18" + "reference": "8.x-1.19" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/search_api-8.x-1.18.zip", - "reference": "8.x-1.18", - "shasum": "6cf1d6820ba55891e204bac40b6031ed15db482a" + "url": "https://ftp.drupal.org/files/projects/search_api-8.x-1.19.zip", + "reference": "8.x-1.19", + "shasum": "5654e9d02117e28c585d89a25ea3cc40d20c5019" }, "require": { "drupal/core": "^8.8 || ^9" @@ -7531,8 +7531,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-1.18", - "datestamp": "1605204423", + "version": "8.x-1.19", + "datestamp": "1612192040", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -7573,8 +7573,8 @@ }, { "name": "drupal/search_api_db", - "version": "1.18.0", - "version_normalized": "1.18.0.0", + "version": "1.19.0", + "version_normalized": "1.19.0.0", "require": { "drupal/core": "^8.8 || ^9", "drupal/search_api": "*" @@ -7582,8 +7582,8 @@ "type": "metapackage", "extra": { "drupal": { - "version": "8.x-1.18", - "datestamp": "1605204423", + "version": "8.x-1.19", + "datestamp": "1612192040", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" diff --git a/web/modules/search_api/CHANGELOG.txt b/web/modules/search_api/CHANGELOG.txt index d4b5c72b49f0df2f2c59a7b9f7f9f6efc45d9a1c..55608ea088571f192614480464d581e4a2d5e04b 100644 --- a/web/modules/search_api/CHANGELOG.txt +++ b/web/modules/search_api/CHANGELOG.txt @@ -1,3 +1,31 @@ +Search API 1.19 (2021-02-01): +----------------------------- +- #3182306 by drunken monkey: Improved output from the "rebuild tracker" batch + job. +- #3111383 by liquidcms, drunken monkey: Fixed problems with exposed, grouped + Views date filters. +- #3127099 by Grimreaper, drunken monkey: Added option to expose searched + fields in Views fulltext filter. +- #2944371 by Upchuk, jasonschweb, drunken monkey: Fixed random sort for DB + backend. +- Issue #3194016 by mkalkbrenner, borisson_: Fixed PHP 8 compatibility. +- #3181827 by brunodbo, drunken monkey: Fixed wrong @var type doc in + ContentEntityTrackingManager. +- #3179045 by sebish, joelpittet, drunken monkey: Fixed parameter type hint in + ContentEntityTrackingManager. +- #3178417 by SivaprasadC, drunken monkey: Fixed two typos in the Database + backend plugin class. +- #3174778 by drunken monkey: Fixed some encoding errors. +- #3178941 by drunken monkey, cspitzlay, MegaChriz, ooziedie, bakulahluwalia: + Fixed fatal error when saving entities with certain setups. +- #3036504 by kfritsche, drunken monkey: Fixed memory issues during indexing + via Drush. +- #3136277 by drunken monkey, pfrenssen, Berdir, dwinters, calmforce: Fixed + issue with modifying condition group via pre-execute event/hook. +- #3174657 by drunken monkey: Fixed "keep facets" feature for search views when + facet source wasn't saved. +- #3181936 by drunken monkey: Fixed tests on Drupal 9.1.x HEAD. + Search API 1.18 (2020-10-22): ----------------------------- - #3153153 by mkalkbrenner, cristiroma, drunken monkey: Fixed serialization of diff --git a/web/modules/search_api/config/schema/search_api.views.schema.yml b/web/modules/search_api/config/schema/search_api.views.schema.yml index 55de809f20134ffe94a5e6297b5aa1d360d6c08e..94511cb8522ee42d9cf2772cb07a7b3ccc53cd42 100644 --- a/web/modules/search_api/config/schema/search_api.views.schema.yml +++ b/web/modules/search_api/config/schema/search_api.views.schema.yml @@ -256,9 +256,15 @@ views.filter.search_api_fulltext: type: mapping label: 'Exposed' mapping: + expose_fields: + type: boolean + label: 'Expose the list of searched fields' placeholder: type: label label: 'Placeholder' + searched_fields_id: + type: string + label: 'Searched fields identifier' views.filter.search_api_language: type: views.filter.language diff --git a/web/modules/search_api/modules/search_api_db/search_api_db.info.yml b/web/modules/search_api/modules/search_api_db/search_api_db.info.yml index 4141fde16c0dac18b4491fa4af96f816813ccd1d..dd719947e80b24a6334374deb1bbc9f902171a48 100644 --- a/web/modules/search_api/modules/search_api_db/search_api_db.info.yml +++ b/web/modules/search_api/modules/search_api_db/search_api_db.info.yml @@ -6,7 +6,7 @@ core_version_requirement: ^8.8 || ^9 dependencies: - search_api:search_api -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/modules/search_api_db/search_api_db_defaults/search_api_db_defaults.info.yml b/web/modules/search_api/modules/search_api_db/search_api_db_defaults/search_api_db_defaults.info.yml index 86b46a70d3fe9f138c35fa3bdb430d60829a63f8..7164db811cd4c406d79986810fd8295ec3b47a95 100644 --- a/web/modules/search_api/modules/search_api_db/search_api_db_defaults/search_api_db_defaults.info.yml +++ b/web/modules/search_api/modules/search_api_db/search_api_db_defaults/search_api_db_defaults.info.yml @@ -13,7 +13,7 @@ dependencies: - drupal:views - search_api:search_api_db -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/DatabaseCompatibilityHandlerInterface.php b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/DatabaseCompatibilityHandlerInterface.php index d96431d5a924ea1f1fda08c6522ed7e1d04acc72..e13211caa08d0103114006b583c26de51a4046c5 100644 --- a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/DatabaseCompatibilityHandlerInterface.php +++ b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/DatabaseCompatibilityHandlerInterface.php @@ -3,6 +3,7 @@ namespace Drupal\search_api_db\DatabaseCompatibility; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\Query\SelectInterface; /** * Bundles methods for resolving DBMS-specific differences. @@ -70,4 +71,12 @@ public function alterNewTable($table, $type = 'text'); */ public function preprocessIndexValue($value, $type = 'text'); + /** + * Applies a random sort to the query. + * + * @param \Drupal\Core\Database\Query\SelectInterface $query + * The search query. + */ + public function orderByRandom(SelectInterface $query); + } diff --git a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/GenericDatabase.php b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/GenericDatabase.php index 5db84b2f9f095c85deb837a0c97ef8c1fdbce7d0..b083899f87bc4f0d2ad123602f85d23b8256e13b 100644 --- a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/GenericDatabase.php +++ b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/GenericDatabase.php @@ -4,6 +4,7 @@ use Drupal\Component\Transliteration\TransliterationInterface; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\Query\SelectInterface; /** * Represents any database for which no specifics are known. @@ -68,4 +69,12 @@ public function preprocessIndexValue($value, $type = 'text') { return mb_strtolower($this->transliterator->transliterate($value)); } + /** + * {@inheritdoc} + */ + public function orderByRandom(SelectInterface $query) { + $alias = $query->addExpression('random()', 'random_order_field'); + $query->orderBy($alias); + } + } diff --git a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/MySql.php b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/MySql.php index 08112f206deacd3442619e389929d66525736067..8ce1a069988cccda6bb6b5b629876db1be175d93 100644 --- a/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/MySql.php +++ b/web/modules/search_api/modules/search_api_db/src/DatabaseCompatibility/MySql.php @@ -3,6 +3,7 @@ namespace Drupal\search_api_db\DatabaseCompatibility; use Drupal\Core\Database\DatabaseException; +use Drupal\Core\Database\Query\SelectInterface; use Drupal\search_api\SearchApiException; /** @@ -36,4 +37,12 @@ public function alterNewTable($table, $type = 'text') { } } + /** + * {@inheritdoc} + */ + public function orderByRandom(SelectInterface $query) { + $alias = $query->addExpression('rand()', 'random_order_field'); + $query->orderBy($alias); + } + } diff --git a/web/modules/search_api/modules/search_api_db/src/Plugin/search_api/backend/Database.php b/web/modules/search_api/modules/search_api_db/src/Plugin/search_api/backend/Database.php index be0254cdbd1b9e58a24f3743d08554e9d1130b07..bbc13bacfbc77f08095bac97cfd998b9ccce50e0 100644 --- a/web/modules/search_api/modules/search_api_db/src/Plugin/search_api/backend/Database.php +++ b/web/modules/search_api/modules/search_api_db/src/Plugin/search_api/backend/Database.php @@ -591,6 +591,7 @@ public function getSupportedFeatures() { 'search_api_autocomplete', 'search_api_facets', 'search_api_facets_operator_or', + 'search_api_random_sort', ]; } @@ -850,7 +851,7 @@ protected function createFieldTable(FieldInterface $field = NULL, array $db = [] // index with the same as the first table, which conflicts in SQLite. // // The core issue addressing this (https://www.drupal.org/node/1008128) was - // closed as it fixed the PostgresSQL part. The SQLite fix is added in + // closed as it fixed the PostgreSQL part. The SQLite fix is added in // https://www.drupal.org/node/2625664 // We prevent this by adding an extra underscore (which is also the proposed // solution in the original core issue). @@ -2356,7 +2357,7 @@ protected function getTableAlias(array $field, SelectInterface $db_query, $new_j protected function preQuery(SelectInterface &$db_query, QueryInterface $query) {} /** - * Adds the approiate "ORDER BY" statements to a search database query. + * Adds the appropriate "ORDER BY" statements to a search database query. * * @param \Drupal\search_api\Query\QueryInterface $query * The search query whose sorts should be applied. @@ -2387,6 +2388,11 @@ protected function setQuerySort(QueryInterface $query, SelectInterface $db_query continue; } + if ($field_name == 'search_api_random') { + $this->dbmsCompatibility->orderByRandom($db_query); + continue; + } + if (!isset($fields[$field_name])) { throw new SearchApiException("Trying to sort on unknown field '$field_name'."); } diff --git a/web/modules/search_api/modules/search_api_db/tests/search_api_db_test_autocomplete/search_api_db_test_autocomplete.info.yml b/web/modules/search_api/modules/search_api_db/tests/search_api_db_test_autocomplete/search_api_db_test_autocomplete.info.yml index 41ebc400d4cb772751204d2b3b94b8258a1dc61b..5a1670123a911c2bfef935b33fbb35b5b1f3f070 100644 --- a/web/modules/search_api/modules/search_api_db/tests/search_api_db_test_autocomplete/search_api_db_test_autocomplete.info.yml +++ b/web/modules/search_api/modules/search_api_db/tests/search_api_db_test_autocomplete/search_api_db_test_autocomplete.info.yml @@ -8,7 +8,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/modules/search_api_db/tests/src/Kernel/BackendTest.php b/web/modules/search_api/modules/search_api_db/tests/src/Kernel/BackendTest.php index 0d47c6e2a9d9f7c59eb740dac26d5be716921ed5..cbbc19e9b9e46a8a29cedd399bacd0c111efeb9f 100644 --- a/web/modules/search_api/modules/search_api_db/tests/src/Kernel/BackendTest.php +++ b/web/modules/search_api/modules/search_api_db/tests/src/Kernel/BackendTest.php @@ -99,6 +99,7 @@ public function setUp() { */ protected function checkBackendSpecificFeatures() { $this->checkMultiValuedInfo(); + $this->searchWithRandom(); $this->setServerMatchMode(); $this->searchSuccessPartial(); $this->setServerMatchMode('prefix'); @@ -259,6 +260,36 @@ protected function setServerMatchMode($match_mode = 'partial') { $this->resetEntityCache(); } + /** + * Tests whether random searches work. + */ + protected function searchWithRandom() { + // Run the query 5 times, using random sorting as the first sort and verify + // that the results are not always the same. + $first_result = NULL; + $second_result = NULL; + for ($i = 1; $i <= 5; $i++) { + $results = $this->buildSearch('foo', [], NULL, FALSE) + ->sort('search_api_random') + ->sort('id') + ->execute(); + + $result_ids = array_keys($results->getResultItems()); + if ($first_result === NULL) { + $first_result = $second_result = $result_ids; + } + elseif ($result_ids !== $first_result) { + $second_result = $result_ids; + } + + // Make sure the search still returned the expected items. + $this->assertCount(4, $result_ids); + sort($result_ids); + $this->assertEquals($this->getItemIds([1, 2, 4, 5]), $result_ids); + } + $this->assertNotEquals($first_result, $second_result); + } + /** * Tests whether partial searches work. */ diff --git a/web/modules/search_api/search_api.info.yml b/web/modules/search_api/search_api.info.yml index 0b7ad670ee6a94b7100772df533b871bd891388f..9c0c3ebef770359ab7b3f2a71089023f5f30c5a1 100644 --- a/web/modules/search_api/search_api.info.yml +++ b/web/modules/search_api/search_api.info.yml @@ -5,7 +5,7 @@ package: Search core_version_requirement: ^8.8 || ^9 configure: search_api.overview -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/search_api.module b/web/modules/search_api/search_api.module index e10bc0bf4695bd2d07dd1ab3265e7806c160f8e3..c68c16f0dc7806c124d497469122ab2fc9258678 100644 --- a/web/modules/search_api/search_api.module +++ b/web/modules/search_api/search_api.module @@ -576,19 +576,23 @@ function search_api_form_views_exposed_form_alter(&$form, FormStateInterface $fo && $query_plugin instanceof SearchApiQuery && \Drupal::moduleHandler()->moduleExists('facets'); if ($preserve_facets) { - // Retrieve the facet source. + $filter_key = 'f'; + + // Attempt to retrieve the facet source to use the actual facets filter + // parameter as configured by the admin. (Facet source config entities are + // not always actually saved in the storage, if the admin didn't change + // their settings.) $query = $query_plugin->getSearchApiQuery(); $display_id = $query->getSearchId(FALSE); $facet_source_id = str_replace(':', '__', 'search_api:' . $display_id); $facet_source = \Drupal::entityTypeManager() ->getStorage('facets_facet_source') ->load($facet_source_id); - if (!$facet_source) { - return; + if ($facet_source) { + $filter_key = $facet_source->getFilterKey() ?: 'f'; } // Get the active facet filters from the query parameters. - $filter_key = $facet_source->getFilterKey() ?: 'f'; $filters = \Drupal::request()->query->get($filter_key, []); // Do not iterate over facet filters if the parameter is not an array. diff --git a/web/modules/search_api/src/Entity/Index.php b/web/modules/search_api/src/Entity/Index.php index 221b3fb904dc746ff49d93efb6f50c01dfb7aa8f..090131de722526a908c3e77db56441c3c222ccb7 100644 --- a/web/modules/search_api/src/Entity/Index.php +++ b/web/modules/search_api/src/Entity/Index.php @@ -1008,7 +1008,6 @@ public function indexSpecificItems(array $search_objects) { $description = 'This hook is deprecated in search_api:8.x-1.14 and is removed from search_api:2.0.0. Please use the "search_api.items_indexed" event instead. See https://www.drupal.org/node/3059866'; \Drupal::moduleHandler()->invokeAllDeprecated($description, 'search_api_items_indexed', [$this, $processed_ids]); - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); $dispatcher->dispatch(SearchApiEvents::ITEMS_INDEXED, new ItemsIndexedEvent($this, $processed_ids)); @@ -1016,6 +1015,13 @@ public function indexSpecificItems(array $search_objects) { Cache::invalidateTags(['search_api_list:' . $this->id]); } + // When indexing via Drush, multiple iterations of a batch will happen in + // the same PHP process, so the static cache will quickly fill up. To + // prevent this, clear it after each batch of items gets indexed. + if (function_exists('drush_backend_batch_process') && batch_get()) { + \Drupal::getContainer()->get('entity.memory_cache')->deleteAll(); + } + return $processed_ids; } diff --git a/web/modules/search_api/src/Event/MappingForeignRelationshipsEvent.php b/web/modules/search_api/src/Event/MappingForeignRelationshipsEvent.php index d700ac6c016b182f2dce8f8208353319626b1203..17216973ae992febdac7e49f523fd65147dc1218 100644 --- a/web/modules/search_api/src/Event/MappingForeignRelationshipsEvent.php +++ b/web/modules/search_api/src/Event/MappingForeignRelationshipsEvent.php @@ -67,6 +67,8 @@ public function getIndex(): IndexInterface { * A (numerically keyed) array of foreign relationship mappings. Each * sub-array here represents a single known relationship. Such sub-arrays * will have the following structure: + * - datasource: (string) The ID of the datasource which contains this + * relationship. * - entity_type: (string) Entity type that is referred to from the index. * - bundles: (array) Optional array of particular entity bundles that are * referred to from the index. Empty array here means index refers to diff --git a/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntity.php b/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntity.php index 06cceafc427bdbaf07f6df9ec4ab6252698c3306..deb86a82c67b0760d9df0f9b28dab53a2859c4c7 100644 --- a/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntity.php +++ b/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntity.php @@ -18,6 +18,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Logger\RfcLogLevel; use Drupal\Core\Plugin\PluginFormInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\State\StateInterface; @@ -28,8 +29,9 @@ use Drupal\field\FieldStorageConfigInterface; use Drupal\search_api\Datasource\DatasourcePluginBase; use Drupal\search_api\IndexInterface; -use Drupal\search_api\Utility\Dependencies; +use Drupal\search_api\LoggerTrait; use Drupal\search_api\Plugin\PluginFormTrait; +use Drupal\search_api\Utility\Dependencies; use Drupal\search_api\Utility\FieldsHelperInterface; use Drupal\search_api\Utility\Utility; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -44,6 +46,7 @@ */ class ContentEntity extends DatasourcePluginBase implements PluginFormInterface { + use LoggerTrait; use PluginFormTrait; /** @@ -157,6 +160,7 @@ public static function create(ContainerInterface $container, array $configuratio $datasource->setFieldsHelper($container->get('search_api.fields_helper')); $datasource->setState($container->get('state')); $datasource->setEntityMemoryCache($container->get('entity.memory_cache')); + $datasource->setLogger($container->get('logger.channel.search_api')); return $datasource; } @@ -1039,6 +1043,11 @@ public function getAffectedItemsForEntityChange(EntityInterface $entity, array $ $ids_to_reindex = []; $path_separator = IndexInterface::PROPERTY_PATH_SEPARATOR; foreach ($foreign_entity_relationship_map as $relation_info) { + // Ignore relationships belonging to other datasources. + if (!empty($relation_info['datasource']) + && $relation_info['datasource'] !== $this->getPluginId()) { + continue; + } // Check whether entity type and (if specified) bundles match the entity. if ($relation_info['entity_type'] !== $entity->getEntityTypeId()) { continue; @@ -1073,7 +1082,29 @@ public function getAffectedItemsForEntityChange(EntityInterface $entity, array $ try { $entity_ids = array_values($query->execute()); } - catch (\Exception $e) { + // @todo Switch back to \Exception once Core bug #2893747 is fixed. + catch (\Throwable $e) { + // We don't want to catch all PHP \Error objects thrown, but just the + // ones caused by #2893747. + if (!($e instanceof \Exception) + && (get_class($e) !== \Error::class || $e->getMessage() !== 'Call to a member function getColumns() on bool')) { + throw $e; + } + $vars = [ + '%index' => $this->index->label(), + '%entity_type' => $entity->getEntityType()->getLabel(), + '@entity_id' => $entity->id(), + ]; + try { + $link = $entity->toLink($this->t('Go to changed %entity_type with ID "@entity_id"', $vars)) + ->toString()->getGeneratedLink(); + } + catch (\Throwable $e) { + // Ignore any errors here, it's not that important that the log + // message contains a link. + $link = NULL; + } + $this->logException($e, '%type while attempting to find indexed entities referencing changed %entity_type with ID "@entity_id" for index %index: @message in %function (line %line of %file).', $vars, RfcLogLevel::ERROR, $link); continue; } foreach ($entity_ids as $entity_id) { diff --git a/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php b/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php index d918a8ab973c176ec403cd9b264692d9ff8bd830..551d211652e93fdc34855063f22bbd348cd7c2bc 100644 --- a/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php +++ b/web/modules/search_api/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php @@ -8,7 +8,7 @@ use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\search_api\IndexInterface; @@ -26,7 +26,7 @@ class ContentEntityTrackingManager { /** * The entity type manager. * - * @var \Drupal\Core\Entity\EntityTypeManager + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; @@ -47,14 +47,14 @@ class ContentEntityTrackingManager { /** * Constructs a new class instance. * - * @param \Drupal\Core\Entity\EntityTypeManager $entityTypeManager + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * The entity type manager. * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager * The language manager. * @param \Drupal\search_api\Task\TaskManagerInterface $taskManager * The task manager. */ - public function __construct(EntityTypeManager $entityTypeManager, LanguageManagerInterface $languageManager, TaskManagerInterface $taskManager) { + public function __construct(EntityTypeManagerInterface $entityTypeManager, LanguageManagerInterface $languageManager, TaskManagerInterface $taskManager) { $this->entityTypeManager = $entityTypeManager; $this->languageManager = $languageManager; $this->taskManager = $taskManager; @@ -366,7 +366,7 @@ public function indexUpdate(IndexInterface $index) { * Filters a set of datasource-specific item IDs. * * Returns only those item IDs that are valid for the given datasource and - * index. This method only checks the item language, though – whether an + * index. This method only checks the item language, though – whether an * entity with that ID actually exists, or whether it has a bundle included * for that datasource, is not verified. * diff --git a/web/modules/search_api/src/Plugin/search_api/processor/Highlight.php b/web/modules/search_api/src/Plugin/search_api/processor/Highlight.php index 975495616342c93ec6e6c1139c3a9d90ce1338ca..b13e805a6a60865b2a9f561f5ca23a80e7e8ef05 100644 --- a/web/modules/search_api/src/Plugin/search_api/processor/Highlight.php +++ b/web/modules/search_api/src/Plugin/search_api/processor/Highlight.php @@ -280,7 +280,7 @@ protected function addExcerpts(array $results, array $fulltext_fields, array $ke // We call array_merge() using call_user_func_array() to prevent having to // use it in a loop because it is a resource greedy construction. // @see https://github.com/kalessil/phpinspectionsea/blob/master/docs/performance.md#slow-array-function-used-in-loop - $text = call_user_func_array('array_merge', $item); + $text = call_user_func_array('array_merge', array_values($item)); $item_keys = $keys; // If the backend already did highlighting and told us the exact keys it diff --git a/web/modules/search_api/src/Plugin/views/cache/SearchApiTagCache.php b/web/modules/search_api/src/Plugin/views/cache/SearchApiTagCache.php index 9527c82a117da22e073f32c1b223ad6396488f32..906d6c86c2ce91ef28da2685d36b6974d8e71f4f 100644 --- a/web/modules/search_api/src/Plugin/views/cache/SearchApiTagCache.php +++ b/web/modules/search_api/src/Plugin/views/cache/SearchApiTagCache.php @@ -3,6 +3,7 @@ namespace Drupal\search_api\Plugin\views\cache; use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\views\Plugin\views\cache\Tag; @@ -115,4 +116,15 @@ public function getRowCacheTags(ResultRow $row) { return $tags; } + /** + * {@inheritdoc} + */ + public function alterCacheMetadata(CacheableMetadata $cache_metadata) { + // Allow modules that alter the query to add their cache metadata to the + // view. + $query = $this->getQuery()->getSearchApiQuery(); + $query->preExecute(); + $cache_metadata->addCacheableDependency($query); + } + } diff --git a/web/modules/search_api/src/Plugin/views/filter/SearchApiDate.php b/web/modules/search_api/src/Plugin/views/filter/SearchApiDate.php index 494f965ed1099e36b4f8ec940deee840276f0940..dc8482c6390dabac8b0ebe771cf939af08db90ff 100644 --- a/web/modules/search_api/src/Plugin/views/filter/SearchApiDate.php +++ b/web/modules/search_api/src/Plugin/views/filter/SearchApiDate.php @@ -63,21 +63,6 @@ public function acceptExposedInput($input) { return TRUE; } - // Unfortunately, this is necessary due to a bug in our parent filter. See - // #2704077. - if (!empty($this->options['expose']['identifier'])) { - $value = &$input[$this->options['expose']['identifier']]; - if (!is_array($value)) { - $value = [ - 'value' => $value, - ]; - } - $value += [ - 'min' => '', - 'max' => '', - ]; - } - $return = parent::acceptExposedInput($input); if (!$return) { diff --git a/web/modules/search_api/src/Plugin/views/filter/SearchApiFulltext.php b/web/modules/search_api/src/Plugin/views/filter/SearchApiFulltext.php index df5f705e75be0773cc9641d406fe16d3fc693fc4..f0c15bea36964dfb0221ed0bdb1e305d83cc6626 100644 --- a/web/modules/search_api/src/Plugin/views/filter/SearchApiFulltext.php +++ b/web/modules/search_api/src/Plugin/views/filter/SearchApiFulltext.php @@ -19,6 +19,13 @@ class SearchApiFulltext extends FilterPluginBase { use SearchApiFilterTrait; + /** + * The list of fields selected for the search. + * + * @var array + */ + public $searchedFields = []; + /** * The parse mode manager. * @@ -126,10 +133,21 @@ public function defineOptions() { $options['min_length'] = ['default' => '']; $options['fields'] = ['default' => []]; $options['expose']['contains']['placeholder'] = ['default' => '']; + $options['expose']['contains']['expose_fields'] = ['default' => FALSE]; + $options['expose']['contains']['searched_fields_id'] = ['default' => '']; return $options; } + /** + * {@inheritdoc} + */ + public function defaultExposeOptions() { + parent::defaultExposeOptions(); + + $this->options['expose']['searched_fields_id'] = $this->options['id'] . '_searched_fields'; + } + /** * {@inheritdoc} */ @@ -203,6 +221,58 @@ public function buildExposeForm(&$form, FormStateInterface $form_state) { '#size' => 40, '#description' => $this->t('Hint text that appears inside the field when empty.'), ]; + + $form['expose']['expose_fields'] = [ + '#type' => 'checkbox', + '#default_value' => $this->options['expose']['expose_fields'], + '#title' => $this->t('Expose searched fields'), + '#description' => $this->t('Expose the list of searched fields. This allows users to narrow the search to the desired fields.'), + ]; + $form['expose']['searched_fields_id'] = [ + '#type' => 'textfield', + '#default_value' => $this->options['expose']['searched_fields_id'], + '#title' => $this->t('Searched fields identifier'), + '#size' => 40, + '#description' => $this->t('This will appear in the URL after the ? to identify this searched fields form element.'), + '#states' => [ + 'visible' => [ + ':input[name="options[expose][expose_fields]"]' => ['checked' => TRUE], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildExposedForm(&$form, FormStateInterface $form_state) { + parent::buildExposedForm($form, $form_state); + + if (empty($this->options['exposed'])) { + return; + } + + if ($this->options['expose']['expose_fields']) { + $fields = $this->getFulltextFields(); + $configured_fields = $this->options['fields']; + // Only keep the configured fields. + if (!empty($configured_fields)) { + $configured_fields = array_flip($configured_fields); + $fields = array_intersect_key($fields, $configured_fields); + } + + $searched_fields_identifier = $this->options['id'] . '_searched_fields'; + if (!empty($this->options['expose']['searched_fields_id'])) { + $searched_fields_identifier = $this->options['expose']['searched_fields_id']; + } + $form[$searched_fields_identifier] = [ + '#type' => 'select', + '#title' => $this->t('Search fields'), + '#options' => $fields, + '#multiple' => TRUE, + '#size' => min(count($fields), 5), + ]; + } } /** @@ -243,6 +313,13 @@ public function validateExposed(&$form, FormStateInterface $form_state) { return; } + // Store searched fields. + $searched_fields_identifier = $this->options['id'] . '_searched_fields'; + if (!empty($this->options['expose']['searched_fields_id'])) { + $searched_fields_identifier = $this->options['expose']['searched_fields_id']; + } + $this->searchedFields = $form_state->getValue($searched_fields_identifier, []); + $identifier = $this->options['expose']['identifier']; $input = &$form_state->getValue($identifier, ''); @@ -299,6 +376,10 @@ public function query() { } $fields = $this->options['fields']; $fields = $fields ?: array_keys($this->getFulltextFields()); + // Override the search fields, if exposed. + if (!empty($this->searchedFields)) { + $fields = array_intersect($fields, $this->searchedFields); + } $query = $this->getQuery(); // Save any keywords that were already set. diff --git a/web/modules/search_api/src/Query/Query.php b/web/modules/search_api/src/Query/Query.php index f6b419ae81ab58947e48d87e3ab1f06da984d124..3049a1a0509ddd21aecaff2bfcdfeec687b2e7a0 100644 --- a/web/modules/search_api/src/Query/Query.php +++ b/web/modules/search_api/src/Query/Query.php @@ -756,10 +756,6 @@ public function getOriginalQuery() { * {@inheritdoc} */ public function getCacheContexts() { - // Call the pre-execute method to ensure that processors and modules have - // had the chance to alter the query and modify the cacheability metadata. - $this->preExecute(); - $contexts = $this->cacheContexts; foreach ($this->getIndex()->getDatasources() as $datasource) { @@ -773,10 +769,6 @@ public function getCacheContexts() { * {@inheritdoc} */ public function getCacheTags() { - // Call the pre-execute method to ensure that processors and modules have - // had the chance to alter the query and modify the cacheability metadata. - $this->preExecute(); - $tags = $this->cacheTags; // If the configuration of the search index changes we should invalidate the @@ -791,10 +783,6 @@ public function getCacheTags() { * {@inheritdoc} */ public function getCacheMaxAge() { - // Call the pre-execute method to ensure that processors and modules have - // had the chance to alter the query and modify the cacheability metadata. - $this->preExecute(); - return $this->cacheMaxAge; } diff --git a/web/modules/search_api/src/Task/TaskManager.php b/web/modules/search_api/src/Task/TaskManager.php index 78e80f107f5ec893a069a9a8bcae80f46004e0ba..a2595ee208f92716bbe443571a7f5e1c8be6608d 100644 --- a/web/modules/search_api/src/Task/TaskManager.php +++ b/web/modules/search_api/src/Task/TaskManager.php @@ -352,11 +352,13 @@ public function processBatch(array $task_ids, array $conditions, &$context) { $pending = $this->getTasksCount($conditions); $context['finished'] = 1 - $pending / $context['results']['total']; $executed = $context['results']['total'] - $pending; - $context['message'] = $this->formatPlural( - $executed, - 'Successfully executed @count pending task.', - 'Successfully executed @count pending tasks.' - ); + if ($executed > 0) { + $context['message'] = $this->formatPlural( + $executed, + 'Successfully executed @count pending task.', + 'Successfully executed @count pending tasks.' + ); + } } /** diff --git a/web/modules/search_api/src/Utility/TrackingHelper.php b/web/modules/search_api/src/Utility/TrackingHelper.php index 9d9ac87c4a69cd60676c3e958b33c8f5ae4402cb..088211ac0e92352e4714bd5a211a6ecfcf904907 100644 --- a/web/modules/search_api/src/Utility/TrackingHelper.php +++ b/web/modules/search_api/src/Utility/TrackingHelper.php @@ -102,10 +102,11 @@ public function trackReferencedEntityUpdate(EntityInterface $entity, bool $delet // Can't really happen, but play it safe to appease static code analysis. } - // Map of foreign entity relations. Will get lazily populated as soon as we - // actually need it. - $original = $deleted ? NULL : $entity->original ?? NULL; + // Original entity, if available. + $original = $deleted ? NULL : ($entity->original ?? NULL); foreach ($indexes as $index) { + // Map of foreign entity relations. Will get lazily populated as soon as + // we actually need it. $map = NULL; foreach ($index->getDatasources() as $datasource_id => $datasource) { if (!$datasource->canContainEntityReferences()) { @@ -142,6 +143,8 @@ public function trackReferencedEntityUpdate(EntityInterface $entity, bool $delet * A (numerically keyed) array of foreign relationship mappings. Each * sub-array represents a single known relationship. Such sub-arrays will * have the following structure: + * - datasource: (string) The ID of the datasource which contains this + * relationship. * - entity_type: (string) The entity type that is referenced from the * index. * - bundles: (string[]) An optional array of particular entity bundles that @@ -175,6 +178,7 @@ protected function getForeignEntityRelationsMap(IndexInterface $index): array { } $relation_info = [ + 'datasource' => $datasource->getPluginId(), 'entity_type' => NULL, 'bundles' => NULL, 'property_path_to_foreign_entity' => NULL, @@ -216,6 +220,7 @@ protected function getForeignEntityRelationsMap(IndexInterface $index): array { && $relation_info['entity_type'] !== $entity_reference['entity_type']) { $relation_info = $entity_reference; $relation_info['property_path_to_foreign_entity'] = implode(IndexInterface::PROPERTY_PATH_SEPARATOR, $seen_path_chunks); + $relation_info['datasource'] = $datasource->getPluginId(); } if ($property_definition instanceof ComplexDataDefinitionInterface) { diff --git a/web/modules/search_api/tests/search_api_test/search_api_test.info.yml b/web/modules/search_api/tests/search_api_test/search_api_test.info.yml index 08921e779d5e89065f05a77affd86cff43ffcf43..2335bffafab53899da388a396be4cb13ca1a3c7d 100644 --- a/web/modules/search_api/tests/search_api_test/search_api_test.info.yml +++ b/web/modules/search_api/tests/search_api_test/search_api_test.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_bulk_form/search_api_test_bulk_form.info.yml b/web/modules/search_api/tests/search_api_test_bulk_form/search_api_test_bulk_form.info.yml index cdc5011309df62dba5e8644af80308d380189db6..c1e2e3d925062d21772248829d7bcbbe388e52ac 100644 --- a/web/modules/search_api/tests/search_api_test_bulk_form/search_api_test_bulk_form.info.yml +++ b/web/modules/search_api/tests/search_api_test_bulk_form/search_api_test_bulk_form.info.yml @@ -10,7 +10,7 @@ dependencies: core_version_requirement: ^8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_db/search_api_test_db.info.yml b/web/modules/search_api/tests/search_api_test_db/search_api_test_db.info.yml index ad96200c3bc0335e80d9eb313c765e241fdc2f29..b859571a7433e7041ec4788f860247ff7fdae721 100644 --- a/web/modules/search_api/tests/search_api_test_db/search_api_test_db.info.yml +++ b/web/modules/search_api/tests/search_api_test_db/search_api_test_db.info.yml @@ -8,7 +8,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_events/search_api_test_events.info.yml b/web/modules/search_api/tests/search_api_test_events/search_api_test_events.info.yml index 2528bdc72a47f341c1ea521bd6ee11c9e2ddfad0..88accc5a7103cfeeb9267d84a989b7f9335f399c 100644 --- a/web/modules/search_api/tests/search_api_test_events/search_api_test_events.info.yml +++ b/web/modules/search_api/tests/search_api_test_events/search_api_test_events.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_example_content/search_api_test_example_content.info.yml b/web/modules/search_api/tests/search_api_test_example_content/search_api_test_example_content.info.yml index 4b5356a2283901d9839fefe6ee0c4ba7d0ab8c1c..15cd563884e79c9fc8494f13f23d350400ddf9e3 100644 --- a/web/modules/search_api/tests/search_api_test_example_content/search_api_test_example_content.info.yml +++ b/web/modules/search_api/tests/search_api_test_example_content/search_api_test_example_content.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_example_content_references/search_api_test_example_content_references.info.yml b/web/modules/search_api/tests/search_api_test_example_content_references/search_api_test_example_content_references.info.yml index e87cf643630158a53b653e8541578c7713f22cf4..6e096ca44afff79934c2d32fc6805de01d5b0a1d 100644 --- a/web/modules/search_api/tests/search_api_test_example_content_references/search_api_test_example_content_references.info.yml +++ b/web/modules/search_api/tests/search_api_test_example_content_references/search_api_test_example_content_references.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_excerpt_field/config/install/views.view.search_api_test_excerpt_field.yml b/web/modules/search_api/tests/search_api_test_excerpt_field/config/install/views.view.search_api_test_excerpt_field.yml index b19e1871c241e702076fed3bd450ae3ba7f11d38..03184ab3f901dbfafdc6031b0e4fc59bfabe3f79 100644 --- a/web/modules/search_api/tests/search_api_test_excerpt_field/config/install/views.view.search_api_test_excerpt_field.yml +++ b/web/modules/search_api/tests/search_api_test_excerpt_field/config/install/views.view.search_api_test_excerpt_field.yml @@ -57,8 +57,8 @@ display: offset: false offset_label: Offset tags: - previous: ‹‹ - next: ›› + previous: '«' + next: '»' style: type: default options: diff --git a/web/modules/search_api/tests/search_api_test_excerpt_field/search_api_test_excerpt_field.info.yml b/web/modules/search_api/tests/search_api_test_excerpt_field/search_api_test_excerpt_field.info.yml index 5a079c3ddee373ef38108b57014153d7212c721d..3830f96784054d8b1c1bf9f024927d8f8ecfb47d 100644 --- a/web/modules/search_api/tests/search_api_test_excerpt_field/search_api_test_excerpt_field.info.yml +++ b/web/modules/search_api/tests/search_api_test_excerpt_field/search_api_test_excerpt_field.info.yml @@ -9,7 +9,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_extraction/search_api_test_extraction.info.yml b/web/modules/search_api/tests/search_api_test_extraction/search_api_test_extraction.info.yml index e6386b6354fb851e3984f0c6a4a745911c3c7c34..af3c3baa60534806c70c663879de0bb93fa25d53 100644 --- a/web/modules/search_api/tests/search_api_test_extraction/search_api_test_extraction.info.yml +++ b/web/modules/search_api/tests/search_api_test_extraction/search_api_test_extraction.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_hooks/search_api_test_hooks.info.yml b/web/modules/search_api/tests/search_api_test_hooks/search_api_test_hooks.info.yml index cb9eb68b4e43bc42d67f46316414a5ad221f62fd..02bd3080f159db74edf3a8ef2b6d93d005774b90 100644 --- a/web/modules/search_api/tests/search_api_test_hooks/search_api_test_hooks.info.yml +++ b/web/modules/search_api/tests/search_api_test_hooks/search_api_test_hooks.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_inconsistent_config/search_api_test_inconsistent_config.info.yml b/web/modules/search_api/tests/search_api_test_inconsistent_config/search_api_test_inconsistent_config.info.yml index 3fe52b63a7ae81ab26b182c2543ce4d5524b5f58..6e946b778785bde08f46710d2344d7b8ec3952cf 100644 --- a/web/modules/search_api/tests/search_api_test_inconsistent_config/search_api_test_inconsistent_config.info.yml +++ b/web/modules/search_api/tests/search_api_test_inconsistent_config/search_api_test_inconsistent_config.info.yml @@ -8,7 +8,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_language_fallback/search_api_test_language_fallback.info.yml b/web/modules/search_api/tests/search_api_test_language_fallback/search_api_test_language_fallback.info.yml index 0daaefa1383b44f1a3d1993358931137067c6108..946a8b91f2d54389fce549acf3e648138a208f89 100644 --- a/web/modules/search_api/tests/search_api_test_language_fallback/search_api_test_language_fallback.info.yml +++ b/web/modules/search_api/tests/search_api_test_language_fallback/search_api_test_language_fallback.info.yml @@ -5,7 +5,7 @@ package: 'Search API' core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_no_ui/search_api_test_no_ui.info.yml b/web/modules/search_api/tests/search_api_test_no_ui/search_api_test_no_ui.info.yml index 26efc29333b9ef9c759192a63604018a3a201302..2d49a90e205e1f18e029fd67037460b1ad9ddd3c 100644 --- a/web/modules/search_api/tests/search_api_test_no_ui/search_api_test_no_ui.info.yml +++ b/web/modules/search_api/tests/search_api_test_no_ui/search_api_test_no_ui.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_node_indexing/search_api_test_node_indexing.info.yml b/web/modules/search_api/tests/search_api_test_node_indexing/search_api_test_node_indexing.info.yml index c26492cbec40f66efca433663f8d7f01f93b6c5b..1112004e2ddb4e3ada2229d1f2ac4b6509312df8 100644 --- a/web/modules/search_api/tests/search_api_test_node_indexing/search_api_test_node_indexing.info.yml +++ b/web/modules/search_api/tests/search_api_test_node_indexing/search_api_test_node_indexing.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_tasks/search_api_test_tasks.info.yml b/web/modules/search_api/tests/search_api_test_tasks/search_api_test_tasks.info.yml index 49d04312cefa41c16ab91449b0b524229757d46d..ca678d7ca61554f64d0871e24651f13ec61ae7f7 100644 --- a/web/modules/search_api/tests/search_api_test_tasks/search_api_test_tasks.info.yml +++ b/web/modules/search_api/tests/search_api_test_tasks/search_api_test_tasks.info.yml @@ -7,7 +7,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_block_view.yml b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_block_view.yml new file mode 100644 index 0000000000000000000000000000000000000000..cf6a8c1a753e3ca081a9270f4fdb6adc44cec9ff --- /dev/null +++ b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_block_view.yml @@ -0,0 +1,211 @@ +id: search_api_test_block_view +label: 'Search API Test Block View' +module: views +tag: '' +langcode: en +dependencies: + config: + - search_api.index.database_search_index + module: + - search_api +base_field: search_api_id +base_table: search_api_index_database_search_index +core: 8.x +description: '' +status: true +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: none + options: { } + query: + type: search_api_query + options: + bypass_access: false + skip_access: false + preserve_facet_query_args: false + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: none + options: { } + style: + type: default + row: + type: fields + fields: + title: + id: title + table: search_api_index_database_search_index + field: title + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + field_rendering: false + fallback_handler: search_api + fallback_options: + link_to_item: false + use_highlighting: false + multi_type: separator + multi_separator: ', ' + plugin_id: search_api_field + filters: + search_api_fulltext: + id: search_api_fulltext + table: search_api_index_database_search_index + field: search_api_fulltext + relationship: none + group_type: group + admin_label: '' + operator: and + value: TEST + group: 1 + exposed: true + expose: + operator_id: search_api_fulltext_op + label: 'Fulltext search' + description: '' + use_operator: false + operator: search_api_fulltext_op + operator_limit_selection: false + operator_list: { } + identifier: search_api_fulltext + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + parse_mode: terms + min_length: null + fields: { } + plugin_id: search_api_fulltext + sorts: { } + title: 'Search API Test Block View' + header: + result: + id: result + table: views + field: result + relationship: none + group_type: group + admin_label: '' + empty: true + content: 'Search API Test Block View: Found @total items' + plugin_id: result + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + use_ajax: false + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - 'user.node_grants:view' + tags: + - 'config:search_api.index.database_search_index' + block_1: + display_plugin: block + id: block_1 + display_title: Block + position: 1 + display_options: + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - 'user.node_grants:view' + tags: + - 'config:search_api.index.database_search_index' diff --git a/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_cache.yml b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_cache.yml index 55f598e7e312a911fda5f906c20e8ebe69c83e76..0f271a8926fd03db7a03c347072ad90e2724f9eb 100644 --- a/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_cache.yml +++ b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_cache.yml @@ -57,8 +57,8 @@ display: offset: false offset_label: Offset tags: - previous: ‹‹ - next: ›› + previous: '«' + next: '»' style: type: default row: diff --git a/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_view.yml b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_view.yml index 66169b4813cff6a6931e67087b91f7ba990a56fb..ac7186c5c1cdcd4c636d24fb7734f2005e043a2e 100644 --- a/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_view.yml +++ b/web/modules/search_api/tests/search_api_test_views/config/install/views.view.search_api_test_view.yml @@ -78,6 +78,7 @@ display: description: '' use_operator: true operator: search_api_fulltext_op + expose_fields: false identifier: search_api_fulltext required: false remember: false diff --git a/web/modules/search_api/tests/search_api_test_views/search_api_test_views.info.yml b/web/modules/search_api/tests/search_api_test_views/search_api_test_views.info.yml index 8f20a4018975f1dd21f2020aca7ce3e29a961e2e..ec6a4aa682cf54cafeb790cebab771b82ad5554f 100644 --- a/web/modules/search_api/tests/search_api_test_views/search_api_test_views.info.yml +++ b/web/modules/search_api/tests/search_api_test_views/search_api_test_views.info.yml @@ -11,7 +11,7 @@ dependencies: core_version_requirement: ^8.8 || ^9 hidden: true -# Information added by Drupal.org packaging script on 2020-10-22 -version: '8.x-1.18' +# Information added by Drupal.org packaging script on 2021-02-01 +version: '8.x-1.19' project: 'search_api' -datestamp: 1603359377 +datestamp: 1612192043 diff --git a/web/modules/search_api/tests/src/Functional/ViewsTest.php b/web/modules/search_api/tests/src/Functional/ViewsTest.php index f7f1bdb05a894ccceb8c62f88d1778e18b58aaca..ad1d7888fec4615fb9de977ee8131ec96d06478e 100644 --- a/web/modules/search_api/tests/src/Functional/ViewsTest.php +++ b/web/modules/search_api/tests/src/Functional/ViewsTest.php @@ -309,6 +309,9 @@ public function testSearchView() { $this->regressionTests(); + // Check special functionality that requires editing the view. + $this->checkExposedSearchFields(); + // Make sure there was a display plugin created for this view. /** @var \Drupal\search_api\Display\DisplayInterface[] $displays */ $displays = \Drupal::getContainer() @@ -373,6 +376,7 @@ public function testViewWithOperations() { protected function regressionTests() { $this->regressionTest2869121(); $this->regressionTest3031991(); + $this->regressionTest3136277(); } /** @@ -458,6 +462,59 @@ protected function regressionTest3031991() { $this->checkResults($query, [4], 'Search with multiple fulltext filters'); } + /** + * Tests that query preprocessing works correctly for block views. + * + * @see https://www.drupal.org/node/3136277 + */ + protected function regressionTest3136277() { + $block = $this->drupalPlaceBlock('views_block:search_api_test_block_view-block_1', [ + 'region' => 'content', + ]); + /** @var \Drupal\search_api\IndexInterface $index */ + $index = Index::load($this->indexId); + $processor = \Drupal::getContainer() + ->get('search_api.plugin_helper') + ->createProcessorPlugin($index, 'ignorecase'); + $index->addProcessor($processor)->save(); + + $this->drupalGet('<front>'); + $this->assertSession()->pageTextContains('Search API Test Block View: Found 4 items'); + + $index->removeProcessor('ignorecase')->save(); + $block->delete(); + } + + /** + * Verifies that exposed fulltext fields work correctly. + */ + protected function checkExposedSearchFields() { + $key = 'display.default.display_options.filters.search_api_fulltext.expose.expose_fields'; + $view = \Drupal::configFactory() + ->getEditable('views.view.search_api_test_view'); + $view->set($key, TRUE); + $view->save(); + + $query = [ + 'search_api_fulltext' => 'foo', + 'search_api_fulltext_searched_fields' => [ + 'name', + ], + ]; + $this->checkResults($query, [1, 2, 4], 'Search for results in name field only'); + + $query = [ + 'search_api_fulltext' => 'foo', + 'search_api_fulltext_searched_fields' => [ + 'body', + ], + ]; + $this->checkResults($query, [5], 'Search for results in body field only'); + + $view->set($key, FALSE); + $view->save(); + } + /** * Checks the Views results for a certain set of parameters. * diff --git a/web/modules/search_api/tests/src/Kernel/Datasource/ReferencedEntitiesReindexingTest.php b/web/modules/search_api/tests/src/Kernel/Datasource/ReferencedEntitiesReindexingTest.php index ead2c474612b4ac794ba9e1042e675fa54bcd686..d41173d264fe053cf0dd196137eb4caa12b52a01 100644 --- a/web/modules/search_api/tests/src/Kernel/Datasource/ReferencedEntitiesReindexingTest.php +++ b/web/modules/search_api/tests/src/Kernel/Datasource/ReferencedEntitiesReindexingTest.php @@ -8,6 +8,7 @@ use Drupal\node\Entity\Node; use Drupal\search_api\Entity\Index; use Drupal\search_api\Entity\Server; +use Drupal\search_api\Utility\TrackingHelper; use Drupal\search_api\Utility\Utility; /** @@ -76,6 +77,7 @@ public function setUp() { 'selected' => ['parent'], ], ], + 'entity:user' => [], ], 'server' => 'server', 'field_settings' => [ @@ -292,4 +294,54 @@ protected function createEntitiesFromMap(array $entity_fields, array $references return $entities; } + /** + * Tests whether relationships are correctly separated between datasources. + * + * @see https://www.drupal.org/node/3178941 + */ + public function testUnrelatedDatasourceUnaffected() { + // First, check whether the tracking helper correctly includes "datasource" + // keys with all foreign relationship entries. + $tracking_helper = \Drupal::getContainer() + ->get('search_api.tracking_helper'); + $method = new \ReflectionMethod(TrackingHelper::class, 'getForeignEntityRelationsMap'); + $method->setAccessible(TRUE); + /** @see \Drupal\search_api\Utility\TrackingHelper::getForeignEntityRelationsMap() */ + $map = $method->invoke($tracking_helper, $this->index); + $expected = [ + [ + 'datasource' => 'entity:node', + 'entity_type' => 'node', + // Note: It's unspecified that this array has string keys, only its + // values are important. Still, it's easier to just reflect the current + // implementation, when checking for equality. + 'bundles' => ['child' => 'child'], + 'property_path_to_foreign_entity' => 'entity_reference', + 'field_name' => 'indexed', + ], + ]; + $this->assertEquals($expected, $map); + + // Then, check whether datasources correctly ignore relationships from other + // datasources, or that they at least don't lead to an exception/error. + $datasource = $this->index->getDatasource('entity:user'); + $entities = $this->createEntitiesFromMap([ + 'child' => [ + 'title' => 'Child', + 'indexed' => 'Indexed value', + 'not_indexed' => 'Not indexed value.', + ], + ], [], 'child'); + $child = reset($entities); + $original_child = clone $child; + $child->get('indexed')->setValue(['New value']); + $result = $datasource->getAffectedItemsForEntityChange($child, $map, $original_child); + $this->assertEquals([], $result); + + // Change foreign relationships map slightly to trigger #3178941 on purpose. + $map[0]['property_path_to_foreign_entity'] = 'entity_reference:entity'; + $result = $datasource->getAffectedItemsForEntityChange($child, $map, $original_child); + $this->assertEquals([], $result); + } + } diff --git a/web/modules/search_api/tests/src/Kernel/Views/ViewsCacheabilityMetadataExportTest.php b/web/modules/search_api/tests/src/Kernel/Views/ViewsCacheabilityMetadataExportTest.php index ff684a5344673c63359c7796bb575039e91dd75f..c6e79270a340ced6ee891ff2f452a2c3668251f2 100644 --- a/web/modules/search_api/tests/src/Kernel/Views/ViewsCacheabilityMetadataExportTest.php +++ b/web/modules/search_api/tests/src/Kernel/Views/ViewsCacheabilityMetadataExportTest.php @@ -155,6 +155,7 @@ public function testViewExport() { // Activate the alter hook and resave the view so it will recalculate the // cacheability metadata. $this->state->set('search_api_test_views.alter_query_cacheability_metadata', TRUE); + $view = $this->getView(); $view->save(); // Check that the altered metadata is now present in the view and the diff --git a/web/modules/search_api/tests/src/Kernel/Views/ViewsFieldTraitTest.php b/web/modules/search_api/tests/src/Kernel/Views/ViewsFieldTraitTest.php index 9ee0bdd15fa5a61908e4fbbdef47706544f2ea9b..11eced6ad62f07c84f88ac582c295450e43d8d08 100644 --- a/web/modules/search_api/tests/src/Kernel/Views/ViewsFieldTraitTest.php +++ b/web/modules/search_api/tests/src/Kernel/Views/ViewsFieldTraitTest.php @@ -103,7 +103,7 @@ protected function setUp() { 'aggregated_field' => [ 'label' => 'Aggregated field', 'property_path' => 'aggregated_field', - 'type' => 'text', + 'type' => 'string', 'configuration' => [ 'type' => 'union', 'fields' => [