Skip to content

Commit

Permalink
Add data provider for indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
loevgaard committed Aug 21, 2024
1 parent 5991095 commit 922a7ef
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 63 deletions.
28 changes: 19 additions & 9 deletions src/Config/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Setono\SyliusMeilisearchPlugin\Config;

use Psr\Container\ContainerInterface;
use Setono\SyliusMeilisearchPlugin\DataProvider\IndexableDataProviderInterface;
use Setono\SyliusMeilisearchPlugin\Document\Document;
use Setono\SyliusMeilisearchPlugin\Indexer\IndexerInterface;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;
Expand Down Expand Up @@ -53,15 +54,6 @@ public function __construct(
}
}

/**
* @psalm-suppress MixedInferredReturnType
*/
public function indexer(): IndexerInterface
{
/** @psalm-suppress MixedReturnStatement */
return $this->locator->get(IndexerInterface::class);
}

/**
* @param class-string|object $entity
*/
Expand All @@ -80,6 +72,24 @@ public function hasEntity(string|object $entity): bool
return false;
}

/**
* @psalm-suppress MixedInferredReturnType
*/
public function indexer(): IndexerInterface

Check warning on line 78 in src/Config/Index.php

View check run for this annotation

Codecov / codecov/patch

src/Config/Index.php#L78

Added line #L78 was not covered by tests
{
/** @psalm-suppress MixedReturnStatement */
return $this->locator->get(IndexerInterface::class);

Check warning on line 81 in src/Config/Index.php

View check run for this annotation

Codecov / codecov/patch

src/Config/Index.php#L81

Added line #L81 was not covered by tests
}

/**
* @psalm-suppress MixedInferredReturnType
*/
public function dataProvider(): IndexableDataProviderInterface

Check warning on line 87 in src/Config/Index.php

View check run for this annotation

Codecov / codecov/patch

src/Config/Index.php#L87

Added line #L87 was not covered by tests
{
/** @psalm-suppress MixedReturnStatement */
return $this->locator->get(IndexableDataProviderInterface::class);

Check warning on line 90 in src/Config/Index.php

View check run for this annotation

Codecov / codecov/patch

src/Config/Index.php#L90

Added line #L90 was not covered by tests
}

public function __toString(): string
{
return $this->name;
Expand Down
41 changes: 41 additions & 0 deletions src/DataProvider/DefaultIndexableDataProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\DataProvider;

use Doctrine\Persistence\ManagerRegistry;
use DoctrineBatchUtils\BatchProcessing\SelectBatchIteratorAggregate;
use Psr\EventDispatcher\EventDispatcherInterface;
use Setono\Doctrine\ORMTrait;
use Setono\SyliusMeilisearchPlugin\Config\Index;
use Setono\SyliusMeilisearchPlugin\Event\QueryBuilderForDataProvisionCreated;

final class DefaultIndexableDataProvider implements IndexableDataProviderInterface
{
use ORMTrait;

public function __construct(

Check warning on line 18 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L18

Added line #L18 was not covered by tests
ManagerRegistry $managerRegistry,
private readonly EventDispatcherInterface $eventDispatcher,
) {
$this->managerRegistry = $managerRegistry;

Check warning on line 22 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L22

Added line #L22 was not covered by tests
}

public function getIds(string $entity, Index $index): \Generator

Check warning on line 25 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L25

Added line #L25 was not covered by tests
{
$qb = $this
->getManager($entity)
->createQueryBuilder()
->select('o.id')
->from($entity, 'o')
;

Check warning on line 32 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L27-L32

Added lines #L27 - L32 were not covered by tests

$this->eventDispatcher->dispatch(new QueryBuilderForDataProvisionCreated($entity, $index, $qb));

Check warning on line 34 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L34

Added line #L34 was not covered by tests

/** @var SelectBatchIteratorAggregate<array-key, string|int> $batch */
$batch = SelectBatchIteratorAggregate::fromQuery($qb->getQuery(), 100);

Check warning on line 37 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L37

Added line #L37 was not covered by tests

yield from $batch;

Check warning on line 39 in src/DataProvider/DefaultIndexableDataProvider.php

View check run for this annotation

Codecov / codecov/patch

src/DataProvider/DefaultIndexableDataProvider.php#L39

Added line #L39 was not covered by tests
}
}
20 changes: 20 additions & 0 deletions src/DataProvider/IndexableDataProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\DataProvider;

use Setono\SyliusMeilisearchPlugin\Config\Index;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;

interface IndexableDataProviderInterface
{
/**
* Returns ids of objects of the given entity that should be indexed
*
* @param class-string<IndexableInterface> $entity
*
* @return iterable<array-key, string|int>
*/
public function getIds(string $entity, Index $index): iterable;
}
15 changes: 11 additions & 4 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Setono\SyliusMeilisearchPlugin\DependencyInjection;

use Setono\SyliusMeilisearchPlugin\DataProvider\DefaultIndexableDataProvider;
use Setono\SyliusMeilisearchPlugin\Document\Product;
use Setono\SyliusMeilisearchPlugin\Form\Type\SynonymType;
use Setono\SyliusMeilisearchPlugin\Indexer\DefaultIndexer;
Expand Down Expand Up @@ -36,20 +37,26 @@ public function getConfigTreeBuilder(): TreeBuilder
->beforeNormalization()->castToArray()->end()
->defaultValue([])
->arrayPrototype()
->addDefaultsIfNotSet()
->children()
->scalarNode('document')
->info(sprintf('The fully qualified class name for the document that maps to the index. If you are creating a product index, a good starting point is the %s', Product::class))
->cannotBeEmpty()
->isRequired()
->end()
->scalarNode('indexer')
->info(sprintf('You can set a custom indexer here. If you do not set one, the default indexer will be used. The default indexer is %s', DefaultIndexer::class))
->defaultNull()
->end()
->arrayNode('entities')
->info('The Doctrine entities that make up this index. Examples could be "App\Entity\Product\Product", "App\Entity\Taxonomy\Taxon", etc.')
->scalarPrototype()->end()
->end()
->scalarNode('data_provider')
->info('You can set a custom data provider here. If you do not set one, the default data provider will be used.')
->defaultValue(DefaultIndexableDataProvider::class)
->end()
->scalarNode('indexer')
->info(sprintf('You can set a custom indexer here. If you do not set one, the default indexer will be used. The default indexer is %s', DefaultIndexer::class))
->cannotBeEmpty()
->defaultNull()
->end()
->scalarNode('prefix')
->defaultNull()
->info('If you want to prepend a string to the index name, you can set it here. This can be useful in a development setup where each developer has their own prefix. Notice that the environment is already prefixed by default, so you do not have to prefix that.')
Expand Down
11 changes: 8 additions & 3 deletions src/DependencyInjection/SetonoSyliusMeilisearchExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Meilisearch\Client;
use Setono\SyliusMeilisearchPlugin\Config\Index;
use Setono\SyliusMeilisearchPlugin\DataMapper\DataMapperInterface;
use Setono\SyliusMeilisearchPlugin\DataProvider\IndexableDataProviderInterface;
use Setono\SyliusMeilisearchPlugin\Document\Document;
use Setono\SyliusMeilisearchPlugin\Filter\Doctrine\FilterInterface as DoctrineFilterInterface;
use Setono\SyliusMeilisearchPlugin\Filter\Object\FilterInterface as ObjectFilterInterface;
Expand Down Expand Up @@ -34,7 +35,7 @@ public function load(array $configs, ContainerBuilder $container): void
* @psalm-suppress PossiblyNullArgument
*
* @var array{
* indexes: array<string, array{document: class-string<Document>, indexer: string|null, entities: list<class-string>, prefix: string|null}>,
* indexes: array<string, array{document: class-string<Document>, entities: list<class-string>, data_provider: class-string, indexer: class-string|null, prefix: string|null}>,
* server: array{ host: string, master_key: string },
* search: array{ enabled: bool, path: string, index: string, hits_per_page: integer },
* resources: array,
Expand Down Expand Up @@ -159,7 +160,7 @@ public function prepend(ContainerBuilder $container): void
}

/**
* @param array<string, array{document: class-string<Document>, indexer: string|null, entities: list<class-string>, prefix: string|null}> $config
* @param array<string, array{document: class-string<Document>, entities: list<class-string>, data_provider: class-string, indexer: class-string|null, prefix: string|null}> $config
*/
private static function registerIndexesConfiguration(array $config, ContainerBuilder $container): void
{
Expand All @@ -178,7 +179,10 @@ private static function registerIndexesConfiguration(array $config, ContainerBui
$indexName,
$index['document'],
$index['entities'],
ServiceLocatorTagPass::register($container, [IndexerInterface::class => new Reference($indexerServiceId)]),
ServiceLocatorTagPass::register($container, [
IndexableDataProviderInterface::class => new Reference($index['data_provider']),
IndexerInterface::class => new Reference($indexerServiceId),
]),
$index['prefix'],
]));

Expand All @@ -203,6 +207,7 @@ private static function registerDefaultIndexer(ContainerBuilder $container, stri
new Reference(Client::class),
new Reference('setono_sylius_meilisearch.filter.object.composite'),
new Reference('event_dispatcher'),
new Reference('setono_sylius_meilisearch.command_bus'),
]));

return $indexerServiceId;
Expand Down
18 changes: 0 additions & 18 deletions src/Event/EntityBasedQueryBuilderForIndexingCreatedEvent.php

This file was deleted.

20 changes: 20 additions & 0 deletions src/Event/QueryBuilderForDataProvisionCreated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\Event;

use Doctrine\ORM\QueryBuilder;
use Setono\SyliusMeilisearchPlugin\Config\Index;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;

final class QueryBuilderForDataProvisionCreated
{
public function __construct(

Check warning on line 13 in src/Event/QueryBuilderForDataProvisionCreated.php

View check run for this annotation

Codecov / codecov/patch

src/Event/QueryBuilderForDataProvisionCreated.php#L13

Added line #L13 was not covered by tests
/** @var class-string<IndexableInterface> $entity */
public readonly string $entity,
public readonly Index $index,
public readonly QueryBuilder $qb,
) {
}
}
41 changes: 14 additions & 27 deletions src/Indexer/DefaultIndexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@
namespace Setono\SyliusMeilisearchPlugin\Indexer;

use Doctrine\Persistence\ManagerRegistry;
use DoctrineBatchUtils\BatchProcessing\SelectBatchIteratorAggregate;
use Meilisearch\Client;
use Psr\EventDispatcher\EventDispatcherInterface;
use Setono\Doctrine\ORMTrait;
use Setono\SyliusMeilisearchPlugin\Config\Index;
use Setono\SyliusMeilisearchPlugin\DataMapper\DataMapperInterface;
use Setono\SyliusMeilisearchPlugin\Document\Document;
use Setono\SyliusMeilisearchPlugin\Event\EntityBasedQueryBuilderForIndexingCreatedEvent;
use Setono\SyliusMeilisearchPlugin\Filter\Object\FilterInterface as ObjectFilterInterface;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;
use Setono\SyliusMeilisearchPlugin\Message\Command\IndexEntities;
use Setono\SyliusMeilisearchPlugin\Provider\IndexScope\IndexScopeProviderInterface;
use Setono\SyliusMeilisearchPlugin\Resolver\IndexName\IndexNameResolverInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Webmozart\Assert\Assert;

Expand All @@ -37,14 +36,22 @@ public function __construct(
protected readonly Client $client,
protected readonly ObjectFilterInterface $objectFilter,
protected readonly EventDispatcherInterface $eventDispatcher,
protected readonly MessageBusInterface $commandBus,
) {
$this->managerRegistry = $managerRegistry;
}

public function index(): void
{
foreach ($this->index->entities as $entity) {
$this->indexEntityClass($entity);
/** @var IndexBuffer<string|int> $buffer */
$buffer = new IndexBuffer(100, fn (array $ids) => $this->commandBus->dispatch(IndexEntities::fromIds($entity, $ids)));

Check warning on line 48 in src/Indexer/DefaultIndexer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/DefaultIndexer.php#L48

Added line #L48 was not covered by tests

foreach ($this->index->dataProvider()->getIds($entity, $this->index) as $id) {
$buffer->push($id);

Check warning on line 51 in src/Indexer/DefaultIndexer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/DefaultIndexer.php#L50-L51

Added lines #L50 - L51 were not covered by tests
}

$buffer->flush();

Check warning on line 54 in src/Indexer/DefaultIndexer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/DefaultIndexer.php#L54

Added line #L54 was not covered by tests
}
}

Expand All @@ -61,7 +68,9 @@ public function indexEntities(array $entities): void
$document = new $this->index->document();
$this->dataMapper->map($entity, $document, $indexScope);

$this->objectFilter->filter($entity, $document, $indexScope);
if (!$this->objectFilter->filter($entity, $document, $indexScope)) {
continue;

Check warning on line 72 in src/Indexer/DefaultIndexer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/DefaultIndexer.php#L71-L72

Added lines #L71 - L72 were not covered by tests
}

$documents[] = $this->normalize($document);
}
Expand All @@ -83,28 +92,6 @@ public function removeEntities(array $entities): void
}
}

/**
* @param class-string<IndexableInterface> $entity
*/
protected function indexEntityClass(string $entity): void
{
$qb = $this
->getManager($entity)
->createQueryBuilder()
->select('o')
->from($entity, 'o')
;

$this->eventDispatcher->dispatch(new EntityBasedQueryBuilderForIndexingCreatedEvent($entity, $qb));

/** @var SelectBatchIteratorAggregate<array-key, IndexableInterface> $objects */
$objects = SelectBatchIteratorAggregate::fromQuery($qb->getQuery(), 100);

foreach ($objects as $object) {
$this->indexEntity($object);
}
}

// todo move this to a service
protected function normalize(Document $document): array
{
Expand Down
47 changes: 47 additions & 0 deletions src/Indexer/IndexBuffer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusMeilisearchPlugin\Indexer;

/**
* @internal
*
* @template T
*/
final class IndexBuffer
{
private int $count = 0;

/** @var list<T> */
private array $buffer = [];

/**
* @param \Closure(list<T>):void $callback
*/
public function __construct(private readonly int $bufferSize, private readonly \Closure $callback)

Check warning on line 22 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L22

Added line #L22 was not covered by tests
{
}

/**
* @param T $item
*/
public function push(mixed $item): void

Check warning on line 29 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L29

Added line #L29 was not covered by tests
{
$this->buffer[] = $item;
++$this->count;

Check warning on line 32 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L31-L32

Added lines #L31 - L32 were not covered by tests

if ($this->count >= $this->bufferSize) {
$this->flush();

Check warning on line 35 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L34-L35

Added lines #L34 - L35 were not covered by tests
}
}

public function flush(): void

Check warning on line 39 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L39

Added line #L39 was not covered by tests
{
if ($this->count > 0) {
($this->callback)($this->buffer);
$this->buffer = [];
$this->count = 0;

Check warning on line 44 in src/Indexer/IndexBuffer.php

View check run for this annotation

Codecov / codecov/patch

src/Indexer/IndexBuffer.php#L41-L44

Added lines #L41 - L44 were not covered by tests
}
}
}
Loading

0 comments on commit 922a7ef

Please sign in to comment.