PHP code example of nramos / search-indexer
1. Go to this page and download the library: Download nramos/search-indexer library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
nramos / search-indexer example snippets
#[SearchIndex(indexName: 'biens', autoIndex: true)]
#[SearchProperty(propertyName: 'typeBien', relationProperties: [], filterable: true, sortable: true, searchable: true)]
#[SearchProperty(propertyName: 'id', isPk:true, relationProperties: [], filterable: true, sortable: true, searchable: true)]
#[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
#[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
private Collection $heatings;
$filter = (new MeiliSearchFilter())
->addFilter('status', '=', 'active')
->addFilter('rating.users', '>', 85)
->openParenthesis()
->addFilter('genres', '=', 'horror', 'OR')
->addFilter('genres', '=', 'comedy')
->closeParenthesis()
->openParenthesis()
->addFilter('genres', '=', 'horror')
->addFilter('genres', '=', 'comedy')
->closeParenthesis()
->addInFilter('role', ['admin', 'user'])
->addLocationFilter('radius', 48.8566, 2.3522, 5, 'km')
->addLocationBounding('bounding', [48.8566, 2.3522, 49.8566, 2.4522], 'km')
->addExistenceFilter('release_date')
->addExistenceFilter('overview', false);
$results = $client->search(
'houses', // Nom de l'index
'search query', // Requête de recherche
$filter, // Filtre de recherche
10, // Limite
1, // Page
['status', 'genres'] // Facettes
);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Nramos\SearchIndexer\Annotation\SearchIndex;
use Nramos\SearchIndexer\Annotation\SearchProperty;
use Nramos\SearchIndexer\Indexer\IndexableEntityInterface;
#[ORM\Entity]
#[ORM\Table(name: 'houses')]
#[SearchIndex(indexName: 'houses', autoIndex: true)]
class House implements IndexableEntityInterface
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
#[SearchProperty(propertyName: 'id',isPk: true, filterable: true, sortable: false)]
private $id;
#[ORM\Column(type: 'string')]
#[SearchProperty(propertyName: 'name', filterable: true, sortable: false)]
private ?string $name = null;
#[ORM\Column(type: 'integer')]
#[SearchProperty(propertyName: 'price', filterable: false, sortable: true)]
private $price;
#[ORM\ManyToOne(targetEntity: HouseType::class)]
#[ORM\JoinColumn(name: 'house_type_id', referencedColumnName: 'id')]
#[SearchProperty(propertyName: 'type', relationProperties: ['typeName'], filterable: true, sortable: false)]
private $houseType;
#[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
#[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
private Collection $heatings;
public function __construct()
{
$this->heatings = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getPrice(): mixed
{
return $this->price;
}
public function setPrice(mixed $price): void
{
$this->price = $price;
}
#[SearchProperty(propertyName: 'houseTypeFormated', filterable: true, sortable: false)]
public function getHouseTypeFormated(): mixed
{
return $this->houseType " - de 50 m²";
}
public function getHouseType(): mixed
{
return $this->houseType;
}
public function setHouseType(mixed $houseType): void
{
$this->houseType = $houseType;
}
public function setId(mixed $id): void
{
$this->id = $id;
}
}
#[SearchIndex(indexName: 'houses', autoIndex: false)]
namespace Nramos\SearchIndexer\Indexer;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Nramos\SearchIndexer\Annotation\SearchIndex;
use Psr\Log\LoggerInterface;
use ReflectionClass;
/**
* @see SearchIndexerSubscriberTest
*/
#[AsDoctrineListener(event: Events::postPersist, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::postUpdate, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::preRemove, priority: 0, connection: 'default')]
class SearchIndexerSubscriber
{
public function __construct(
private readonly IndexerInterface $indexer,
private readonly LoggerInterface $logger
)
{
}
public function getSubscribedEvents(): array
{
return [
Events::postPersist,
Events::postUpdate,
Events::preRemove,
];
}
/**
* @param LifecycleEventArgs<EntityManagerInterface> $args
*/
public function postPersist(LifecycleEventArgs $args): void
{
try {
$this->indexEntity($args);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
/**
* @param LifecycleEventArgs<EntityManagerInterface> $args
*/
public function preRemove(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if (!$entity instanceof IndexableEntityInterface) {
return;
}
$reflectionClass = new ReflectionClass($entity);
if ($reflectionClass->getAttributes(SearchIndex::class) && $reflectionClass->getAttributes(SearchIndex::class)[0]->newInstance()->autoIndex) {
try {
$this->indexer->remove($entity);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
}
/**
* @param LifecycleEventArgs<EntityManagerInterface> $args
*/
public function postUpdate(LifecycleEventArgs $args): void
{
try {
$this->indexEntity($args);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
/**
* @param LifecycleEventArgs<EntityManagerInterface> $args
*/
private function indexEntity(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if (!$entity instanceof IndexableEntityInterface) {
return;
}
$reflectionClass = new ReflectionClass($entity);
if ($reflectionClass->getAttributes(SearchIndex::class) && $reflectionClass->getAttributes(SearchIndex::class)[0]->newInstance()->autoIndex) {
try {
$this->indexer->index($entity);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
}
}
use Nramos\SearchIndexer\Indexer\IndexableObjects;
use Nramos\SearchIndexer\Indexer\SearchClientInterface;
class MultiSearchService
{
public function __construct(
private SearchClientInterface $client,
private IndexableObjects $indexableObjects,
) {}
public function searchAcrossIndexes(string $query, array $filters = [], int $limit = 100, int $offset = 0): array
{
$multiSearchQueries = [];
$indexableMap = [];
$indexedClasses = $this->indexableObjects->getIndexedClasses();
foreach ($indexedClasses as $className) {
if (!class_exists($className)) {
throw new \InvalidArgumentException(\sprintf("La classe %s n'existe pas.", $className));
}
$multiSearchQueries[] = [
'indexUid' => $indexName,
'q' => $query,
'facets' => [],
];
$indexableMap[$indexName] = $indexable;
}
return $this->client->multiSearch($multiSearchQueries);
}
#[SearchIndex(indexName: 'biens', autoIndex: true)]
#[SearchProperty(propertyName: 'typeBien', relationProperties: [], filterable: true, sortable: true, searchable: true)]
#[SearchProperty(propertyName: 'id', isPk:true, relationProperties: [], filterable: true, sortable: true, searchable: true)]
#[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
#[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
private Collection $heatings;
$filter = (new MeiliSearchFilter())
->addFilter('status', '=', 'active')
->addFilter('rating.users', '>', 85)
->addInFilter('role', ['admin', 'user'])
->addLocationFilter('radius', 48.8566, 2.3522, 5, 'km');
$results = $client->search(
'houses', // Index name
'search query', // Search query
$filter, // Search filter
10, // Limit
1, // Page
['status', 'genres'] // Facets
);
#[ORM\Entity]
#[ORM\Table(name: 'houses')]
#[SearchIndex(indexName: 'houses', autoIndex: true)]
class House implements IndexableEntityInterface
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
#[SearchProperty(propertyName: 'id', isPk: true, filterable: true, sortable: false)]
private $id;
#[ORM\Column(type: 'string')]
#[SearchProperty(propertyName: 'name', filterable: true, sortable: false)]
private ?string $name = null;
#[ORM\Column(type: 'integer')]
#[SearchProperty(propertyName: 'price', filterable: false, sortable: true)]
private $price;
}
#[SearchIndex(indexName: 'houses', autoIndex: false)]
#[AsDoctrineListener(event: Events::postPersist, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::postUpdate, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::preRemove, priority: 0, connection: 'default')]
class SearchIndexerSubscriber
{
public function __construct(
private readonly IndexerInterface $indexer,
private readonly LoggerInterface $logger
) {}
public function postPersist(LifecycleEventArgs $args): void
{
$this->indexEntity($args);
}
}
public function searchAcrossIndexes(string $query, array $filters = [], int $limit = 100, int $offset = 0): array
{
return $this->client->multiSearch($multiSearchQueries);
}