diff --git a/composer.json b/composer.json index dd4da730..3d5091b7 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "ext-json": "*", "doctrine/doctrine-bundle": "^2.4", "illuminate/collections": "^8.47", - "meilisearch/meilisearch-php": "^0.20", + "meilisearch/meilisearch-php": "^0.21.0", "symfony/filesystem": "^4.4 || ^5.0 || ^6.0", "symfony/property-access": "^4.4 || ^5.0 || ^6.0", "symfony/serializer": "^4.4 || ^5.0 || ^6.0" diff --git a/src/Command/MeiliSearchCreateCommand.php b/src/Command/MeiliSearchCreateCommand.php index 46c44564..81a86edd 100644 --- a/src/Command/MeiliSearchCreateCommand.php +++ b/src/Command/MeiliSearchCreateCommand.php @@ -5,7 +5,7 @@ namespace MeiliSearch\Bundle\Command; use MeiliSearch\Bundle\Exception\InvalidSettingName; -use MeiliSearch\Bundle\Exception\UpdateException; +use MeiliSearch\Bundle\Exception\TaskException; use MeiliSearch\Bundle\Model\Aggregator; use MeiliSearch\Bundle\SearchService; use MeiliSearch\Client; @@ -64,7 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Creating index '.$index['name'].' for '.$entityClassName.''); - $indexInstance = $this->searchClient->getOrCreateIndex($index['name']); + $indexInstance = $this->searchClient->index($index['name']); if (isset($index['settings']) && is_array($index['settings'])) { foreach ($index['settings'] as $variable => $value) { @@ -74,13 +74,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int throw new InvalidSettingName(sprintf('Invalid setting name: "%s"', $variable)); } - $update = $indexInstance->{$method}($value); + $task = $indexInstance->{$method}($value); - $indexInstance->waitForPendingUpdate($update['updateId']); - $updateStatus = $indexInstance->getUpdateStatus($update['updateId']); + $indexInstance->waitForTask($task['uid']); + $task = $indexInstance->getTask($task['uid']); - if ('failed' === $updateStatus['status']) { - throw new UpdateException($updateStatus['error']); + if ('failed' === $task['status']) { + throw new TaskException($task['error']); } } } diff --git a/src/Command/MeiliSearchImportCommand.php b/src/Command/MeiliSearchImportCommand.php index f9de1e6f..f5676a55 100644 --- a/src/Command/MeiliSearchImportCommand.php +++ b/src/Command/MeiliSearchImportCommand.php @@ -6,7 +6,7 @@ use Doctrine\Persistence\ManagerRegistry; use MeiliSearch\Bundle\Exception\InvalidSettingName; -use MeiliSearch\Bundle\Exception\UpdateException; +use MeiliSearch\Bundle\Exception\TaskException; use MeiliSearch\Bundle\Model\Aggregator; use MeiliSearch\Bundle\SearchService; use MeiliSearch\Client; @@ -116,7 +116,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (isset($index['settings']) && is_array($index['settings']) && count($index['settings']) > 0) { - $indexInstance = $this->searchClient->getOrCreateIndex($index['name']); + $indexInstance = $this->searchClient->index($index['name']); foreach ($index['settings'] as $variable => $value) { $method = sprintf('update%s', ucfirst($variable)); if (false === method_exists($indexInstance, $method)) { @@ -124,14 +124,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Update - $update = $indexInstance->{$method}($value); + $task = $indexInstance->{$method}($value); - // Get Update status from updateID - $indexInstance->waitForPendingUpdate($update['updateId'], $responseTimeout); - $updateStatus = $indexInstance->getUpdateStatus($update['updateId']); + // Get task information using uid + $indexInstance->waitForTask($task['uid'], $responseTimeout); + $task = $indexInstance->getTask($task['uid']); - if ('failed' === $updateStatus['status']) { - throw new UpdateException($updateStatus['error']); + if ('failed' === $task['status']) { + throw new TaskException($task['error']); } else { $output->writeln('Settings updated.'); } @@ -164,15 +164,15 @@ private function formatIndexingResponse(array $batch, int $responseTimeout): arr $indexInstance = $this->searchClient->index($indexName); - // Get Update status from updateID - $indexInstance->waitForPendingUpdate($apiResponse['updateId'], $responseTimeout); - $updateStatus = $indexInstance->getUpdateStatus($apiResponse['updateId']); + // Get task information using uid + $indexInstance->waitForTask($apiResponse['uid'], $responseTimeout); + $task = $indexInstance->getTask($apiResponse['uid']); - if ('failed' === $updateStatus['status']) { - throw new UpdateException($updateStatus['error']); + if ('failed' === $task['status']) { + throw new TaskException($task['error']); } - $formattedResponse[$indexName] += $updateStatus['type']['number']; + $formattedResponse[$indexName] += $task['details']['indexedDocuments']; } } diff --git a/src/Engine.php b/src/Engine.php index bd933b34..ade9e94c 100644 --- a/src/Engine.php +++ b/src/Engine.php @@ -54,8 +54,8 @@ public function index($searchableEntities): array $result = []; foreach ($data as $indexUid => $objects) { $result[$indexUid] = $this->client - ->getOrCreateIndex($indexUid, ['primaryKey' => 'objectID']) - ->addDocuments($objects); + ->index($indexUid) + ->addDocuments($objects, 'objectID'); } return $result; @@ -104,19 +104,20 @@ public function remove($searchableEntities): array /** * Clear the records of an index. * This method enables you to delete an index’s contents (records). + * Will fail if the index does not exists. * * @throws ApiException */ public function clear(string $indexUid): array { - $index = $this->client->getOrCreateIndex($indexUid); - $return = $index->deleteAllDocuments(); + $index = $this->client->index($indexUid); + $task = $index->deleteAllDocuments(); - return $index->getUpdateStatus($return['updateId']); + return $task; } /** - * Delete an index and it's content. + * Delete an index and its content. */ public function delete(string $indexUid): ?array { diff --git a/src/Exception/UpdateException.php b/src/Exception/UpdateException.php index ef887f69..7ceaaa6f 100644 --- a/src/Exception/UpdateException.php +++ b/src/Exception/UpdateException.php @@ -5,8 +5,8 @@ namespace MeiliSearch\Bundle\Exception; /** - * Class UpdateException. + * Class TaskException. */ -final class UpdateException extends \LogicException +final class TaskException extends \LogicException { } diff --git a/src/SearchableEntity.php b/src/SearchableEntity.php index d34ae26d..65114f82 100644 --- a/src/SearchableEntity.php +++ b/src/SearchableEntity.php @@ -61,11 +61,9 @@ public function getIndexUid(): string */ public function getSearchableArray(): array { - /** @var \Doctrine\ORM\Mapping\ClassMetadataInfo&\Doctrine\Persistence\Mapping\ClassMetadata $metadata */ - $metadata = $this->entityMetadata; - $context = [ - 'fieldsMapping' => $metadata->fieldMappings, + /* @phpstan-ignore-next-line */ + 'fieldsMapping' => $this->entityMetadata->fieldMappings, ]; if ($this->useSerializerGroups) { diff --git a/tests/Integration/CommandsTest.php b/tests/Integration/CommandsTest.php index 04130cf9..6e7b2423 100644 --- a/tests/Integration/CommandsTest.php +++ b/tests/Integration/CommandsTest.php @@ -32,7 +32,7 @@ protected function setUp(): void parent::setUp(); $this->client = $this->get('search.client'); - $this->index = $this->client->getOrCreateIndex($this->getPrefix().self::$indexName); + $this->index = $this->client->index($this->getPrefix().self::$indexName); $this->application = new Application(self::createKernel()); } @@ -221,7 +221,7 @@ public function testImportDifferentEntitiesIntoSameIndex(): void $this->assertStringContainsString('Done!', $output); /** @var SearchResult $searchResult */ - $searchResult = $this->client->getOrCreateIndex($this->getPrefix().'tags')->search('Test'); + $searchResult = $this->client->index($this->getPrefix().'tags')->search('Test'); $this->assertCount(8, $searchResult->getHits()); $this->assertSame(8, $searchResult->getHitsCount()); } diff --git a/tests/Integration/EventListener/DoctrineEventSubscriberTest.php b/tests/Integration/EventListener/DoctrineEventSubscriberTest.php index 49abeb19..8db8f799 100644 --- a/tests/Integration/EventListener/DoctrineEventSubscriberTest.php +++ b/tests/Integration/EventListener/DoctrineEventSubscriberTest.php @@ -9,11 +9,24 @@ use MeiliSearch\Bundle\Test\BaseKernelTestCase; use MeiliSearch\Bundle\Test\Entity\Page; use MeiliSearch\Bundle\Test\Entity\Post; +use MeiliSearch\Client; class DoctrineEventSubscriberTest extends BaseKernelTestCase { + protected Client $client; + + /** + * @throws \Exception + */ + public function setUp(): void + { + parent::setUp(); + + $this->client = $this->get('search.client'); + } + /** - * This tests creates two posts in the database, but only one is triggered via an event to MS. + * This tests creates two posts in the database, but only one is triggered via an event to Meilisearch. */ public function testPostPersist(): void { @@ -25,6 +38,8 @@ public function testPostPersist(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postPersist($eventArgs); + $this->waitForAllTasks(); + $result = $this->searchService->search($this->entityManager, Post::class, $post->getTitle()); $this->assertCount(1, $result); @@ -40,7 +55,8 @@ public function testPostPersistWithObjectId(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postPersist($eventArgs); - sleep(1); + + $this->waitForAllTasks(); $result = $this->searchService->search($this->entityManager, Page::class, $page->getTitle()); @@ -49,7 +65,7 @@ public function testPostPersistWithObjectId(): void } /** - * This tests creates two posts in the database, but only one is triggered via an event to MS. + * This tests creates two posts in the database, but only one is triggered via an event to Meilisearch. */ public function testPostUpdate(): void { @@ -61,6 +77,8 @@ public function testPostUpdate(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postUpdate($eventArgs); + $this->waitForAllTasks(); + $result = $this->searchService->search($this->entityManager, Post::class, $post->getTitle()); $this->assertCount(1, $result); @@ -76,7 +94,8 @@ public function testPostUpdateWithObjectId(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postUpdate($eventArgs); - sleep(1); + + $this->waitForAllTasks(); $result = $this->searchService->search($this->entityManager, Page::class, $page->getTitle()); @@ -85,7 +104,7 @@ public function testPostUpdateWithObjectId(): void } /** - * This tests creates posts in the database, send it to MS via a trigger. Afterwards Doctrines 'preRemove' event + * This tests creates posts in the database, send it to Meilisearch via a trigger. Afterwards Doctrines 'preRemove' event * is going to remove that entity from MS. */ public function testPreRemove(): void @@ -97,6 +116,8 @@ public function testPreRemove(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postPersist($eventArgs); + $this->waitForAllTasks(); + $result = $this->searchService->search($this->entityManager, Post::class, $post->getTitle()); $this->assertCount(1, $result); @@ -104,11 +125,7 @@ public function testPreRemove(): void $subscriber->preRemove($eventArgs); - /* - * As the deletion of a document is an asyncronous transaction, we need to wait some seconds - * till this is executed. This was introduced as with Github actions there was no other option. - */ - sleep(2); + $this->waitForAllTasks(); $result = $this->searchService->search($this->entityManager, Post::class, $post->getTitle()); @@ -123,7 +140,8 @@ public function testPreRemoveWithObjectId(): void $subscriber = new DoctrineEventSubscriber($this->searchService, []); $subscriber->postPersist($eventArgs); - sleep(1); + + $this->waitForAllTasks(); $result = $this->searchService->search($this->entityManager, Page::class, $page->getTitle()); @@ -131,10 +149,20 @@ public function testPreRemoveWithObjectId(): void $this->assertSame((string) $page->getId(), (string) $result[0]->getId()); $subscriber->preRemove($eventArgs); - sleep(1); + + $this->waitForAllTasks(); $result = $this->searchService->search($this->entityManager, Page::class, $page->getTitle()); $this->assertCount(0, $result); } + + /** + * Waits for all the tasks to be finished by checking the topest one (so the newest one). + */ + private function waitForAllTasks(): void + { + $firstTask = $this->client->getTasks()['results'][0]; + $this->client->waitForTask($firstTask['uid']); + } } diff --git a/tests/Integration/SearchTest.php b/tests/Integration/SearchTest.php index 2b443092..abff7ea6 100644 --- a/tests/Integration/SearchTest.php +++ b/tests/Integration/SearchTest.php @@ -40,7 +40,7 @@ protected function setUp(): void $this->client = $this->get('search.client'); $this->objectManager = $this->get('doctrine')->getManager(); - $this->index = $this->client->getOrCreateIndex($this->getPrefix().self::$indexName); + $this->index = $this->client->index($this->getPrefix().self::$indexName); $this->application = new Application(self::createKernel()); } diff --git a/tests/Integration/SettingsTest.php b/tests/Integration/SettingsTest.php index 308d9be5..f9ec6e40 100644 --- a/tests/Integration/SettingsTest.php +++ b/tests/Integration/SettingsTest.php @@ -43,8 +43,16 @@ public function setUp(): void public function testGetDefaultSettings(): void { $primaryKey = 'ObjectID'; - $settingA = $this->client->getOrCreateIndex($this->getPrefix().'indexA')->getSettings(); - $settingB = $this->client->getOrCreateIndex($this->getPrefix().'indexB', ['primaryKey' => $primaryKey])->getSettings(); + $indexAName = $this->getPrefix().'indexA'; + $indexBName = $this->getPrefix().'indexB'; + $this->client->createIndex($indexAName); + $this->client->createIndex($indexBName, ['primaryKey' => $primaryKey]); + + $firstTask = $this->client->getTasks()['results'][0]; + $this->client->waitForTask($firstTask['uid']); + + $settingA = $this->client->index($indexAName)->getSettings(); + $settingB = $this->client->index($indexBName)->getSettings(); $this->assertEquals(self::DEFAULT_RANKING_RULES, $settingA['rankingRules']); $this->assertNull($settingA['distinctAttribute']);