Skip to content

Commit

Permalink
Add multi search
Browse files Browse the repository at this point in the history
  • Loading branch information
norkunas committed Aug 18, 2023
1 parent 3841fb0 commit c942cbb
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 6 deletions.
240 changes: 240 additions & 0 deletions src/Contracts/SearchQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Bundle\Contracts;

use Meilisearch\Contracts\SearchQuery as EngineQuery;

class SearchQuery
{
/**
* @var class-string
*/
private string $className;
private ?string $q = null;
private ?array $filter = null;
private ?array $attributesToRetrieve = null;
private ?array $attributesToCrop = null;
private ?int $cropLength;
private ?array $attributesToHighlight = null;
private ?string $cropMarker = null;
private ?string $highlightPreTag = null;
private ?string $highlightPostTag = null;
private ?array $facets = null;
private ?bool $showMatchesPosition = null;
private ?array $sort = null;
private ?string $matchingStrategy = null;
private ?int $offset = null;
private ?int $limit = null;
private ?int $hitsPerPage = null;
private ?int $page = null;
private ?array $vector = null;
private ?array $attributesToSearchOn = null;
private ?bool $showRankingScore = null;
private ?bool $showRankingScoreDetails = null;

/**
* @param class-string $className
*/
public function __construct(string $className)
{
$this->className = $className;
}

/**
* @return class-string
*/
public function getClassName(): string
{
return $this->className;
}

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

return $this;
}

public function getQuery(): ?string
{
return $this->q;
}

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

return $this;
}

public function getFilter(): ?array
{
return $this->filter;
}

public function setAttributesToRetrieve(array $attributesToRetrieve): SearchQuery
{
$this->attributesToRetrieve = $attributesToRetrieve;

return $this;
}

public function setAttributesToCrop(array $attributesToCrop): SearchQuery
{
$this->attributesToCrop = $attributesToCrop;

return $this;
}

public function setCropLength(?int $cropLength): SearchQuery
{
$this->cropLength = $cropLength;

return $this;
}

public function setAttributesToHighlight(array $attributesToHighlight): SearchQuery
{
$this->attributesToHighlight = $attributesToHighlight;

return $this;
}

public function setCropMarker(string $cropMarker): SearchQuery
{
$this->cropMarker = $cropMarker;

return $this;
}

public function setHighlightPreTag(string $highlightPreTag): SearchQuery
{
$this->highlightPreTag = $highlightPreTag;

return $this;
}

public function setHighlightPostTag(string $highlightPostTag): SearchQuery
{
$this->highlightPostTag = $highlightPostTag;

return $this;
}

public function setFacets(array $facets): SearchQuery
{
$this->facets = $facets;

return $this;
}

public function setShowMatchesPosition(?bool $showMatchesPosition): SearchQuery
{
$this->showMatchesPosition = $showMatchesPosition;

return $this;
}

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

return $this;
}

/**
* @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;

return $this;
}

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

return $this;
}

public function setOffset(?int $offset): SearchQuery
{
$this->offset = $offset;

return $this;
}

public function setLimit(?int $limit): SearchQuery
{
$this->limit = $limit;

return $this;
}

public function setHitsPerPage(?int $hitsPerPage): SearchQuery
{
$this->hitsPerPage = $hitsPerPage;

return $this;
}

public function setPage(?int $page): SearchQuery
{
$this->page = $page;

return $this;
}

/**
* @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;
}

/**
* @internal
*/
public function toEngineQuery(string $prefix, array $indices): EngineQuery
{
$query = new EngineQuery();
foreach ($indices as $indice) {
if ($indice['class'] === $this->className) {
$query->setIndexUid("$prefix{$indice['name']}");

break;
}
}
if ($this->q !== null) {
$query->setQuery($this->q);
}

// @todo: set all data

return $query;
}
}
9 changes: 9 additions & 0 deletions src/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Meilisearch\Bundle;

use Meilisearch\Client;
use Meilisearch\Contracts\SearchQuery;
use Meilisearch\Exceptions\ApiException;

final class Engine
Expand Down Expand Up @@ -133,6 +134,14 @@ public function search(string $query, string $indexUid, array $searchParams): ar
return $this->client->index($indexUid)->rawSearch($query, $searchParams);
}

/**
* @param list<SearchQuery> $queries
*/
public function multiSearch(array $queries): array
{
return $this->client->multiSearch($queries);
}

/**
* Search the index and returns the number of results.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/SearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Meilisearch\Bundle;

use Doctrine\Persistence\ObjectManager;
use Meilisearch\Bundle\Contracts\SearchQuery;

interface SearchService
{
Expand Down Expand Up @@ -60,6 +61,13 @@ public function search(
array $searchParams = []
): array;

/**
* @param list<SearchQuery> $queries
*
* @return array<non-empty-string, list<object>>
*/
public function multiSearch(ObjectManager $objectManager, array $queries): array;

/**
* Get the raw search result.
*
Expand Down
53 changes: 53 additions & 0 deletions src/Services/MeilisearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Persistence\ObjectManager;
use Meilisearch\Bundle\Collection;
use Meilisearch\Bundle\Contracts\SearchQuery;
use Meilisearch\Bundle\Engine;
use Meilisearch\Bundle\Entity\Aggregator;
use Meilisearch\Bundle\Exception\ObjectIdNotFoundException;
Expand Down Expand Up @@ -188,6 +189,42 @@ public function search(
return $results;
}

public function multiSearch(ObjectManager $objectManager, array $queries): array
{
$prefix = $this->configuration->get('prefix');
$indices = $this->configuration->get('indices');

// $response = $this->engine->multiSearch(array_map(function (SearchQuery $query) use ($prefix, $indices) {
// $this->assertIsSearchable($query->getClassName());
//
// return $query->toEngineQuery($prefix, $indices);
// }, $queries));
$response = $this->engine->multiSearch(array_map(static fn (SearchQuery $query) => $query->toEngineQuery($prefix, $indices), $queries));
$results = [];

foreach ($response['results'] as $indexResponse) {
$indexResults = [];
$className = $this->getClassNameFromIndex($indexResponse['indexUid']);

foreach ($indexResponse['hits'] as $hit) {
if (!isset($hit[self::RESULT_KEY_OBJECTID])) {
throw new ObjectIdNotFoundException(sprintf('There is no "%s" key in the multi search "%s" result.', self::RESULT_KEY_OBJECTID, $indexResponse['indexUid']));
}

$repo = $objectManager->getRepository($className);
$entity = $repo->find($hit['objectID']);

if (null !== $entity) {
$indexResults[] = $entity;
}
}

$results[$className] = $indexResults;
}

return $results;
}

public function rawSearch(
string $className,
string $query = '',
Expand Down Expand Up @@ -346,4 +383,20 @@ private function assertIsSearchable(string $className): void
throw new Exception('Class '.$className.' is not searchable.');
}
}

/**
* @return class-string
*/
private function getClassNameFromIndex(string $index): string
{
$prefix = $this->configuration->get('prefix');

foreach ($this->configuration->get('indices') as $indice) {
if ("$prefix{$indice['name']}" === $index) {
return $indice['class'];
}
}

throw new Exception(sprintf('Cannot find searchable class for "%s" index.', $index));
}
}
Loading

0 comments on commit c942cbb

Please sign in to comment.