diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 851dcedab1c4d4614842dbcd4977076148211e0d..894a3e12692d62941faa3a18f4bd3339c5fa6fc3 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,7 @@ Search API 1.x, dev (xxxx-xx-xx): --------------------------------- +- #2922525 by drunken monkey, borisson_: Changed "Index items immediately" to + delay indexing until the end of the page request. - #2930720 by drunken monkey, borisson_: Added a UI for rebuilding the tracking table for an index. - #2912246 by drunken monkey: Fixed inconsistent array indices in query diff --git a/search_api.services.yml b/search_api.services.yml index eaff4455b870f9f94ad9d146efddddb65a8edcad..92f58db5db31b2cb39716da4e306c2fedd1b9fd8 100644 --- a/search_api.services.yml +++ b/search_api.services.yml @@ -68,6 +68,12 @@ services: class: Drupal\search_api\Utility\PluginHelper arguments: ['@plugin.manager.search_api.datasource', '@plugin.manager.search_api.processor', '@plugin.manager.search_api.tracker'] + search_api.post_request_indexing: + class: Drupal\search_api\Utility\PostRequestIndexing + arguments: ['@entity_type.manager'] + tags: + - { name: event_subscriber } + search_api.query_helper: class: Drupal\search_api\Utility\QueryHelper arguments: ['@request_stack', '@module_handler', '@plugin.manager.search_api.parse_mode'] diff --git a/src/Entity/Index.php b/src/Entity/Index.php index 8d69c653449a1179fbfa0188e4c492c5e4dd8ed0..e0eab54de05146e49293bdc6a6120718f02abe7c 100644 --- a/src/Entity/Index.php +++ b/src/Entity/Index.php @@ -1062,15 +1062,8 @@ class Index extends ConfigEntityBase implements IndexInterface { } $this->getTrackerInstance()->$tracker_method($item_ids); if (!$this->isReadOnly() && $this->getOption('index_directly') && !$this->batchTracking) { - try { - $items = $this->loadItemsMultiple($item_ids); - if ($items) { - $this->indexSpecificItems($items); - } - } - catch (SearchApiException $e) { - $this->logException($e); - } + \Drupal::getContainer()->get('search_api.post_request_indexing') + ->registerIndexingOperation($this->id(), $item_ids); } } } diff --git a/src/Utility/PostRequestIndexing.php b/src/Utility/PostRequestIndexing.php new file mode 100644 index 0000000000000000000000000000000000000000..a9cc74504164ecd83cc545e0319f74608e3436bd --- /dev/null +++ b/src/Utility/PostRequestIndexing.php @@ -0,0 +1,108 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[KernelEvents::TERMINATE][] = ['onKernelTerminate']; + + return $events; + } + + /** + * Indexes all items that were registered for indexing in this page request. + * + * Invoked by the terminate kernel event. + * + * @see \Symfony\Component\HttpKernel\Event\PostResponseEvent + */ + public function onKernelTerminate() { + foreach ($this->operations as $index_id => $item_ids) { + try { + $storage = $this->entityTypeManager->getStorage('search_api_index'); + } + catch (InvalidPluginDefinitionException $e) { + // It might be possible that the module got uninstalled during the rest + // of the page request, or something else happened. To be on the safe + // side, catch the exception in case the entity type isn't found. + return; + } + + /** @var \Drupal\search_api\IndexInterface $index */ + $index = $storage->load($index_id); + // It's possible that the index was deleted in the meantime, so make sure + // it's actually there. + if (!$index) { + continue; + } + + try { + $items = $index->loadItemsMultiple($item_ids); + if ($items) { + $index->indexSpecificItems($items); + } + } + catch (SearchApiException $e) { + $vars['%index'] = $index->label(); + watchdog_exception('search_api', $e, '%type while trying to index items on %index: @message in %function (line %line of %file).', $vars); + } + + // We usually shouldn't be called twice in a page request, but no harm in + // being too careful: Remove the operation once it was executed correctly. + unset($this->operations[$index_id]); + } + } + + /** + * {@inheritdoc} + */ + public function registerIndexingOperation($index_id, array $item_ids) { + foreach ($item_ids as $item_id) { + $this->operations[$index_id][$item_id] = $item_id; + } + } + +} diff --git a/src/Utility/PostRequestIndexingInterface.php b/src/Utility/PostRequestIndexingInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..f7a72aed939085cc54a5fa5d1cb430db7210bc9e --- /dev/null +++ b/src/Utility/PostRequestIndexingInterface.php @@ -0,0 +1,20 @@ +assertEquals(2, $tracked_items, 'Two items are tracked.'); // Index items, then check whether updating an article is handled correctly. + $this->triggerPostRequestIndexing(); $this->getCalledMethods('backend'); $article1->save(); $methods = $this->getCalledMethods('backend'); + $this->assertEquals([], $methods, 'No items were indexed right away (before end of page request).'); + $this->triggerPostRequestIndexing(); + $methods = $this->getCalledMethods('backend'); $this->assertEquals(['indexItems'], $methods, 'Update successfully tracked.'); $article1->search_api_skip_tracking = TRUE; diff --git a/tests/src/Kernel/PostRequestIndexingTrait.php b/tests/src/Kernel/PostRequestIndexingTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..dddcfc0108d820eff9848512857030aeeb7ad9ae --- /dev/null +++ b/tests/src/Kernel/PostRequestIndexingTrait.php @@ -0,0 +1,18 @@ +get('search_api.post_request_indexing') + ->onKernelTerminate(); + } + +} diff --git a/tests/src/Kernel/System/CliTest.php b/tests/src/Kernel/System/CliTest.php index 0dc90666a6e45c562d0105fd88cfc9b5e21782b0..0c418d144a755dd5040f65aea85361ce863a12a9 100644 --- a/tests/src/Kernel/System/CliTest.php +++ b/tests/src/Kernel/System/CliTest.php @@ -7,6 +7,7 @@ use Drupal\KernelTests\KernelTestBase; use Drupal\search_api\Entity\Index; use Drupal\search_api\Entity\Server; use Drupal\search_api\Utility\Utility; +use Drupal\Tests\search_api\Kernel\PostRequestIndexingTrait; /** * Tests Search API functionality when executed in the CLI. @@ -15,6 +16,8 @@ use Drupal\search_api\Utility\Utility; */ class CliTest extends KernelTestBase { + use PostRequestIndexingTrait; + /** * The search server used for testing. * @@ -123,7 +126,15 @@ class CliTest extends KernelTestBase { $indexed_items = $index->getTrackerInstance()->getIndexedItemsCount(); $this->assertEquals(4, $total_items, 'All 4 items are tracked.'); - $this->assertEquals(2, $indexed_items, '2 items are indexed'); + $this->assertEquals(0, $indexed_items, 'No items are indexed.'); + + $this->triggerPostRequestIndexing(); + + $total_items = $index->getTrackerInstance()->getTotalItemsCount(); + $indexed_items = $index->getTrackerInstance()->getIndexedItemsCount(); + + $this->assertEquals(4, $total_items, 'All 4 items are tracked.'); + $this->assertEquals(2, $indexed_items, '2 items are indexed.'); } }