Skip to content

Commit c7d6be2

Browse files
meili-bors[bot]meili-botbrunoocasali
authored
Merge #539
539: Changes related to the next Meilisearch release (v1.3.0) r=brunoocasali a=meili-bot Related to this issue: meilisearch/integration-guides#280 This PR: - gathers the changes related to the next Meilisearch release (v1.3.0) so that this package is ready when the official release is out. - should pass the tests against the [latest pre-release of Meilisearch](https://github.com/meilisearch/meilisearch/releases). - might eventually contain test failures until the Meilisearch v1.3.0 is out. ⚠️ This PR should NOT be merged until the next release of Meilisearch (v1.3.0) is out. _This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/main/resources/pre-release-week.md) purpose._ Co-authored-by: meili-bot <[email protected]> Co-authored-by: Bruno Casali <[email protected]>
2 parents 4df0edb + 49e2094 commit c7d6be2

14 files changed

+399
-2
lines changed

src/Contracts/FacetSearchQuery.php

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Contracts;
6+
7+
class FacetSearchQuery
8+
{
9+
private ?string $q = null;
10+
private ?string $matchingStrategy = null;
11+
private ?array $filter = null;
12+
private ?string $facetQuery = null;
13+
private ?string $facetName = null;
14+
15+
public function setQuery(string $q): FacetSearchQuery
16+
{
17+
$this->q = $q;
18+
19+
return $this;
20+
}
21+
22+
public function setMatchingStrategy(string $matchingStrategy): FacetSearchQuery
23+
{
24+
$this->matchingStrategy = $matchingStrategy;
25+
26+
return $this;
27+
}
28+
29+
public function setFilter(array $filter): FacetSearchQuery
30+
{
31+
$this->filter = $filter;
32+
33+
return $this;
34+
}
35+
36+
public function setFacetQuery(string $facetQuery): FacetSearchQuery
37+
{
38+
$this->facetQuery = $facetQuery;
39+
40+
return $this;
41+
}
42+
43+
public function setFacetName(string $facetName): FacetSearchQuery
44+
{
45+
$this->facetName = $facetName;
46+
47+
return $this;
48+
}
49+
50+
public function toArray(): array
51+
{
52+
return array_filter([
53+
'q' => $this->q,
54+
'matchingStrategy' => $this->matchingStrategy,
55+
'filter' => $this->filter,
56+
'facetQuery' => $this->facetQuery,
57+
'facetName' => $this->facetName,
58+
], function ($item) { return null !== $item; });
59+
}
60+
}

src/Contracts/Index/Faceting.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Contracts\Index;
6+
7+
use Meilisearch\Contracts\Data;
8+
9+
class Faceting extends Data implements \JsonSerializable
10+
{
11+
public function jsonSerialize(): object
12+
{
13+
return (object) $this->getIterator()->getArrayCopy();
14+
}
15+
}

src/Contracts/Index/Settings.php

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public function __construct(array $data = [])
1212
{
1313
$data['synonyms'] = new Synonyms($data['synonyms'] ?? []);
1414
$data['typoTolerance'] = new TypoTolerance($data['typoTolerance'] ?? []);
15+
$data['faceting'] = new Faceting($data['faceting'] ?? []);
16+
1517
parent::__construct($data);
1618
}
1719

src/Contracts/SearchQuery.php

+57
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class SearchQuery
2525
private ?int $limit;
2626
private ?int $hitsPerPage;
2727
private ?int $page;
28+
private ?array $vector;
29+
private ?array $attributesToSearchOn = null;
30+
private ?bool $showRankingScore = null;
31+
private ?bool $showRankingScoreDetails = null;
2832

2933
public function setQuery(string $q): SearchQuery
3034
{
@@ -103,6 +107,29 @@ public function setShowMatchesPosition(?bool $showMatchesPosition): SearchQuery
103107
return $this;
104108
}
105109

110+
public function setShowRankingScore(?bool $showRankingScore): SearchQuery
111+
{
112+
$this->showRankingScore = $showRankingScore;
113+
114+
return $this;
115+
}
116+
117+
/**
118+
* This is an EXPERIMENTAL feature, which may break without a major version.
119+
* It's available after Meilisearch v1.3.
120+
* To enable it properly and use ranking scoring details its required to opt-in through the /experimental-features route.
121+
*
122+
* More info: https://www.meilisearch.com/docs/reference/api/experimental-features
123+
*
124+
* @param bool $showRankingScoreDetails whether the feature is enabled or not
125+
*/
126+
public function setShowRankingScoreDetails(?bool $showRankingScoreDetails): SearchQuery
127+
{
128+
$this->showRankingScoreDetails = $showRankingScoreDetails;
129+
130+
return $this;
131+
}
132+
106133
public function setSort(array $sort): SearchQuery
107134
{
108135
$this->sort = $sort;
@@ -152,6 +179,32 @@ public function setIndexUid(string $uid): SearchQuery
152179
return $this;
153180
}
154181

182+
/**
183+
* This is an EXPERIMENTAL feature, which may break without a major version.
184+
* It's available from Meilisearch v1.3.
185+
* To enable it properly and use vector store capabilities it's required to activate it through the /experimental-features route.
186+
*
187+
* More info: https://www.meilisearch.com/docs/reference/api/experimental-features
188+
*
189+
* @param list<float|list<float>> $vector a multi-level array floats
190+
*/
191+
public function setVector(array $vector): SearchQuery
192+
{
193+
$this->vector = $vector;
194+
195+
return $this;
196+
}
197+
198+
/**
199+
* @param list<non-empty-string> $attributesToSearchOn
200+
*/
201+
public function setAttributesToSearchOn(array $attributesToSearchOn): SearchQuery
202+
{
203+
$this->attributesToSearchOn = $attributesToSearchOn;
204+
205+
return $this;
206+
}
207+
155208
public function toArray(): array
156209
{
157210
return array_filter([
@@ -173,6 +226,10 @@ public function toArray(): array
173226
'limit' => $this->limit ?? null,
174227
'hitsPerPage' => $this->hitsPerPage ?? null,
175228
'page' => $this->page ?? null,
229+
'vector' => $this->vector ?? null,
230+
'attributesToSearchOn' => $this->attributesToSearchOn,
231+
'showRankingScore' => $this->showRankingScore,
232+
'showRankingScoreDetails' => $this->showRankingScoreDetails,
176233
], function ($item) { return null !== $item; });
177234
}
178235
}

src/Contracts/TasksResults.php

+20
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,22 @@
66

77
class TasksResults extends Data
88
{
9+
/**
10+
* @var int<0, max>
11+
*/
912
private int $next;
13+
/**
14+
* @var int<0, max>
15+
*/
1016
private int $limit;
17+
/**
18+
* @var int<0, max>
19+
*/
1120
private int $from;
21+
/**
22+
* @var int<0, max>
23+
*/
24+
private int $total;
1225

1326
public function __construct(array $params)
1427
{
@@ -17,6 +30,7 @@ public function __construct(array $params)
1730
$this->from = $params['from'] ?? 0;
1831
$this->limit = $params['limit'] ?? 0;
1932
$this->next = $params['next'] ?? 0;
33+
$this->total = $params['total'] ?? 0;
2034
}
2135

2236
/**
@@ -42,13 +56,19 @@ public function getFrom(): int
4256
return $this->from;
4357
}
4458

59+
public function getTotal(): int
60+
{
61+
return $this->total;
62+
}
63+
4564
public function toArray(): array
4665
{
4766
return [
4867
'results' => $this->data,
4968
'next' => $this->next,
5069
'limit' => $this->limit,
5170
'from' => $this->from,
71+
'total' => $this->total,
5272
];
5373
}
5474

src/Endpoints/Delegates/HandlesSettings.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Meilisearch\Endpoints\Delegates;
66

7+
use Meilisearch\Contracts\Index\Faceting;
78
use Meilisearch\Contracts\Index\Synonyms;
89
use Meilisearch\Contracts\Index\TypoTolerance;
910

@@ -81,7 +82,8 @@ public function resetDisplayedAttributes(): array
8182

8283
public function getFaceting(): array
8384
{
84-
return $this->http->get(self::PATH.'/'.$this->uid.'/settings/faceting');
85+
return (new Faceting($this->http->get(self::PATH.'/'.$this->uid.'/settings/faceting')))
86+
->getIterator()->getArrayCopy();
8587
}
8688

8789
public function updateFaceting(array $faceting): array

src/Endpoints/Indexes.php

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Meilisearch\Endpoints;
66

77
use Meilisearch\Contracts\Endpoint;
8+
use Meilisearch\Contracts\FacetSearchQuery;
89
use Meilisearch\Contracts\Http;
910
use Meilisearch\Contracts\Index\Settings;
1011
use Meilisearch\Contracts\IndexesQuery;
@@ -15,6 +16,7 @@
1516
use Meilisearch\Endpoints\Delegates\HandlesSettings;
1617
use Meilisearch\Endpoints\Delegates\HandlesTasks;
1718
use Meilisearch\Exceptions\ApiException;
19+
use Meilisearch\Search\FacetSearchResult;
1820
use Meilisearch\Search\SearchResult;
1921

2022
class Indexes extends Endpoint
@@ -211,6 +213,15 @@ public function rawSearch(?string $query, array $searchParams = []): array
211213
return $result;
212214
}
213215

216+
// Facet Search
217+
218+
public function facetSearch(FacetSearchQuery $params): FacetSearchResult
219+
{
220+
$response = $this->http->post(self::PATH.'/'.$this->uid.'/facet-search', $params->toArray());
221+
222+
return new FacetSearchResult($response);
223+
}
224+
214225
// Stats
215226

216227
public function stats(): array

src/Search/FacetSearchResult.php

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Search;
6+
7+
class FacetSearchResult implements \Countable, \IteratorAggregate
8+
{
9+
/**
10+
* @var array<int, array<string, mixed>>
11+
*/
12+
private array $facetHits;
13+
private int $processingTimeMs;
14+
private ?string $facetQuery;
15+
16+
public function __construct(array $body)
17+
{
18+
$this->facetHits = $body['facetHits'] ?? [];
19+
$this->facetQuery = $body['facetQuery'];
20+
$this->processingTimeMs = $body['processingTimeMs'];
21+
}
22+
23+
/**
24+
* @return array<int, array>
25+
*/
26+
public function getFacetHits(): array
27+
{
28+
return $this->facetHits;
29+
}
30+
31+
public function getProcessingTimeMs(): int
32+
{
33+
return $this->processingTimeMs;
34+
}
35+
36+
public function toArray(): array
37+
{
38+
return [
39+
'facetHits' => $this->facetHits,
40+
'facetQuery' => $this->facetQuery,
41+
'processingTimeMs' => $this->processingTimeMs,
42+
];
43+
}
44+
45+
public function toJSON(): string
46+
{
47+
return json_encode($this->toArray(), JSON_PRETTY_PRINT);
48+
}
49+
50+
public function getIterator(): \ArrayIterator
51+
{
52+
return new \ArrayIterator($this->facetHits);
53+
}
54+
55+
public function count(): int
56+
{
57+
return \count($this->facetHits);
58+
}
59+
}

tests/Endpoints/FacetSearchTest.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Endpoints;
6+
7+
use Meilisearch\Contracts\FacetSearchQuery;
8+
use Meilisearch\Endpoints\Indexes;
9+
use Tests\TestCase;
10+
11+
final class FacetSearchTest extends TestCase
12+
{
13+
private Indexes $index;
14+
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
19+
$this->index = $this->createEmptyIndex($this->safeIndexName());
20+
$this->index->updateDocuments(self::DOCUMENTS);
21+
$promise = $this->index->updateFilterableAttributes(['genre']);
22+
$this->index->waitForTask($promise['taskUid']);
23+
}
24+
25+
public function testBasicSearchWithFilters(): void
26+
{
27+
$response = $this->index->search('prince', ['facets' => ['genre']]);
28+
29+
$this->assertSame(array_keys($response->getFacetDistribution()['genre']), [
30+
'adventure', 'fantasy',
31+
]);
32+
33+
$response = $this->index->facetSearch(
34+
(new FacetSearchQuery())
35+
->setFacetQuery('fa')
36+
->setFacetName('genre')
37+
->setQuery('prince')
38+
);
39+
40+
$this->assertSame(array_column($response->getFacetHits(), 'value'), ['fantasy']);
41+
}
42+
}

tests/Endpoints/IndexTest.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ public function testIndexGetSettings(): void
3434
);
3535
$this->assertSame([], $this->index->getFilterableAttributes());
3636
$this->assertSame(['*'], $this->index->getDisplayedAttributes());
37-
$this->assertSame(['maxValuesPerFacet' => 100], $this->index->getFaceting());
37+
$this->assertSame([
38+
'maxValuesPerFacet' => 100,
39+
'sortFacetValuesBy' => [
40+
'*' => 'alpha',
41+
],
42+
], $this->index->getFaceting());
3843
$this->assertSame(['maxTotalHits' => 1000], $this->index->getPagination());
3944
$this->assertSame(
4045
[

0 commit comments

Comments
 (0)