Skip to content

Commit

Permalink
Merge #539
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
3 people authored Jul 31, 2023
2 parents 4df0edb + 49e2094 commit c7d6be2
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 2 deletions.
60 changes: 60 additions & 0 deletions src/Contracts/FacetSearchQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts;

class FacetSearchQuery
{
private ?string $q = null;
private ?string $matchingStrategy = null;
private ?array $filter = null;
private ?string $facetQuery = null;
private ?string $facetName = null;

public function setQuery(string $q): FacetSearchQuery
{
$this->q = $q;

return $this;
}

public function setMatchingStrategy(string $matchingStrategy): FacetSearchQuery
{
$this->matchingStrategy = $matchingStrategy;

return $this;
}

public function setFilter(array $filter): FacetSearchQuery
{
$this->filter = $filter;

return $this;
}

public function setFacetQuery(string $facetQuery): FacetSearchQuery
{
$this->facetQuery = $facetQuery;

return $this;
}

public function setFacetName(string $facetName): FacetSearchQuery
{
$this->facetName = $facetName;

return $this;
}

public function toArray(): array
{
return array_filter([
'q' => $this->q,
'matchingStrategy' => $this->matchingStrategy,
'filter' => $this->filter,
'facetQuery' => $this->facetQuery,
'facetName' => $this->facetName,
], function ($item) { return null !== $item; });
}
}
15 changes: 15 additions & 0 deletions src/Contracts/Index/Faceting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts\Index;

use Meilisearch\Contracts\Data;

class Faceting extends Data implements \JsonSerializable
{
public function jsonSerialize(): object
{
return (object) $this->getIterator()->getArrayCopy();
}
}
2 changes: 2 additions & 0 deletions src/Contracts/Index/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public function __construct(array $data = [])
{
$data['synonyms'] = new Synonyms($data['synonyms'] ?? []);
$data['typoTolerance'] = new TypoTolerance($data['typoTolerance'] ?? []);
$data['faceting'] = new Faceting($data['faceting'] ?? []);

parent::__construct($data);
}

Expand Down
57 changes: 57 additions & 0 deletions src/Contracts/SearchQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class SearchQuery
private ?int $limit;
private ?int $hitsPerPage;
private ?int $page;
private ?array $vector;
private ?array $attributesToSearchOn = null;
private ?bool $showRankingScore = null;
private ?bool $showRankingScoreDetails = null;

public function setQuery(string $q): SearchQuery
{
Expand Down Expand Up @@ -103,6 +107,29 @@ public function setShowMatchesPosition(?bool $showMatchesPosition): SearchQuery
return $this;
}

public function setShowRankingScore(?bool $showRankingScore): SearchQuery
{
$this->showRankingScore = $showRankingScore;

return $this;
}

/**
* This is an EXPERIMENTAL feature, which may break without a major version.
* It's available after Meilisearch v1.3.
* To enable it properly and use ranking scoring details its required to opt-in through the /experimental-features route.
*
* More info: https://www.meilisearch.com/docs/reference/api/experimental-features
*
* @param bool $showRankingScoreDetails whether the feature is enabled or not
*/
public function setShowRankingScoreDetails(?bool $showRankingScoreDetails): SearchQuery
{
$this->showRankingScoreDetails = $showRankingScoreDetails;

return $this;
}

public function setSort(array $sort): SearchQuery
{
$this->sort = $sort;
Expand Down Expand Up @@ -152,6 +179,32 @@ public function setIndexUid(string $uid): SearchQuery
return $this;
}

/**
* This is an EXPERIMENTAL feature, which may break without a major version.
* It's available from Meilisearch v1.3.
* To enable it properly and use vector store capabilities it's required to activate it through the /experimental-features route.
*
* More info: https://www.meilisearch.com/docs/reference/api/experimental-features
*
* @param list<float|list<float>> $vector a multi-level array floats
*/
public function setVector(array $vector): SearchQuery
{
$this->vector = $vector;

return $this;
}

/**
* @param list<non-empty-string> $attributesToSearchOn
*/
public function setAttributesToSearchOn(array $attributesToSearchOn): SearchQuery
{
$this->attributesToSearchOn = $attributesToSearchOn;

return $this;
}

public function toArray(): array
{
return array_filter([
Expand All @@ -173,6 +226,10 @@ public function toArray(): array
'limit' => $this->limit ?? null,
'hitsPerPage' => $this->hitsPerPage ?? null,
'page' => $this->page ?? null,
'vector' => $this->vector ?? null,
'attributesToSearchOn' => $this->attributesToSearchOn,
'showRankingScore' => $this->showRankingScore,
'showRankingScoreDetails' => $this->showRankingScoreDetails,
], function ($item) { return null !== $item; });
}
}
20 changes: 20 additions & 0 deletions src/Contracts/TasksResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@

class TasksResults extends Data
{
/**
* @var int<0, max>
*/
private int $next;
/**
* @var int<0, max>
*/
private int $limit;
/**
* @var int<0, max>
*/
private int $from;
/**
* @var int<0, max>
*/
private int $total;

public function __construct(array $params)
{
Expand All @@ -17,6 +30,7 @@ public function __construct(array $params)
$this->from = $params['from'] ?? 0;
$this->limit = $params['limit'] ?? 0;
$this->next = $params['next'] ?? 0;
$this->total = $params['total'] ?? 0;
}

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

public function getTotal(): int
{
return $this->total;
}

public function toArray(): array
{
return [
'results' => $this->data,
'next' => $this->next,
'limit' => $this->limit,
'from' => $this->from,
'total' => $this->total,
];
}

Expand Down
4 changes: 3 additions & 1 deletion src/Endpoints/Delegates/HandlesSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Meilisearch\Endpoints\Delegates;

use Meilisearch\Contracts\Index\Faceting;
use Meilisearch\Contracts\Index\Synonyms;
use Meilisearch\Contracts\Index\TypoTolerance;

Expand Down Expand Up @@ -81,7 +82,8 @@ public function resetDisplayedAttributes(): array

public function getFaceting(): array
{
return $this->http->get(self::PATH.'/'.$this->uid.'/settings/faceting');
return (new Faceting($this->http->get(self::PATH.'/'.$this->uid.'/settings/faceting')))
->getIterator()->getArrayCopy();
}

public function updateFaceting(array $faceting): array
Expand Down
11 changes: 11 additions & 0 deletions src/Endpoints/Indexes.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Meilisearch\Endpoints;

use Meilisearch\Contracts\Endpoint;
use Meilisearch\Contracts\FacetSearchQuery;
use Meilisearch\Contracts\Http;
use Meilisearch\Contracts\Index\Settings;
use Meilisearch\Contracts\IndexesQuery;
Expand All @@ -15,6 +16,7 @@
use Meilisearch\Endpoints\Delegates\HandlesSettings;
use Meilisearch\Endpoints\Delegates\HandlesTasks;
use Meilisearch\Exceptions\ApiException;
use Meilisearch\Search\FacetSearchResult;
use Meilisearch\Search\SearchResult;

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

// Facet Search

public function facetSearch(FacetSearchQuery $params): FacetSearchResult
{
$response = $this->http->post(self::PATH.'/'.$this->uid.'/facet-search', $params->toArray());

return new FacetSearchResult($response);
}

// Stats

public function stats(): array
Expand Down
59 changes: 59 additions & 0 deletions src/Search/FacetSearchResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Search;

class FacetSearchResult implements \Countable, \IteratorAggregate
{
/**
* @var array<int, array<string, mixed>>
*/
private array $facetHits;
private int $processingTimeMs;
private ?string $facetQuery;

public function __construct(array $body)
{
$this->facetHits = $body['facetHits'] ?? [];
$this->facetQuery = $body['facetQuery'];
$this->processingTimeMs = $body['processingTimeMs'];
}

/**
* @return array<int, array>
*/
public function getFacetHits(): array
{
return $this->facetHits;
}

public function getProcessingTimeMs(): int
{
return $this->processingTimeMs;
}

public function toArray(): array
{
return [
'facetHits' => $this->facetHits,
'facetQuery' => $this->facetQuery,
'processingTimeMs' => $this->processingTimeMs,
];
}

public function toJSON(): string
{
return json_encode($this->toArray(), JSON_PRETTY_PRINT);
}

public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->facetHits);
}

public function count(): int
{
return \count($this->facetHits);
}
}
42 changes: 42 additions & 0 deletions tests/Endpoints/FacetSearchTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Tests\Endpoints;

use Meilisearch\Contracts\FacetSearchQuery;
use Meilisearch\Endpoints\Indexes;
use Tests\TestCase;

final class FacetSearchTest extends TestCase
{
private Indexes $index;

protected function setUp(): void
{
parent::setUp();

$this->index = $this->createEmptyIndex($this->safeIndexName());
$this->index->updateDocuments(self::DOCUMENTS);
$promise = $this->index->updateFilterableAttributes(['genre']);
$this->index->waitForTask($promise['taskUid']);
}

public function testBasicSearchWithFilters(): void
{
$response = $this->index->search('prince', ['facets' => ['genre']]);

$this->assertSame(array_keys($response->getFacetDistribution()['genre']), [
'adventure', 'fantasy',
]);

$response = $this->index->facetSearch(
(new FacetSearchQuery())
->setFacetQuery('fa')
->setFacetName('genre')
->setQuery('prince')
);

$this->assertSame(array_column($response->getFacetHits(), 'value'), ['fantasy']);
}
}
7 changes: 6 additions & 1 deletion tests/Endpoints/IndexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ public function testIndexGetSettings(): void
);
$this->assertSame([], $this->index->getFilterableAttributes());
$this->assertSame(['*'], $this->index->getDisplayedAttributes());
$this->assertSame(['maxValuesPerFacet' => 100], $this->index->getFaceting());
$this->assertSame([
'maxValuesPerFacet' => 100,
'sortFacetValuesBy' => [
'*' => 'alpha',
],
], $this->index->getFaceting());
$this->assertSame(['maxTotalHits' => 1000], $this->index->getPagination());
$this->assertSame(
[
Expand Down
Loading

0 comments on commit c7d6be2

Please sign in to comment.