summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Seidl2018-02-23 10:15:17 (GMT)
committerThomas Seidl2018-02-23 10:15:17 (GMT)
commitce68da16f22423c01c44c80790d7c1afd9b3dcc5 (patch)
treedc04f37326eb0b0364b8d86b8f9dbac6d9b41ebb
parent20307f5e7a60e97e7f45d3435bce18ecc0e545bd (diff)
Issue #2922525 by drunken monkey, borisson_: Changed "Index items immediately" to delay indexing until the end of the page request.
-rw-r--r--CHANGELOG.txt2
-rw-r--r--search_api.services.yml6
-rw-r--r--src/Entity/Index.php11
-rw-r--r--src/Utility/PostRequestIndexing.php108
-rw-r--r--src/Utility/PostRequestIndexingInterface.php20
-rw-r--r--tests/src/Functional/IntegrationTest.php6
-rw-r--r--tests/src/Kernel/PostRequestIndexingTrait.php18
-rw-r--r--tests/src/Kernel/System/CliTest.php13
8 files changed, 174 insertions, 10 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 851dced..894a3e1 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 eaff445..92f58db 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 8d69c65..e0eab54 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 0000000..a9cc745
--- /dev/null
+++ b/src/Utility/PostRequestIndexing.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Drupal\search_api\Utility;
+
+use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\search_api\LoggerTrait;
+use Drupal\search_api\SearchApiException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Provides a service for indexing items at the end of the page request.
+ */
+class PostRequestIndexing implements PostRequestIndexingInterface, EventSubscriberInterface {
+
+ use LoggerTrait;
+
+ /**
+ * Indexing operations that should be executed at the end of the page request.
+ *
+ * The array is keyed by index ID and has arrays of item IDs to index for that
+ * search index as values.
+ *
+ * @var string[][]
+ */
+ protected $operations = [];
+
+ /**
+ * The entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * Constructs a new class instance.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ */
+ public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+ $this->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 0000000..f7a72ae
--- /dev/null
+++ b/src/Utility/PostRequestIndexingInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\search_api\Utility;
+
+/**
+ * Provides an interface for the post-request indexing service.
+ */
+interface PostRequestIndexingInterface {
+
+ /**
+ * Registers items for indexing at the end of the page request.
+ *
+ * @param string $index_id
+ * The ID of the search index on which items should be indexed.
+ * @param array $item_ids
+ * The IDs of the items to index.
+ */
+ public function registerIndexingOperation($index_id, array $item_ids);
+
+}
diff --git a/tests/src/Functional/IntegrationTest.php b/tests/src/Functional/IntegrationTest.php
index 8095787..39d678d 100644
--- a/tests/src/Functional/IntegrationTest.php
+++ b/tests/src/Functional/IntegrationTest.php
@@ -16,6 +16,7 @@ use Drupal\search_api\SearchApiException;
use Drupal\search_api\Utility\Utility;
use Drupal\search_api_test\Plugin\search_api\tracker\TestTracker;
use Drupal\search_api_test\PluginTestTrait;
+use Drupal\Tests\search_api\Kernel\PostRequestIndexingTrait;
/**
* Tests the overall functionality of the Search API framework and admin UI.
@@ -25,6 +26,7 @@ use Drupal\search_api_test\PluginTestTrait;
class IntegrationTest extends SearchApiBrowserTestBase {
use PluginTestTrait;
+ use PostRequestIndexingTrait;
/**
* An admin user used for this test.
@@ -653,9 +655,13 @@ class IntegrationTest extends SearchApiBrowserTestBase {
$this->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 0000000..dddcfc0
--- /dev/null
+++ b/tests/src/Kernel/PostRequestIndexingTrait.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\Tests\search_api\Kernel;
+
+/**
+ * Provides a helper method for triggering post-request indexing.
+ */
+trait PostRequestIndexingTrait {
+
+ /**
+ * Triggers any post-request indexing operations that were registered.
+ */
+ protected function triggerPostRequestIndexing() {
+ \Drupal::getContainer()->get('search_api.post_request_indexing')
+ ->onKernelTerminate();
+ }
+
+}
diff --git a/tests/src/Kernel/System/CliTest.php b/tests/src/Kernel/System/CliTest.php
index 0dc9066..0c418d1 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.');
}
}