From ba64fa5c2ff18e92c8757700e5e9d5b6d174149d Mon Sep 17 00:00:00 2001 From: meili-bot <74670311+meili-bot@users.noreply.github.com> Date: Mon, 7 Nov 2022 18:07:43 +0100 Subject: [PATCH 01/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a4e03929..456b6be3 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ $index->search( ## 🤖 Compatibility with Meilisearch -This package only guarantees the compatibility with the [version v0.29.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.29.0). +This package only guarantees the compatibility with the [version v0.30.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.30.0). ## 💡 Learn more From 9df897b425b4f38938ce6b77b188c4120203dfec Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 17:41:35 -0300 Subject: [PATCH 02/13] Fix tasks specs with v0.30rc2 --- src/Contracts/TasksQuery.php | 26 +++++++++++++------------- tests/Contracts/TasksQueryTest.php | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Contracts/TasksQuery.php b/src/Contracts/TasksQuery.php index 6a117a47..1fdcb498 100644 --- a/src/Contracts/TasksQuery.php +++ b/src/Contracts/TasksQuery.php @@ -9,9 +9,9 @@ class TasksQuery private int $from; private int $limit; private int $next; - private array $type; - private array $status; - private array $indexUid; + private array $types; + private array $statuses; + private array $indexUids; public function setFrom(int $from): TasksQuery { @@ -36,28 +36,28 @@ public function setNext(int $next): TasksQuery public function setTypes(array $types): TasksQuery { - $this->type = $types; + $this->types = $types; return $this; } - public function setStatus(array $status): TasksQuery + public function setStatuses(array $statuses): TasksQuery { - $this->status = $status; + $this->statuses = $statuses; return $this; } - public function setUid(array $indexUid): TasksQuery + public function setIndexUids(array $indexUids): TasksQuery { - $this->indexUid = $indexUid; + $this->indexUids = $indexUids; return $this; } - public function getUid(): array + public function getIndexUids(): array { - return $this->indexUid ?? []; + return $this->indexUids ?? []; } public function toArray(): array @@ -66,9 +66,9 @@ public function toArray(): array 'from' => $this->from ?? null, 'limit' => $this->limit ?? null, 'next' => $this->next ?? null, - 'status' => isset($this->status) ? implode(',', $this->status) : null, - 'type' => isset($this->type) ? implode(',', $this->type) : null, - 'indexUid' => isset($this->indexUid) ? implode(',', $this->indexUid) : null, + 'statuses' => isset($this->statuses) ? implode(',', $this->statuses) : null, + 'types' => isset($this->types) ? implode(',', $this->types) : null, + 'indexUids' => isset($this->indexUids) ? implode(',', $this->indexUids) : null, ], function ($item) { return null != $item || is_numeric($item); }); } } diff --git a/tests/Contracts/TasksQueryTest.php b/tests/Contracts/TasksQueryTest.php index 6e813a27..fb79aee5 100644 --- a/tests/Contracts/TasksQueryTest.php +++ b/tests/Contracts/TasksQueryTest.php @@ -13,7 +13,7 @@ public function testSetTypes(): void { $data = (new TasksQuery())->setTypes(['abc', 'xyz']); - $this->assertEquals($data->toArray(), ['type' => 'abc,xyz']); + $this->assertEquals($data->toArray(), ['types' => 'abc,xyz']); } public function testSetNext(): void @@ -42,7 +42,7 @@ public function testToArrayWithDifferentSets(): void $data = (new TasksQuery())->setFrom(10)->setLimit(9)->setNext(99)->setStatus(['enqueued']); $this->assertEquals($data->toArray(), [ - 'limit' => 9, 'next' => 99, 'from' => 10, 'status' => 'enqueued', + 'limit' => 9, 'next' => 99, 'from' => 10, 'statuses' => 'enqueued', ]); } } From ef5c5cdb7b085f212c900d9c7cf9bbd58190098e Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 19:59:38 -0300 Subject: [PATCH 03/13] Fix method naming after refactor (from setUid to setIndexUids) --- src/Endpoints/Indexes.php | 6 +++--- tests/Endpoints/IndexTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index 9dc50c62..c8988dad 100644 --- a/src/Endpoints/Indexes.php +++ b/src/Endpoints/Indexes.php @@ -164,10 +164,10 @@ public function getTasks(TasksQuery $options = null): TasksResults { $options = $options ?? new TasksQuery(); - if (0 == \count($options->getUid())) { - $options->setUid(array_merge([$this->uid], $options->getUid())); + if (0 == \count($options->getIndexUids())) { + $options->setIndexUids(array_merge([$this->uid], $options->getIndexUids())); } else { - $options->setUid([$this->uid]); + $options->setIndexUids([$this->uid]); } $response = $this->http->get('/tasks', $options->toArray()); diff --git a/tests/Endpoints/IndexTest.php b/tests/Endpoints/IndexTest.php index aac0de45..edfb9ad1 100644 --- a/tests/Endpoints/IndexTest.php +++ b/tests/Endpoints/IndexTest.php @@ -185,7 +185,7 @@ public function testGetTasks(): void $promise = $this->index->addDocuments([['id' => 1, 'title' => 'Pride and Prejudice']]); $this->index->waitForTask($promise['taskUid']); - $tasks = $this->index->getTasks((new TasksQuery())->setUid(['other-index'])); + $tasks = $this->index->getTasks((new TasksQuery())->setIndexUids(['other-index'])); $allIndexUids = array_map(function ($val) { return $val['indexUid']; }, $tasks->getResults()); $results = array_unique($allIndexUids); From 8fa99756117ae3bd959e3105ab91f80d9e5dc74c Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 23:10:32 -0300 Subject: [PATCH 04/13] Move TasksQuery contents to a new trait TasksQueryTrait --- src/Contracts/TasksQuery.php | 55 ++++-------- src/Delegates/TasksQueryTrait.php | 138 ++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 40 deletions(-) create mode 100644 src/Delegates/TasksQueryTrait.php diff --git a/src/Contracts/TasksQuery.php b/src/Contracts/TasksQuery.php index 1fdcb498..c7eee15b 100644 --- a/src/Contracts/TasksQuery.php +++ b/src/Contracts/TasksQuery.php @@ -4,14 +4,14 @@ namespace MeiliSearch\Contracts; +use MeiliSearch\Delegates\TasksQueryTrait; + class TasksQuery { + use TasksQueryTrait; + private int $from; private int $limit; - private int $next; - private array $types; - private array $statuses; - private array $indexUids; public function setFrom(int $from): TasksQuery { @@ -27,48 +27,23 @@ public function setLimit(int $limit): TasksQuery return $this; } - public function setNext(int $next): TasksQuery - { - $this->next = $next; - - return $this; - } - - public function setTypes(array $types): TasksQuery - { - $this->types = $types; - - return $this; - } - - public function setStatuses(array $statuses): TasksQuery - { - $this->statuses = $statuses; - - return $this; - } - - public function setIndexUids(array $indexUids): TasksQuery - { - $this->indexUids = $indexUids; - - return $this; - } - - public function getIndexUids(): array - { - return $this->indexUids ?? []; - } - public function toArray(): array { return array_filter([ 'from' => $this->from ?? null, 'limit' => $this->limit ?? null, 'next' => $this->next ?? null, - 'statuses' => isset($this->statuses) ? implode(',', $this->statuses) : null, - 'types' => isset($this->types) ? implode(',', $this->types) : null, - 'indexUids' => isset($this->indexUids) ? implode(',', $this->indexUids) : null, + 'beforeEnqueuedAt' => $this->formatDate($this->beforeEnqueuedAt ?? null), + 'afterEnqueuedAt' => $this->formatDate($this->afterEnqueuedAt ?? null), + 'beforeStartedAt' => $this->formatDate($this->beforeStartedAt ?? null), + 'afterStartedAt' => $this->formatDate($this->afterStartedAt ?? null), + 'beforeFinishedAt' => $this->formatDate($this->beforeFinishedAt ?? null), + 'afterFinishedAt' => $this->formatDate($this->afterFinishedAt ?? null), + 'statuses' => $this->formatArray($this->statuses ?? null), + 'uids' => $this->formatArray($this->uids ?? null), + 'canceledBy' => $this->formatArray($this->canceledBy ?? null), + 'types' => $this->formatArray($this->types ?? null), + 'indexUids' => $this->formatArray($this->indexUids ?? null), ], function ($item) { return null != $item || is_numeric($item); }); } } diff --git a/src/Delegates/TasksQueryTrait.php b/src/Delegates/TasksQueryTrait.php new file mode 100644 index 00000000..7619b1cc --- /dev/null +++ b/src/Delegates/TasksQueryTrait.php @@ -0,0 +1,138 @@ +next = $next; + + return $this; + } + + public function setTypes(array $types) + { + $this->types = $types; + + return $this; + } + + public function setStatuses(array $statuses) + { + $this->statuses = $statuses; + + return $this; + } + + public function setIndexUids(array $indexUids) + { + $this->indexUids = $indexUids; + + return $this; + } + + public function getIndexUids(): array + { + return $this->indexUids ?? []; + } + + public function setUids(array $uids) + { + $this->uids = $uids; + + return $this; + } + + public function setCanceledBy(array $canceledBy) + { + $this->canceledBy = $canceledBy; + + return $this; + } + + public function setBeforeEnqueuedAt(\DateTime $date) + { + $this->beforeEnqueuedAt = $date; + + return $this; + } + + public function setAfterEnqueuedAt(\DateTime $date) + { + $this->afterEnqueuedAt = $date; + + return $this; + } + + public function setBeforeStartedAt(\DateTime $date) + { + $this->beforeStartedAt = $date; + + return $this; + } + + public function setAfterStartedAt(\DateTime $date) + { + $this->afterStartedAt = $date; + + return $this; + } + + public function setBeforeFinishedAt(\DateTime $date) + { + $this->beforeFinishedAt = $date; + + return $this; + } + + public function setAfterFinishedAt(\DateTime $date) + { + $this->afterFinishedAt = $date; + + return $this; + } + + public function toArray(): array + { + return array_filter([ + 'next' => $this->next ?? null, + 'beforeEnqueuedAt' => $this->formatDate($this->beforeEnqueuedAt ?? null), + 'afterEnqueuedAt' => $this->formatDate($this->afterEnqueuedAt ?? null), + 'beforeStartedAt' => $this->formatDate($this->beforeStartedAt ?? null), + 'afterStartedAt' => $this->formatDate($this->afterStartedAt ?? null), + 'beforeFinishedAt' => $this->formatDate($this->beforeFinishedAt ?? null), + 'afterFinishedAt' => $this->formatDate($this->afterFinishedAt ?? null), + 'statuses' => $this->formatArray($this->statuses ?? null), + 'uids' => $this->formatArray($this->uids ?? null), + 'canceledBy' => $this->formatArray($this->canceledBy ?? null), + 'types' => $this->formatArray($this->types ?? null), + 'indexUids' => $this->formatArray($this->indexUids ?? null), + ], function ($item) { return null != $item || is_numeric($item); }); + } + + private function formatDate(?\DateTime $date) + { + return isset($date) ? $date->format(\DateTime::RFC3339) : null; + } + + private function formatArray(?array $arr) + { + return isset($arr) ? implode(',', $arr) : null; + } +} From 332beb21fcff8f9912248dff09de4a8dd737d7d0 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 23:11:32 -0300 Subject: [PATCH 05/13] Add test use cases for the new date filters --- tests/Contracts/TasksQueryTest.php | 10 +++++++++- tests/Endpoints/TasksTest.php | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/Contracts/TasksQueryTest.php b/tests/Contracts/TasksQueryTest.php index fb79aee5..aaf1ea3f 100644 --- a/tests/Contracts/TasksQueryTest.php +++ b/tests/Contracts/TasksQueryTest.php @@ -23,6 +23,14 @@ public function testSetNext(): void $this->assertEquals($data->toArray(), ['next' => 99]); } + public function testSetAnyDateFilter(): void + { + $date = new \DateTime(); + $data = (new TasksQuery())->setBeforeEnqueuedAt($date); + + $this->assertEquals($data->toArray(), ['beforeEnqueuedAt' => $date->format(\DateTime::RFC3339)]); + } + public function testToArrayWithSetLimit(): void { $data = (new TasksQuery())->setLimit(10); @@ -39,7 +47,7 @@ public function testToArrayWithSetLimitWithZero(): void public function testToArrayWithDifferentSets(): void { - $data = (new TasksQuery())->setFrom(10)->setLimit(9)->setNext(99)->setStatus(['enqueued']); + $data = (new TasksQuery())->setFrom(10)->setLimit(9)->setNext(99)->setStatuses(['enqueued']); $this->assertEquals($data->toArray(), [ 'limit' => 9, 'next' => 99, 'from' => 10, 'statuses' => 'enqueued', diff --git a/tests/Endpoints/TasksTest.php b/tests/Endpoints/TasksTest.php index e99dd1ed..a8d37a46 100644 --- a/tests/Endpoints/TasksTest.php +++ b/tests/Endpoints/TasksTest.php @@ -109,7 +109,8 @@ public function testGetAllTasksByIndex(): void public function testGetAllTasksByIndexWithFilter(): void { - $response = $this->index->getTasks((new TasksQuery())->setStatus(['succeeded'])->setLimit(2)); + $response = $this->index->getTasks((new TasksQuery()) + ->setAfterEnqueuedAt(new \DateTime('yesterday'))->setStatuses(['succeeded'])->setLimit(2)); $firstIndex = $response->getResults()[0]['uid']; $this->assertEquals($response->getResults()[0]['status'], 'succeeded'); From 871e3bb0f9f819ca6554d6eaaa25c18e999470fd Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 16:36:11 -0300 Subject: [PATCH 06/13] Add support to finitePagination --- src/Search/SearchResult.php | 77 +++++++++++++++++---- tests/Endpoints/SearchTest.php | 121 +++++++++------------------------ tests/TestCase.php | 26 +++++++ 3 files changed, 123 insertions(+), 101 deletions(-) diff --git a/src/Search/SearchResult.php b/src/Search/SearchResult.php index 39ad3e2c..b592c9fd 100644 --- a/src/Search/SearchResult.php +++ b/src/Search/SearchResult.php @@ -16,12 +16,18 @@ class SearchResult implements \Countable, \IteratorAggregate * and its value will not be modified by the methods in this class. * Please, use `hitsCount` if you want to know the real size of the `hits` array at any time. */ - private int $estimatedTotalHits; + private ?int $estimatedTotalHits; + private ?int $hitsCount; + private ?int $offset; + private ?int $limit; + + private ?int $hitsPerPage; + private ?int $page; + private ?int $totalPages; + private ?int $totalHits; - private int $hitsCount; - private int $offset; - private int $limit; private int $processingTimeMs; + private bool $numberedPagination; private string $query; @@ -37,11 +43,24 @@ class SearchResult implements \Countable, \IteratorAggregate public function __construct(array $body) { + if (isset($body['estimatedTotalHits'])) { + $this->numberedPagination = false; + + $this->offset = $body['offset']; + $this->limit = $body['limit']; + $this->estimatedTotalHits = $body['estimatedTotalHits']; + $this->hitsCount = \count($body['hits']); + } else { + $this->numberedPagination = true; + + $this->hitsPerPage = $body['hitsPerPage']; + $this->page = $body['page']; + $this->totalPages = $body['totalPages']; + $this->totalHits = $body['totalHits']; + $this->hitsCount = $body['totalHits']; + } + $this->hits = $body['hits'] ?? []; - $this->offset = $body['offset']; - $this->limit = $body['limit']; - $this->estimatedTotalHits = $body['estimatedTotalHits']; - $this->hitsCount = \count($body['hits']); $this->processingTimeMs = $body['processingTimeMs']; $this->query = $body['query']; $this->facetDistribution = $body['facetDistribution'] ?? []; @@ -132,6 +151,26 @@ public function getQuery(): string return $this->query; } + public function getHitsPerPage(): ?int + { + return $this->hitsPerPage; + } + + public function getPage(): ?int + { + return $this->page; + } + + public function getTotalPages(): ?int + { + return $this->totalPages; + } + + public function getTotalHits(): ?int + { + return $this->totalHits; + } + /** * @return array */ @@ -152,16 +191,30 @@ public function getRaw(): array public function toArray(): array { - return [ + $arr = [ 'hits' => $this->hits, - 'offset' => $this->offset, - 'limit' => $this->limit, - 'estimatedTotalHits' => $this->estimatedTotalHits, 'hitsCount' => $this->hitsCount, 'processingTimeMs' => $this->processingTimeMs, 'query' => $this->query, 'facetDistribution' => $this->facetDistribution, ]; + + if (!$this->numberedPagination) { + $arr = array_merge($arr, [ + 'offset' => $this->offset, + 'limit' => $this->limit, + 'estimatedTotalHits' => $this->estimatedTotalHits, + ]); + } else { + $arr = array_merge($arr, [ + 'hitsPerPage' => $this->hitsPerPage, + 'page' => $this->page, + 'totalPages' => $this->totalPages, + 'totalHits' => $this->totalHits, + ]); + } + + return $arr; } public function toJSON(): string diff --git a/tests/Endpoints/SearchTest.php b/tests/Endpoints/SearchTest.php index 026d8557..27ebf886 100644 --- a/tests/Endpoints/SearchTest.php +++ b/tests/Endpoints/SearchTest.php @@ -24,11 +24,7 @@ public function testBasicSearch(): void { $response = $this->index->search('prince'); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertSame(2, $response->getEstimatedTotalHits()); $this->assertCount(2, $response->getHits()); @@ -36,34 +32,36 @@ public function testBasicSearch(): void 'raw' => true, ]); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(2, $response['estimatedTotalHits']); } + public function testBasicSearchWithFinitePagination(): void + { + $response = $this->index->search('prince', ['hitsPerPage' => 2]); + + $this->assertFinitePagination($response->toArray()); + $this->assertCount(2, $response->getHits()); + + $response = $this->index->search('prince', ['hitsPerPage' => 2], [ + 'raw' => true, + ]); + + $this->assertFinitePagination($response); + } + public function testBasicEmptySearch(): void { $response = $this->index->search(''); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertCount(7, $response->getHits()); $response = $this->index->search('', [], [ 'raw' => true, ]); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(7, $response['estimatedTotalHits']); } @@ -71,22 +69,14 @@ public function testBasicPlaceholderSearch(): void { $response = $this->index->search(null); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertCount(\count(self::DOCUMENTS), $response->getHits()); $response = $this->index->search(null, [], [ 'raw' => true, ]); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(\count(self::DOCUMENTS), $response['estimatedTotalHits']); } @@ -109,22 +99,14 @@ public function testBasicSearchIfNoPrimaryKeyAndDocumentProvided(): void $res = $emptyIndex->search('prince'); - $this->assertArrayHasKey('hits', $res->toArray()); - $this->assertArrayHasKey('offset', $res->toArray()); - $this->assertArrayHasKey('limit', $res->toArray()); - $this->assertArrayHasKey('processingTimeMs', $res->toArray()); - $this->assertArrayHasKey('query', $res->toArray()); + $this->assertEstimatedPagination($res->toArray()); $this->assertCount(0, $res->getHits()); $res = $emptyIndex->search('prince', [], [ 'raw' => true, ]); - $this->assertArrayHasKey('hits', $res); - $this->assertArrayHasKey('offset', $res); - $this->assertArrayHasKey('limit', $res); - $this->assertArrayHasKey('processingTimeMs', $res); - $this->assertArrayHasKey('query', $res); + $this->assertEstimatedPagination($res); $this->assertSame(0, $res['estimatedTotalHits']); } @@ -570,15 +552,11 @@ public function testSearchWithPhraseSearch(): void $this->assertEquals('Harry Potter and the Half-Blood Prince', $response['hits'][0]['title']); } - public function testBasicSerachWithRawSearch(): void + public function testBasicSearchWithRawSearch(): void { $response = $this->index->rawSearch('prince'); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(2, $response['estimatedTotalHits']); $this->assertCount(2, $response['hits']); $this->assertEquals('Le Petit Prince', $response['hits'][0]['title']); @@ -588,11 +566,7 @@ public function testBasicSearchWithRawOption(): void { $response = $this->index->search('prince', [], ['raw' => true]); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(2, $response['estimatedTotalHits']); $this->assertCount(2, $response['hits']); } @@ -608,11 +582,7 @@ function (array $hit): bool { return 'Le Petit Prince' === $hit['title']; } $response = $this->index->search('prince', [], $options = ['transformHits' => $keepLePetitPrinceFunc]); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertSame('Le Petit Prince', $response->getHit(0)['title']); $this->assertSame(2, $response->getEstimatedTotalHits()); $this->assertSame(1, $response->getHitsCount()); @@ -634,11 +604,7 @@ function (array $hit) { $response = $this->index->search('prince', [], ['transformHits' => $titlesToUpperCaseFunc]); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertSame(2, $response->getEstimatedTotalHits()); $this->assertSame(2, $response->getHitsCount()); $this->assertCount(2, $response->getHits()); @@ -659,11 +625,7 @@ function (array $hit): bool { return 'Le Petit Prince' === $hit['title']; } 'transformHits' => $keepLePetitPrinceFunc, ]); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(2, $response['estimatedTotalHits']); $this->assertCount(2, $response['hits']); } @@ -679,11 +641,7 @@ function (array $hit): bool { return 'Le Petit Prince' === $hit['title']; } $response = $this->index->search('prince', [], ['transformHits' => $keepLePetitPrinceFunc])->toArray(); - $this->assertArrayHasKey('hits', $response); - $this->assertArrayHasKey('offset', $response); - $this->assertArrayHasKey('limit', $response); - $this->assertArrayHasKey('processingTimeMs', $response); - $this->assertArrayHasKey('query', $response); + $this->assertEstimatedPagination($response); $this->assertSame(2, $response['estimatedTotalHits']); $this->assertCount(1, $response['hits']); $this->assertEquals('Le Petit Prince', $response['hits'][0]['title']); @@ -752,12 +710,7 @@ function (int $facetValue): bool { return 1 < $facetValue; }, ['transformFacetDistribution' => $filterAllFacets] ); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('facetDistribution', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertEquals($response->getRaw()['hits'], $response->getHits()); $this->assertNotEquals($response->getRaw()['facetDistribution'], $response->getFacetDistribution()); $this->assertCount(3, $response->getRaw()['facetDistribution']['genre']); @@ -790,12 +743,7 @@ public function testBasicSearchWithTransformFacetsDritributionOptionToMap(): voi ['transformFacetDistribution' => $facetsToUpperFunc] ); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('facetDistribution', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertEquals($response->getRaw()['hits'], $response->getHits()); $this->assertNotEquals($response->getRaw()['facetDistribution'], $response->getFacetDistribution()); $this->assertCount(3, $response->getFacetDistribution()['genre']); @@ -825,12 +773,7 @@ public function testBasicSearchWithTransformFacetsDritributionOptionToOder(): vo ['transformFacetDistribution' => $facetsToUpperFunc] ); - $this->assertArrayHasKey('hits', $response->toArray()); - $this->assertArrayHasKey('facetDistribution', $response->toArray()); - $this->assertArrayHasKey('offset', $response->toArray()); - $this->assertArrayHasKey('limit', $response->toArray()); - $this->assertArrayHasKey('processingTimeMs', $response->toArray()); - $this->assertArrayHasKey('query', $response->toArray()); + $this->assertEstimatedPagination($response->toArray()); $this->assertEquals($response->getRaw()['hits'], $response->getHits()); $this->assertEquals('adventure', array_key_first($response->getFacetDistribution()['genre'])); $this->assertEquals('romance', array_key_last($response->getFacetDistribution()['genre'])); diff --git a/tests/TestCase.php b/tests/TestCase.php index dbc70740..a8e011dc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -65,6 +65,32 @@ public function assertIsValidPromise(array $promise): void $this->assertArrayHasKey('taskUid', $promise); } + public function assertFinitePagination(array $response): void + { + $currentKeys = array_keys($response); + $validBody = ['hitsPerPage', 'totalHits', 'totalPages', 'page', 'processingTimeMs', 'query', 'hits']; + + foreach ($validBody as $value) { + $this->assertTrue( + \in_array($value, $currentKeys, true), + 'Not a valid finite pagination response, since the "'.$value.'" key is not present in: ['.implode(', ', $currentKeys).']' + ); + } + } + + public function assertEstimatedPagination(array $response): void + { + $currentKeys = array_keys($response); + $validBody = ['offset', 'limit', 'estimatedTotalHits', 'processingTimeMs', 'query', 'hits']; + + foreach ($validBody as $value) { + $this->assertTrue( + \in_array($value, $currentKeys, true), + 'Not a valid estimated pagination response, since the "'.$value.'" key is not present in: ['.implode(', ', $currentKeys).']' + ); + } + } + public function createEmptyIndex($indexName, $options = []): Indexes { $response = $this->client->createIndex($indexName, $options); From 128aa03e7164cdd6471ca6beb96da7398ec48d85 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 23:12:52 -0300 Subject: [PATCH 07/13] Create a query object CancelTasksQuery --- src/Contracts/CancelTasksQuery.php | 12 ++++++ tests/Contracts/CancelTasksQueryTest.php | 49 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/Contracts/CancelTasksQuery.php create mode 100644 tests/Contracts/CancelTasksQueryTest.php diff --git a/src/Contracts/CancelTasksQuery.php b/src/Contracts/CancelTasksQuery.php new file mode 100644 index 00000000..24b8f66b --- /dev/null +++ b/src/Contracts/CancelTasksQuery.php @@ -0,0 +1,12 @@ +setTypes(['abc', 'xyz']); + + $this->assertEquals($data->toArray(), ['types' => 'abc,xyz']); + } + + public function testSetNext(): void + { + $data = (new TasksQuery())->setNext(99); + + $this->assertEquals($data->toArray(), ['next' => 99]); + } + + public function testSetAnyDateFilter(): void + { + $date = new \DateTime(); + $data = (new TasksQuery())->setBeforeEnqueuedAt($date); + + $this->assertEquals($data->toArray(), ['beforeEnqueuedAt' => $date->format(\DateTime::RFC3339)]); + } + + public function testToArrayWithSetNextWithZero(): void + { + $data = (new TasksQuery())->setNext(0); + + $this->assertEquals($data->toArray(), ['next' => 0]); + } + + public function testToArrayWithDifferentSets(): void + { + $data = (new TasksQuery())->setFrom(10)->setLimit(9)->setNext(99)->setStatuses(['enqueued']); + + $this->assertEquals($data->toArray(), [ + 'limit' => 9, 'next' => 99, 'from' => 10, 'statuses' => 'enqueued', + ]); + } +} From d43466ef29db281c71ab7caffcf785c4c7078810 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 14:02:29 -0300 Subject: [PATCH 08/13] Add cancelTasks method to Client --- src/Endpoints/Delegates/HandlesTasks.php | 6 ++++++ src/Endpoints/Tasks.php | 9 +++++++++ tests/Endpoints/TasksTest.php | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/Endpoints/Delegates/HandlesTasks.php b/src/Endpoints/Delegates/HandlesTasks.php index 2f671ff8..ab828bdf 100644 --- a/src/Endpoints/Delegates/HandlesTasks.php +++ b/src/Endpoints/Delegates/HandlesTasks.php @@ -4,6 +4,7 @@ namespace MeiliSearch\Endpoints\Delegates; +use MeiliSearch\Contracts\CancelTasksQuery; use MeiliSearch\Contracts\TasksQuery; use MeiliSearch\Contracts\TasksResults; @@ -23,6 +24,11 @@ public function getTasks(TasksQuery $options = null): TasksResults return new TasksResults($response); } + public function cancelTasks(CancelTasksQuery $options = null): array + { + return $this->tasks->cancelTasks($options); + } + public function waitForTask($uid, int $timeoutInMs = 5000, int $intervalInMs = 50): array { return $this->tasks->waitTask($uid, $timeoutInMs, $intervalInMs); diff --git a/src/Endpoints/Tasks.php b/src/Endpoints/Tasks.php index a8185d04..2b98cd53 100644 --- a/src/Endpoints/Tasks.php +++ b/src/Endpoints/Tasks.php @@ -4,6 +4,7 @@ namespace MeiliSearch\Endpoints; +use MeiliSearch\Contracts\CancelTasksQuery; use MeiliSearch\Contracts\Endpoint; use MeiliSearch\Exceptions\TimeOutException; @@ -21,6 +22,14 @@ public function all(array $query = []): array return $this->http->get(self::PATH.'/', $query); } + public function cancelTasks(?CancelTasksQuery $options): array + { + $options = $options ?? new CancelTasksQuery(); + $response = $this->http->post('/tasks/cancel', null, $options->toArray()); + + return $response; + } + /** * @throws TimeOutException */ diff --git a/tests/Endpoints/TasksTest.php b/tests/Endpoints/TasksTest.php index a8d37a46..58db9898 100644 --- a/tests/Endpoints/TasksTest.php +++ b/tests/Endpoints/TasksTest.php @@ -4,6 +4,7 @@ namespace Tests\Endpoints; +use MeiliSearch\Contracts\CancelTasksQuery; use MeiliSearch\Contracts\TasksQuery; use MeiliSearch\Endpoints\Indexes; use MeiliSearch\Exceptions\ApiException; @@ -124,6 +125,20 @@ public function testGetAllTasksByIndexWithFilter(): void $this->assertEquals($firstIndex, $newFirstIndex); } + public function testCancelTasksWithFilter(): void + { + $date = new \DateTime('yesterday'); + $query = http_build_query(['afterEnqueuedAt' => $date->format(\DateTime::RFC3339)]); + $promise = $this->client->cancelTasks((new CancelTasksQuery())->setAfterEnqueuedAt($date)); + + $this->assertEquals($promise['type'], 'taskCancelation'); + $response = $this->client->waitForTask($promise['taskUid']); + + $this->assertEquals($response['details']['originalFilter'], '?'.$query); + $this->assertEquals($response['type'], 'taskCancelation'); + $this->assertEquals($response['status'], 'succeeded'); + } + public function testExceptionIfNoTaskIdWhenGetting(): void { $this->expectException(ApiException::class); From caa1b8a416e44486a064704ebd240fffc8ea6cb7 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 15:27:16 -0300 Subject: [PATCH 09/13] Add swapIndexes method --- src/Delegates/HandlesSystem.php | 7 +++++++ src/Endpoints/Indexes.php | 5 +++++ tests/Endpoints/IndexTest.php | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/src/Delegates/HandlesSystem.php b/src/Delegates/HandlesSystem.php index 74a5f97a..15799f65 100644 --- a/src/Delegates/HandlesSystem.php +++ b/src/Delegates/HandlesSystem.php @@ -36,4 +36,11 @@ public function generateTenantToken(string $apiKeyUid, $searchRules, ?array $opt { return $this->tenantToken->generateTenantToken($apiKeyUid, $searchRules, $options); } + + public function swapIndexes(array $indexes) + { + $options = array_map(function ($data) { return ['indexes' => $data]; }, $indexes); + + return $this->index->swapIndexes($options); + } } diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index c8988dad..b551eddc 100644 --- a/src/Endpoints/Indexes.php +++ b/src/Endpoints/Indexes.php @@ -153,6 +153,11 @@ public function delete(): array return $this->http->delete(self::PATH.'/'.$this->uid) ?? []; } + public function swapIndexes(array $indexes): array + { + return $this->http->post('/swap-indexes', $indexes); + } + // Tasks public function getTask($uid): array diff --git a/tests/Endpoints/IndexTest.php b/tests/Endpoints/IndexTest.php index edfb9ad1..ae60deba 100644 --- a/tests/Endpoints/IndexTest.php +++ b/tests/Endpoints/IndexTest.php @@ -268,6 +268,14 @@ public function testDeleteIndexes(): void $this->assertArrayHasKey('enqueuedAt', $res); } + public function testSwapIndexes(): void + { + $promise = $this->client->swapIndexes([['indexA', 'indexB'], ['indexC', 'indexD']]); + $response = $this->client->waitForTask($promise['taskUid']); + + $this->assertSame($response['details']['swaps'], [['indexes' => ['indexA', 'indexB']], ['indexes' => ['indexC', 'indexD']]]); + } + public function testParseDate(): void { $date = '2021-01-01T01:23:45.123456Z'; From 8a0fb3900daa91ed8dfa602d85fc450d22e2bd7c Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 16:43:07 -0300 Subject: [PATCH 10/13] Add docs to swapIndexes method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomas Norkūnas --- src/Endpoints/Indexes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index b551eddc..924642fa 100644 --- a/src/Endpoints/Indexes.php +++ b/src/Endpoints/Indexes.php @@ -153,6 +153,9 @@ public function delete(): array return $this->http->delete(self::PATH.'/'.$this->uid) ?? []; } + /** + * @param array{0: list, 1: list} $indexes + */ public function swapIndexes(array $indexes): array { return $this->http->post('/swap-indexes', $indexes); From 642e8b64228c5e95a1230ab47403bd6a8ac719b5 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 16:36:11 -0300 Subject: [PATCH 11/13] Add support to finitePagination --- src/Endpoints/Indexes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index 924642fa..39e6650b 100644 --- a/src/Endpoints/Indexes.php +++ b/src/Endpoints/Indexes.php @@ -154,7 +154,7 @@ public function delete(): array } /** - * @param array{0: list, 1: list} $indexes + * @param array $indexes */ public function swapIndexes(array $indexes): array { From 3e56de282e1a8dec84f84d643b91feb67f30d466 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 16:37:49 -0300 Subject: [PATCH 12/13] Add DeleteTasksQuery query object to receive data to delete tasks method --- src/Contracts/DeleteTasksQuery.php | 12 ++++++ tests/Contracts/DeleteTasksQueryTest.php | 49 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/Contracts/DeleteTasksQuery.php create mode 100644 tests/Contracts/DeleteTasksQueryTest.php diff --git a/src/Contracts/DeleteTasksQuery.php b/src/Contracts/DeleteTasksQuery.php new file mode 100644 index 00000000..d9416f6c --- /dev/null +++ b/src/Contracts/DeleteTasksQuery.php @@ -0,0 +1,12 @@ +setTypes(['abc', 'xyz']); + + $this->assertEquals($data->toArray(), ['types' => 'abc,xyz']); + } + + public function testSetNext(): void + { + $data = (new TasksQuery())->setNext(99); + + $this->assertEquals($data->toArray(), ['next' => 99]); + } + + public function testSetAnyDateFilter(): void + { + $date = new \DateTime(); + $data = (new TasksQuery())->setBeforeEnqueuedAt($date); + + $this->assertEquals($data->toArray(), ['beforeEnqueuedAt' => $date->format(\DateTime::RFC3339)]); + } + + public function testToArrayWithSetNextWithZero(): void + { + $data = (new TasksQuery())->setNext(0); + + $this->assertEquals($data->toArray(), ['next' => 0]); + } + + public function testToArrayWithDifferentSets(): void + { + $data = (new TasksQuery())->setFrom(10)->setLimit(9)->setNext(99)->setStatuses(['enqueued']); + + $this->assertEquals($data->toArray(), [ + 'limit' => 9, 'next' => 99, 'from' => 10, 'statuses' => 'enqueued', + ]); + } +} From 029ac5159a392f81aa8d6f86c8b3d268d5319c3b Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 16:40:29 -0300 Subject: [PATCH 13/13] Add deleteTasks method to Client --- src/Endpoints/Delegates/HandlesTasks.php | 6 ++++++ src/Endpoints/Tasks.php | 9 +++++++++ tests/Endpoints/IndexTest.php | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/Endpoints/Delegates/HandlesTasks.php b/src/Endpoints/Delegates/HandlesTasks.php index ab828bdf..78276fa0 100644 --- a/src/Endpoints/Delegates/HandlesTasks.php +++ b/src/Endpoints/Delegates/HandlesTasks.php @@ -5,6 +5,7 @@ namespace MeiliSearch\Endpoints\Delegates; use MeiliSearch\Contracts\CancelTasksQuery; +use MeiliSearch\Contracts\DeleteTasksQuery; use MeiliSearch\Contracts\TasksQuery; use MeiliSearch\Contracts\TasksResults; @@ -24,6 +25,11 @@ public function getTasks(TasksQuery $options = null): TasksResults return new TasksResults($response); } + public function deleteTasks(DeleteTasksQuery $options = null): array + { + return $this->tasks->deleteTasks($options); + } + public function cancelTasks(CancelTasksQuery $options = null): array { return $this->tasks->cancelTasks($options); diff --git a/src/Endpoints/Tasks.php b/src/Endpoints/Tasks.php index 2b98cd53..aa52e2e1 100644 --- a/src/Endpoints/Tasks.php +++ b/src/Endpoints/Tasks.php @@ -5,6 +5,7 @@ namespace MeiliSearch\Endpoints; use MeiliSearch\Contracts\CancelTasksQuery; +use MeiliSearch\Contracts\DeleteTasksQuery; use MeiliSearch\Contracts\Endpoint; use MeiliSearch\Exceptions\TimeOutException; @@ -30,6 +31,14 @@ public function cancelTasks(?CancelTasksQuery $options): array return $response; } + public function deleteTasks(?DeleteTasksQuery $options): array + { + $options = $options ?? new DeleteTasksQuery(); + $response = $this->http->delete(self::PATH, $options->toArray()); + + return $response; + } + /** * @throws TimeOutException */ diff --git a/tests/Endpoints/IndexTest.php b/tests/Endpoints/IndexTest.php index ae60deba..d91b5373 100644 --- a/tests/Endpoints/IndexTest.php +++ b/tests/Endpoints/IndexTest.php @@ -4,6 +4,7 @@ namespace Tests\Endpoints; +use MeiliSearch\Contracts\DeleteTasksQuery; use MeiliSearch\Contracts\TasksQuery; use MeiliSearch\Endpoints\Indexes; use MeiliSearch\Exceptions\TimeOutException; @@ -276,6 +277,15 @@ public function testSwapIndexes(): void $this->assertSame($response['details']['swaps'], [['indexes' => ['indexA', 'indexB']], ['indexes' => ['indexC', 'indexD']]]); } + public function testDeleteTasks(): void + { + $promise = $this->client->deleteTasks((new DeleteTasksQuery())->setUids([1, 2])); + $response = $this->client->waitForTask($promise['taskUid']); + + $this->assertSame($response['details']['originalFilter'], '?uids=1%2C2'); + $this->assertIsNumeric($response['details']['matchedTasks']); + } + public function testParseDate(): void { $date = '2021-01-01T01:23:45.123456Z';