From 2de53f4b919c9d6681ba04d6b79c2f3cbbf0d3e6 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 15:27:16 -0300 Subject: [PATCH 1/3] 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 74a5f97a2..15799f65c 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 c8988dad9..b551eddcf 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 edfb9ad1e..ae60deba2 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 2164a5ce8fd7587bacecb78954693b16bea10998 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Wed, 23 Nov 2022 16:43:07 -0300 Subject: [PATCH 2/3] 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 b551eddcf..924642faf 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 cbf4c161899f307faef786a99c258231f5c08c40 Mon Sep 17 00:00:00 2001 From: Bruno Casali Date: Mon, 21 Nov 2022 16:36:11 -0300 Subject: [PATCH 3/3] Add support to finitePagination --- src/Endpoints/Indexes.php | 2 +- src/Search/SearchResult.php | 77 +++++++++++++++++---- tests/Endpoints/SearchTest.php | 121 +++++++++------------------------ tests/TestCase.php | 26 +++++++ 4 files changed, 124 insertions(+), 102 deletions(-) diff --git a/src/Endpoints/Indexes.php b/src/Endpoints/Indexes.php index 924642faf..39e6650b9 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 { diff --git a/src/Search/SearchResult.php b/src/Search/SearchResult.php index 39ad3e2c5..b592c9fda 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 026d85577..27ebf8865 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 dbc707401..a8e011dc6 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);