Skip to content
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

fix(elasticsearch): elasticsearch BC #5283

Merged
merged 1 commit into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,62 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\AbstractFilterExtension::class);
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Psr\Container\ContainerInterface;

if (false) {
class AbstractFilterExtension extends \ApiPlatform\Elasticsearch\Extension\AbstractFilterExtension
/**
* Abstract class for easing the implementation of a filter extension.
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
abstract class AbstractFilterExtension implements RequestBodySearchCollectionExtensionInterface
{
private $resourceMetadataFactory;
private $filterLocator;

public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ContainerInterface $filterLocator)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->filterLocator = $filterLocator;
}

/**
* {@inheritdoc}
*/
public function applyToCollection(array $requestBody, string $resourceClass, ?string $operationName = null, array $context = []): array
{
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);

if (!$resourceFilters) {
return $requestBody;
}

$context['filters'] = $context['filters'] ?? [];
$clauseBody = [];

foreach ($resourceFilters as $filterId) {
if ($this->filterLocator->has($filterId) && is_a($filter = $this->filterLocator->get($filterId), $this->getFilterInterface())) {
$clauseBody = $filter->apply($clauseBody, $resourceClass, $operationName, $context);
}
}

if (!$clauseBody) {
return $requestBody;
}

return $this->alterRequestBody($requestBody, $clauseBody);
}

/**
* Gets the related filter interface.
*/
abstract protected function getFilterInterface(): string;

/**
* Alters the request body.
*/
abstract protected function alterRequestBody(array $requestBody, array $clauseBody): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,39 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\ConstantScoreFilterExtension::class);
use ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Filter\ConstantScoreFilterInterface;

if (false) {
final class ConstantScoreFilterExtension extends \ApiPlatform\Elasticsearch\Extension\ConstantScoreFilterExtension
/**
* Applies filter clauses while executing a constant score query.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class ConstantScoreFilterExtension extends AbstractFilterExtension
{
/**
* {@inheritdoc}
*/
protected function getFilterInterface(): string
{
return ConstantScoreFilterInterface::class;
}

/**
* {@inheritdoc}
*/
protected function alterRequestBody(array $requestBody, array $clauseBody): array
{
$requestBody['query'] = $requestBody['query'] ?? [];
$requestBody['query'] += [
'constant_score' => [
'filter' => $clauseBody,
],
];

return $requestBody;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,88 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\SortExtension::class);
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Bridge\Elasticsearch\Api\IdentifierExtractorInterface;
use ApiPlatform\Core\Bridge\Elasticsearch\Util\FieldDatatypeTrait;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

if (false) {
final class SortExtension extends \ApiPlatform\Elasticsearch\Extension\SortExtension
/**
* Applies selected sorting while querying resource collection.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class SortExtension implements RequestBodySearchCollectionExtensionInterface
{
use FieldDatatypeTrait;

private $defaultDirection;
private $identifierExtractor;
private $resourceMetadataFactory;
private $nameConverter;

public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, IdentifierExtractorInterface $identifierExtractor, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, ?NameConverterInterface $nameConverter = null, ?string $defaultDirection = null)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->identifierExtractor = $identifierExtractor;
$this->propertyMetadataFactory = $propertyMetadataFactory;
$this->resourceClassResolver = $resourceClassResolver;
$this->nameConverter = $nameConverter;
$this->defaultDirection = $defaultDirection;
}

/**
* {@inheritdoc}
*/
public function applyToCollection(array $requestBody, string $resourceClass, ?string $operationName = null, array $context = []): array
{
$orders = [];

if (
null !== ($defaultOrder = $this->resourceMetadataFactory->create($resourceClass)->getAttribute('order'))
&& \is_array($defaultOrder)
) {
foreach ($defaultOrder as $property => $direction) {
if (\is_int($property)) {
$property = $direction;
$direction = 'asc';
}

$orders[] = $this->getOrder($resourceClass, $property, $direction);
}
} elseif (null !== $this->defaultDirection) {
$orders[] = $this->getOrder(
$resourceClass,
$this->identifierExtractor->getIdentifierFromResourceClass($resourceClass),
$this->defaultDirection
);
}

if (!$orders) {
return $requestBody;
}

$requestBody['sort'] = array_merge_recursive($requestBody['sort'] ?? [], $orders);

return $requestBody;
}

private function getOrder(string $resourceClass, string $property, string $direction): array
{
$order = ['order' => strtolower($direction)];

if (null !== $nestedPath = $this->getNestedFieldPath($resourceClass, $property)) {
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass);
$order['nested'] = ['path' => $nestedPath];
}

$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass);

return [$property => $order];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,34 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\SortFilterExtension::class);
use ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Filter\SortFilterInterface;

if (false) {
final class SortFilterExtension extends \ApiPlatform\Elasticsearch\Extension\SortFilterExtension
/**
* Applies filters on the sort parameter while querying resource collection.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class SortFilterExtension extends AbstractFilterExtension
{
/**
* {@inheritdoc}
*/
protected function getFilterInterface(): string
{
return SortFilterInterface::class;
}

/**
* {@inheritdoc}
*/
protected function alterRequestBody(array $requestBody, array $clauseBody): array
{
$requestBody['sort'] = array_merge_recursive($requestBody['sort'] ?? [], $clauseBody);

return $requestBody;
}
}
22 changes: 0 additions & 22 deletions src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php

This file was deleted.

86 changes: 83 additions & 3 deletions src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,90 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\Serializer;

class_exists(\ApiPlatform\Elasticsearch\Serializer\ItemNormalizer::class);
use ApiPlatform\Core\Bridge\Elasticsearch\Api\IdentifierExtractorInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

if (false) {
final class ItemNormalizer extends \ApiPlatform\Elasticsearch\Serializer\ItemNormalizer
/**
* Item denormalizer for Elasticsearch.
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class ItemNormalizer extends ObjectNormalizer
{
public const FORMAT = 'elasticsearch';

private $identifierExtractor;

public function __construct(IdentifierExtractorInterface $identifierExtractor, ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
{
parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);

$this->identifierExtractor = $identifierExtractor;
}

/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null): bool
{
return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format);
}

/**
* {@inheritdoc}
*
* @return mixed
*/
public function denormalize($data, $class, $format = null, array $context = [])
{
if (\is_string($data['_id'] ?? null) && \is_array($data['_source'] ?? null)) {
$data = $this->populateIdentifier($data, $class)['_source'];
}

return parent::denormalize($data, $class, $format, $context);
}

/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null): bool
{
// prevent the use of lower priority normalizers (e.g. serializer.normalizer.object) for this format
return self::FORMAT === $format;
}

/**
* {@inheritdoc}
*
* @throws LogicException
*
* @return mixed
*/
public function normalize($object, $format = null, array $context = [])
{
throw new LogicException(sprintf('%s is a write-only format.', self::FORMAT));
}

/**
* Populates the resource identifier with the document identifier if not present in the original JSON document.
*/
private function populateIdentifier(array $data, string $class): array
{
$identifier = $this->identifierExtractor->getIdentifierFromResourceClass($class);
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);

if (!isset($data['_source'][$identifier])) {
$data['_source'][$identifier] = $data['_id'];
}

return $data;
}
}
2 changes: 0 additions & 2 deletions src/Elasticsearch/Serializer/DocumentNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,3 @@ private function populateIdentifier(array $data, string $class): array
return $data;
}
}

class_alias(DocumentNormalizer::class, \ApiPlatform\Core\Bridge\Elasticsearch\Serializer\DocumentNormalizer::class);
2 changes: 0 additions & 2 deletions src/Elasticsearch/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,3 @@ public function setSerializer(SerializerInterface $serializer)
$this->decorated->setSerializer($serializer);
}
}

class_alias(ItemNormalizer::class, \ApiPlatform\Core\Bridge\Elasticsearch\Serializer\ItemNormalizer::class);
16 changes: 0 additions & 16 deletions src/Symfony/Bundle/Resources/config/elasticsearch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
</service>

<service id="api_platform.elasticsearch.normalizer.item" class="ApiPlatform\Elasticsearch\Serializer\ItemNormalizer" decorates="api_platform.serializer.normalizer.item" public="false">
<argument type="service" id="api_platform.elasticsearch.normalizer.item.inner" />
</service>

<service id="api_platform.elasticsearch.normalizer.document" class="ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer" public="false">
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory.retro_compatible" />
<argument type="service" id="serializer.mapping.class_metadata_factory" />
<argument type="service" id="api_platform.elasticsearch.name_converter.inner_fields" />
<argument type="service" id="serializer.property_accessor" />
<argument type="service" id="property_info" on-invalid="ignore" />
<argument type="service" id="serializer.mapping.class_discriminator_resolver" on-invalid="ignore" />

<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
<tag name="serializer.normalizer" priority="-922" />
</service>
soyuka marked this conversation as resolved.
Show resolved Hide resolved

<service id="api_platform.elasticsearch.item_data_provider" class="ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\ItemDataProvider" public="false">
<argument type="service" id="api_platform.elasticsearch.client" />
<argument type="service" id="api_platform.elasticsearch.metadata.document.metadata_factory" />
Expand Down