Skip to content

Add multi search #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (null !== $this->q) {
$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));
Comment on lines +197 to +201
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

Comment on lines +197 to +201

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// $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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a common thing in the bundle, no? I'm wondering if this could be reused somewhere else.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I'd leave that for later :)

{
$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