diff --git a/Classes/Command/IndexCommandController.php b/Classes/Command/IndexCommandController.php index b01800e..bed8af7 100644 --- a/Classes/Command/IndexCommandController.php +++ b/Classes/Command/IndexCommandController.php @@ -63,24 +63,24 @@ class IndexCommandController extends CommandController * * @param string $identifier Comma separated list of identifiers. */ - public function deleteCommand(string $identifiers) + public function deleteDocumentsCommand(string $identifiers) { $this->executeForIdentifier($identifiers, function (IndexerInterface $indexer) { - $indexer->deleteDocuments(); + $indexer->deleteAllDocuments(); $this->outputLine('Documents in index ' . $indexer->getIdentifier() . ' were deleted.'); }); } /** - * Will flush the index for given identifiers from backend. + * Will delete the index for given identifiers. * * @param string $identifier Comma separated list of identifiers. */ - public function flushCommand(string $identifiers = 'pages') + public function deleteCommand(string $identifiers = 'pages') { $this->executeForIdentifier($identifiers, function (IndexerInterface $indexer) { $indexer->delete(); - $this->outputLine('Indice ' . $indexer->getIdentifier() . ' was flushed.'); + $this->outputLine('Index ' . $indexer->getIdentifier() . ' was deleted.'); }); } diff --git a/Classes/Connection/ConnectionInterface.php b/Classes/Connection/ConnectionInterface.php index 76122f8..7677e7c 100644 --- a/Classes/Connection/ConnectionInterface.php +++ b/Classes/Connection/ConnectionInterface.php @@ -51,17 +51,17 @@ interface ConnectionInterface public function deleteDocument(string $documentType, string $identifier); /** - * Search by given request and return result. + * Will all documents of certain kind / in certain index. */ - public function search(SearchRequestInterface $searchRequest): SearchResultInterface; - - /** - * Will delete the index / db of defined document type. - */ - public function deleteIndexByDocumentType(string $documentType); + public function deleteAllDocuments(string $documentType); /** * Will delete the whole index / db. */ - public function deleteIndex(); + public function deleteIndex(string $documentType); + + /** + * Search by given request and return result. + */ + public function search(SearchRequestInterface $searchRequest): SearchResultInterface; } diff --git a/Classes/Connection/Elasticsearch.php b/Classes/Connection/Elasticsearch.php index 81e1258..0f85058 100644 --- a/Classes/Connection/Elasticsearch.php +++ b/Classes/Connection/Elasticsearch.php @@ -142,6 +142,29 @@ class Elasticsearch implements Singleton, ConnectionInterface } } + public function deleteAllDocuments(string $documentType) + { + $this->deleteDocumentsByQuery($documentType, Query::create([ + 'query' => [ + 'term' => [ + 'search_document_type' => $documentType, + ], + ], + ])); + } + + public function deleteIndex(string $documentType) + { + try { + $this->indexFactory->getIndex($this->connection, $documentType)->delete(); + } catch (\InvalidArgumentException $e) { + $this->logger->notice( + 'Index did not exist, therefore was not deleted.', + [$documentType, $e] + ); + } + } + public function updateDocument(string $documentType, array $document) { $this->withType( @@ -162,49 +185,6 @@ class Elasticsearch implements Singleton, ConnectionInterface ); } - public function deleteIndex() - { - $index = $this->connection->getClient()->getIndex($this->indexFactory->getIndexName()); - - if (!$index->exists()) { - $this->logger->notice( - 'Index did not exist, therefore was not deleted.', - [$this->indexFactory->getIndexName()] - ); - return; - } - - $index->delete(); - } - - public function deleteIndexByDocumentType(string $documentType) - { - $this->deleteIndexByQuery(Query::create([ - 'query' => [ - 'term' => [ - 'search_document_type' => $documentType, - ], - ], - ])); - } - - private function deleteIndexByQuery(Query $query) - { - $index = $this->connection->getClient()->getIndex($this->indexFactory->getIndexName()); - if (!$index->exists()) { - $this->logger->notice( - 'Index did not exist, therefore items can not be deleted by query.', - [$this->indexFactory->getIndexName(), $query->getQuery()] - ); - return; - } - $response = $index->deleteByQuery($query); - if ($response->getData()['deleted'] > 0) { - // Refresh index when delete query is invoked - $index->refresh(); - } - } - public function search(SearchRequestInterface $searchRequest): SearchResultInterface { $this->logger->debug('Search for', [$searchRequest->getSearchTerm()]); @@ -232,4 +212,22 @@ class Elasticsearch implements Singleton, ConnectionInterface $callback($type, $documentType); $type->getIndex()->refresh(); } + + private function deleteDocumentsByQuery(string $documentType, Query $query) + { + try { + $index = $this->indexFactory->getIndex($this->connection, $documentType); + $response = $index->deleteByQuery($query); + + if ($response->getData()['deleted'] > 0) { + // Refresh index when delete query is invoked + $index->refresh(); + } + } catch (\InvalidArgumentException $e) { + $this->logger->notice( + 'Index did not exist, therefore items can not be deleted by query.', + [$documentType, $query->getQuery()] + ); + } + } } diff --git a/Classes/Connection/Elasticsearch/IndexFactory.php b/Classes/Connection/Elasticsearch/IndexFactory.php index 2482c0e..718342f 100644 --- a/Classes/Connection/Elasticsearch/IndexFactory.php +++ b/Classes/Connection/Elasticsearch/IndexFactory.php @@ -70,22 +70,35 @@ class IndexFactory implements Singleton } /** - * Get an index based on TYPO3 table name. + * @throws \InvalidArgumentException If index does not exist. */ public function getIndex(Connection $connection, string $documentType): \Elastica\Index { $index = $connection->getClient()->getIndex($this->getIndexName()); if ($index->exists() === false) { - $config = $this->getConfigurationFor($documentType); - $this->logger->debug(sprintf('Create index %s.', $documentType), [$documentType, $config]); - $index->create($config); - $this->logger->debug(sprintf('Created index %s.', $documentType), [$documentType]); + throw new \InvalidArgumentException('The requested index does not exist.', 1546173102); } return $index; } + public function createIndex(Connection $connection, string $documentType): \Elastica\Index + { + $index = $connection->getClient()->getIndex($this->getIndexName()); + + if ($index->exists() === true) { + return $index; + } + + $config = $this->getConfigurationFor($documentType); + $this->logger->debug(sprintf('Create index %s.', $documentType), [$documentType, $config]); + $index->create($config); + $this->logger->debug(sprintf('Created index %s.', $documentType), [$documentType]); + + return $index; + } + protected function getConfigurationFor(string $documentType): array { try { diff --git a/Classes/Connection/Elasticsearch/TypeFactory.php b/Classes/Connection/Elasticsearch/TypeFactory.php index 92cc6a1..8e52eb2 100644 --- a/Classes/Connection/Elasticsearch/TypeFactory.php +++ b/Classes/Connection/Elasticsearch/TypeFactory.php @@ -49,12 +49,9 @@ class TypeFactory implements Singleton $this->connection = $connection; } - /** - * Get an index bases on TYPO3 table name. - */ public function getType(string $documentType): \Elastica\Type { - $index = $this->indexFactory->getIndex($this->connection, $documentType); + $index = $this->indexFactory->createIndex($this->connection, $documentType); return $index->getType('document'); } } diff --git a/Classes/Domain/Index/AbstractIndexer.php b/Classes/Domain/Index/AbstractIndexer.php index adb172e..7eae615 100644 --- a/Classes/Domain/Index/AbstractIndexer.php +++ b/Classes/Domain/Index/AbstractIndexer.php @@ -106,14 +106,14 @@ abstract class AbstractIndexer implements IndexerInterface public function delete() { $this->logger->info('Start deletion of index.'); - $this->connection->deleteIndex(); + $this->connection->deleteIndex($this->getDocumentName()); $this->logger->info('Finish deletion.'); } - public function deleteDocuments() + public function deleteAllDocuments() { $this->logger->info('Start deletion of indexed documents.'); - $this->connection->deleteIndexByDocumentType($this->getDocumentName()); + $this->connection->deleteAllDocuments($this->getDocumentName()); $this->logger->info('Finish deletion.'); } diff --git a/Classes/Domain/Index/IndexerInterface.php b/Classes/Domain/Index/IndexerInterface.php index f413677..4071ee8 100644 --- a/Classes/Domain/Index/IndexerInterface.php +++ b/Classes/Domain/Index/IndexerInterface.php @@ -42,9 +42,9 @@ interface IndexerInterface public function delete(); /** - * Delete the whole index. + * Delete all documents from index. */ - public function deleteDocuments(); + public function deleteAllDocuments(); /** * Receives the identifier of the indexer itself. diff --git a/Documentation/source/changelog/0.1.0.rst b/Documentation/source/changelog/0.1.0.rst index 036a222..3cfc4ce 100644 --- a/Documentation/source/changelog/0.1.0.rst +++ b/Documentation/source/changelog/0.1.0.rst @@ -8,7 +8,7 @@ v0.1.0 0.1.0/2018-changed-interfaces 0.1.0/2018-elasticsearch-upgrade 0.1.0/2018-search-service-interface - 0.1.0/20181027-added-flush-command + 0.1.0/20181027-added-delete-all-documents-command 0.1.0/20181027-allow-multiple-identifier-on-cli 0.1.0/20181027-remove-cms7-support 0.1.0/20181028-fluid-templating-list-items diff --git a/Documentation/source/changelog/0.1.0/2018-changed-interfaces.rst b/Documentation/source/changelog/0.1.0/2018-changed-interfaces.rst index 993b7da..669c776 100644 --- a/Documentation/source/changelog/0.1.0/2018-changed-interfaces.rst +++ b/Documentation/source/changelog/0.1.0/2018-changed-interfaces.rst @@ -5,7 +5,7 @@ Some interfaces and abstract classes have been adjusted: ``Codappix\SearchCore\Connection\ConnectionInterface``: - * New method ``public function deleteIndexByDocumentType(string $documentType);`` + * New method ``public function deleteAllDocuments(string $documentType);`` ``Codappix\SearchCore\Domain\Index\IndexerInterface``: @@ -28,4 +28,12 @@ Also some exceptions have changed: throws an ``\InvalidArgumentException`` instead of ``\Exception``, if no ``search_identifier`` was provided. +* ``Codappix\SearchCore\Connection\Elasticsearch\IndexFactory::getIndex()`` now + throws an ``\InvalidArgumentException`` if the index does not exist. Leaving + handling up to the caller. + Before the index was created if it didn't exist. To create an index, a new method + ``public function createIndex(Connection $connection, string $documentType): \Elastica\Index`` + was added. This method will only create the index if it didn't exist before. + In the end, the index is returned always. Making this method a 1:1 replacement for + older ``getIndex()``. diff --git a/Documentation/source/changelog/0.1.0/20181027-added-delete-all-documents-command.rst b/Documentation/source/changelog/0.1.0/20181027-added-delete-all-documents-command.rst new file mode 100644 index 0000000..22f221a --- /dev/null +++ b/Documentation/source/changelog/0.1.0/20181027-added-delete-all-documents-command.rst @@ -0,0 +1,11 @@ +Feature "Added delete documents command" +======================================== + +A new command to delete all documents within an index was added. In contrast to the +existing delete command, this deletes only documents but keeps the index. + +E.g. if your backend is Elasticsearch or a relational database, the index or table is +kept, including structure or mappings, while only the documents or rows are removed. + +In contrast the existing delete command will still remove the index or table itself, +depending on the used connection. diff --git a/Documentation/source/changelog/0.1.0/20181027-added-flush-command.rst b/Documentation/source/changelog/0.1.0/20181027-added-flush-command.rst deleted file mode 100644 index 569865d..0000000 --- a/Documentation/source/changelog/0.1.0/20181027-added-flush-command.rst +++ /dev/null @@ -1,6 +0,0 @@ -Feature "Added flush command" -============================= - -A new command to flush indices was added. In contrast to the existing delete command, -this one will delete the whole index, while the existing delete command only deletes -all documents within an index. diff --git a/Documentation/source/usage.rst b/Documentation/source/usage.rst index 687ae2d..b385c55 100644 --- a/Documentation/source/usage.rst +++ b/Documentation/source/usage.rst @@ -36,18 +36,17 @@ documents from the index. Multiple indexes can be called by providing a comma separated list of identifiers as a single argument. Spaces before and after commas are ignored. -.. _usage_manual_flush: +.. _usage_manual_delete_all_documents: -Manual flush ------------- +Manual delete all documents +--------------------------- -You can trigger flush for indexes from CLI:: +You can trigger deletion of all documents for indexes from CLI:: - ./typo3/cli_dispatch.phpsh extbase index:flush --identifiers 'pages' - ./bin/typo3cms index:flush --identifiers 'pages' + ./typo3/cli_dispatch.phpsh extbase index:deletedocuments --identifiers 'pages' + ./bin/typo3cms index:deletedocuments --identifiers 'pages' -This will flush the index for the table ``pages``. Flush means removing the index -from backend. +This will delete all documents within the index for the table ``pages``. Multiple indexes can be called by providing a comma separated list of identifiers as a single argument. Spaces before and after commas are ignored. diff --git a/Tests/Functional/Connection/Elasticsearch/IndexDeletionTest.php b/Tests/Functional/Connection/Elasticsearch/IndexDeletionTest.php index ee14f25..ae27737 100644 --- a/Tests/Functional/Connection/Elasticsearch/IndexDeletionTest.php +++ b/Tests/Functional/Connection/Elasticsearch/IndexDeletionTest.php @@ -89,14 +89,12 @@ class IndexDeletionTest extends AbstractFunctionalTestCase $response = $this->client->request('typo3content/_search?q=*:*'); $this->assertSame($response->getData()['hits']['total'], 5, 'Not exactly 5 documents are in index.'); - $contentIndexer->deleteDocuments(); + $contentIndexer->deleteAllDocuments(); $response = $this->client->request('typo3content/_search?q=*:*'); $this->assertSame($response->getData()['hits']['total'], 2, 'Not exactly 2 documents are in index.'); - $pageIndexer->deleteDocuments(); + $pageIndexer->deleteAllDocuments(); $response = $this->client->request('typo3content/_search?q=*:*'); $this->assertSame($response->getData()['hits']['total'], 0, 'Index should be empty.'); - - $index->delete(); } } diff --git a/Tests/Unit/Command/IndexCommandControllerTest.php b/Tests/Unit/Command/IndexCommandControllerTest.php index 2b52109..69f02ba 100644 --- a/Tests/Unit/Command/IndexCommandControllerTest.php +++ b/Tests/Unit/Command/IndexCommandControllerTest.php @@ -97,7 +97,7 @@ class IndexCommandControllerTest extends AbstractUnitTestCase /** * @test */ - public function deletionIsPossible() + public function deletionOfDocumentsIsPossible() { $indexerMock = $this->getMockBuilder(TcaIndexer::class) ->disableOriginalConstructor() @@ -114,14 +114,14 @@ class IndexCommandControllerTest extends AbstractUnitTestCase ->will($this->returnValue($indexerMock)); $indexerMock->expects($this->once()) - ->method('deleteDocuments'); - $this->subject->deleteCommand('allowedTable'); + ->method('deleteAllDocuments'); + $this->subject->deleteDocumentsCommand('allowedTable'); } /** * @test */ - public function flushIsPossible() + public function deletionOfIndexIsPossible() { $indexerMock = $this->getMockBuilder(TcaIndexer::class) ->disableOriginalConstructor() @@ -139,7 +139,7 @@ class IndexCommandControllerTest extends AbstractUnitTestCase $indexerMock->expects($this->once()) ->method('delete'); - $this->subject->flushCommand('pages'); + $this->subject->deleteCommand('pages'); } /** diff --git a/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php b/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php index 6d80f70..77d5f90 100644 --- a/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php +++ b/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php @@ -150,6 +150,6 @@ class IndexFactoryTest extends AbstractUnitTestCase ]) ); - $this->subject->getIndex($connection, 'someIndex'); + $this->subject->createIndex($connection, 'someIndex'); } }