Skip to content

Elasticsearch - how to create a custom filter #1926

@signor-pedro

Description

@signor-pedro

Hi, I was trying to create a custom filter for geo-distance query.

I had a very hard time understanding what to do - this section is documented rather poorly.

I ended up doing something like this:

use ApiPlatform\Elasticsearch\Extension\RequestBodySearchCollectionExtensionInterface;
use ApiPlatform\Elasticsearch\Filter\AbstractFilter;
use ApiPlatform\Metadata\Operation;

class GeoDistanceFilter extends AbstractFilter implements RequestBodySearchCollectionExtensionInterface
{
    public function apply(array $clauseBody, string $resourceClass, ?Operation $operation = null, array $context = []): array
    {
        throw new \LogicException(
            'This method is never called, but for some reason dictated by the AbstractFilter type.'
        );
    }

    public function applyToCollection(array $requestBody, string $resourceClass, ?Operation $operation = null, array $context = []): array
    {
        // Check for the presence of geo-search parameters
        $filters = $context['filters'] ?? [];

        if (!isset($filters['latitute'], $filters['longitude'], $filters['distance'])) {
            return $requestBody;
        }

        // ... construct and apply the geo-distance query ...

        return $requestBody;
    }

    public function getDescription(string $resourceClass): array
    {
        return [
            'latitute' => [
                'property' => 'lat',
                'type' => 'float',
                'required' => false,
                'description' => 'Latitude for geo-distance search.',
                'openapi' => [
                    'example' => 48.8566,
                ],
            ],
            'longitude' => [
                'property' => 'lon',
                'type' => 'float',
                'required' => false,
                'description' => 'Longitude for geo-distance search.',
                'openapi' => [
                    'example' => 2.3522,
                ],
            ],
            'distance' => [
                'property' => 'distance',
                'type' => 'string',
                'required' => false,
                'description' => 'The maximum distance from the point (lat, lon). Example: "30km".',
                'openapi' => [
                    'example' => '30km',
                ],
            ],
        ];
    }
}

I had a hard time understanding some of these concepts:

  • what is the point of the apply method if it never gets called on runtime - some sort of a BC/adapter thing?
  • what is a reasonable code example of a custom filter? The docs only show an example of an extension:
class AndOperatorFilterExtension implements RequestBodySearchCollectionExtensionInterface
{
    public function applyToCollection(array $requestBody, string $resourceClass, ?Operation $operation = null, array $context = []): array;
    {
        $requestBody['query'] = $requestBody['query'] ?? [];
        $andQuery = [
            'query' => $context['filters']['fullName'],
            'operator' => 'and',
        ];
        
        $requestBody['query']['constant_score']['filter']['bool']['must'][0]['match']['full_name'] = $andQuery;
        
        return $requestBody;
    }
}

However this example would not work by itself, as it does not implement AbstractFilter nor FilterInterface. A more complete example together with an example of the annotation such as

#[ApiFilter(CustomGeoDistanceFilter::class, properties: ['locations.point' => 'geo_distance'])]
class SearchOffer
{
}

would be very appreciated.

Thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions