PHPackages                             ensi/laravel-elastic-query-specification - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. ensi/laravel-elastic-query-specification

ActiveLibrary[Search &amp; Filtering](/categories/search)

ensi/laravel-elastic-query-specification
========================================

laravel elastic query specification

8.1.4(1mo ago)511.5k↑78.8%2MITPHPPHP ^8.1CI passing

Since Sep 28Pushed 1mo ago2 watchersCompare

[ Source](https://github.com/ensi-platform/laravel-elastic-query-specification)[ Packagist](https://packagist.org/packages/ensi/laravel-elastic-query-specification)[ RSS](/packages/ensi-laravel-elastic-query-specification/feed)WikiDiscussions v8 Synced today

READMEChangelog (10)Dependencies (10)Versions (37)Used By (0)

Laravel Elastic Query Specification
===================================

[](#laravel-elastic-query-specification)

[![Latest Version on Packagist](https://camo.githubusercontent.com/45d0ae2237e80f315d0fe0a971f48eb22452e4d5528f377e51e1baa8e33cb4dc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f656e73692f6c61726176656c2d656c61737469632d71756572792d73706563696669636174696f6e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ensi/laravel-elastic-query-specification)[![Tests](https://github.com/ensi-platform/laravel-elastic-query-specification/actions/workflows/run-tests.yml/badge.svg?branch=v8)](https://github.com/ensi-platform/laravel-elastic-query-specification/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/a30dd3280c7a5ee98a424dc43613f77445a092ea139193655d4680497993943c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f656e73692f6c61726176656c2d656c61737469632d71756572792d73706563696669636174696f6e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ensi/laravel-elastic-query-specification)

Extension for [ensi/laravel-elastic-query](https://github.com/ensi-platform/laravel-elastic-query/) to describe queries in a declarative way.

Installation
------------

[](#installation)

1. Install [ensi/laravel-elastic-query](https://github.com/ensi-platform/laravel-elastic-query/)
2. Install this package via composer:

```
composer require ensi/laravel-elastic-query-specification
```

Version Compatibility
---------------------

[](#version-compatibility)

Laravel Elastic Query SpecificationLaravelPHPLaravel Elastic Query^0.1.0^8.0^8.0^0.2.0^0.2.0^8.0^8.0^0.3.0^0.2.3^8.0 || ^9.0^8.0^0.3.0^0.3.0^8.0 || ^9.0^8.0^0.3.0^7.x ([see details](https://github.com/ensi-platform/laravel-elastic-query-specification/tree/v7))^9.0 || ^10.0 || ^11.0^8.1^7.1.0^8.0.0^8.0 || ^9.0^8.0^8.0^8.0.2^8.0 || ^9.0 || ^10.0^8.0^8.0^8.0.3^8.0 || ^9.0 || ^10.0 || ^11.0^8.0^8.0.23^8.1.0^9.0 || ^10.0 || ^11.0^8.1^8.1.0^8.1.3^9.0 || ^10.0 || ^11.0 || ^12.0^8.1^8.2.3Basic usage
-----------

[](#basic-usage)

All types of declarative queries are based on the specification. It contains definitions of available filters, sorts, and aggregates.

```
use Ensi\LaravelElasticQuerySpecification\Agregating\AllowedAggregate;
use Ensi\LaravelElasticQuerySpecification\Filtering\AllowedFilter;
use Ensi\LaravelElasticQuerySpecification\Sorting\AllowedSort;
use Ensi\LaravelElasticQuerySpecification\Specification\CompositeSpecification;
use Ensi\LaravelElasticQuerySpecification\Specification\Specification;

class ProductSpecification extends CompositeSpecification
{
    public function __construct()
    {
        parent::__construct();

        $this->allowedFilters([
            'package',
            'active',
            AllowedFilter::exact('cashback', 'cashback.active')->default(true)
        ]);

        $this->allowedSorts(['name', 'rating']);

        $this->allowedAggregates([
            'package',
            AllowedAggregate::minmax('rating')
        ]);

        $this->allowedFacets([
            'package'
        ]);

        $this->whereNotNull('package');

        $this->nested('offers', function (Specification $spec) {
            $spec->allowedFilters(['seller_id', 'active']);

            $spec->allowedAggregates([
                'seller_id',
                AllowedAggregate::minmax('price')
            ]);

            $spec->allowedSorts([
                AllowedSort::field('price')->byMin()
            ]);

            $spec->allowedFacets([
                AllowedFacet::terms('seller_id')
            ]);
        });
    }
}
```

Here are examples of queries for this specification.

```
{
 "sort": ["+price", "-rating"],
 "filter": {
    "active": true,
    "seller_id": 10
 }
}
```

```
{
 "aggregate": ["price", "rating"],
 "filter": {
    "package": "bottle",
    "seller_id": 10
 }
}
```

```
{
  "facet": ["seller_id", "package"],
  "filter": {
    "package": "bottle",
    "seller_id": [10, 20, 50, 90]
  }
}
```

The `nested` method adds specifications for nested documents. The names of filters, aggregates, and sorts are exported from them to the global scope without adding any prefixes. It is acceptable to have the same names for filters, but not for other components.

```
$this->nested('nested_field', function (Specification $spec) { ... })
$this->nested('nested_field', new SomeSpecificationImpl());
```

In the specifications for nested documents, only the fields of these documents can be used.

It is acceptable to add several specifications for the same `nested` field.

The `where*` constraints allow you to set additional program selection conditions that cannot be changed by the client. The constraints specified in the root specification are always applied. Constraints in the nested specifications are only used as additions to filters, aggregates, or sorts added to the query. For example, if there is no active filter in the nested specification, then the constraints from this specification will not fall into the filters section of the Elasticsearch query.

The `allowedFilters` method determines the filters available to the client. Each filter must contain a unique name within the specification. At the same time, in the root and nested specifications or in different nested specifications, the names may be repeated. All filters with the same name will be filled with one value from the query parameters.

In addition to the name of the filter itself, you can separately specify the name of the field in the index for which it is applied, and the default value.

```
$this->allowedFilters([AllowedFilter::exact('name', 'field')->default(500)]);

// the following statements are equivalent
$this->allowedFilters(['name']);
$this->allowedFilters([AllowedFilter::exact('name', 'name')]);
```

Types of filters

```
AllowedFilter::exact('name', 'field');          // The field value is checked for equality to one of the specified
AllowedFilter::exists('name', 'field');         // There is a check that the field is in the document and has a non-zero value.
AllowedFilter::greater('name', 'field');        // The field value must be greater than the specified one.
AllowedFilter::greaterOrEqual('name', 'field'); // The field value must be greater than or equal to the specified one.
AllowedFilter::less('name', 'field');           // The field value must be less than the specified one.
AllowedFilter::lessOrEqual('name', 'field');    // The field value must be less than or equal to the specified one.
AllowedFilter::match('name', 'field');          // Full text search in the field
AllowedFilter::multiMatch('name', ['field1^3', 'field2']);    // Full text search in the fields
```

The sorts available to the client are added by the `allowedSorts` method. The sorting direction is set in its name. The sign `+` or the absence of a sign corresponds to the ascending order, `-` to the descending order. By default, ascending sorting is used with the minimum selection, if there are several values in the field.

```
$this->allowedSorts([AllowedSort::field('name', 'field')]);

// the following statements are equivalent
$this->allowedSorts(['name']);
$this->allowedSorts([AllowedSort::field('+name', 'name')]);
$this->allowedSorts([AllowedSort::field('+name', 'name')->byMin()]);

// set the sorting mode
$this->allowedSorts([AllowedSort::field('name', 'field')->byMin()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byMax()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byAvg()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->bySum()]);
$this->allowedSorts([AllowedSort::field('name', 'field')->byMedian()]);
```

To sort from a nested specification, all constraints and active filters from the same specification are taken into account.

Aggregates are declared with the `allowedAggregates` method. The client specifies in the query parameters a list of names of aggregates, the results of which he expects in the response.

```
$this->allowedAggregates([AllowedAggregate::terms('name', 'field')]);

// the following statements are equivalent
$this->allowedAggregates(['name']);
$this->allowedAggregates([AllowedAggregate::terms('name', 'name')]);
```

Types of aggregates

```
AllowedAggregate::terms('name', 'field');   // Get all variants of attribute values
AllowedAggregate::minmax('name', 'field');  // Get min and max attribute values
```

Aggregates from nested specifications are added to the Elasticsearch query with all constraints and active filters.

You can use the `allowedFacets` method to define facets. Each facet requires an aggregate and one or more filters. You can use both the existing aggregate

```
AllowedFacet::fromAggregate('name', 'filter');
```

and the aggregate created by the facet itself

```
AllowedFacet::terms('name', 'filter');
AllowedFacet::minmax('name', ['filter1', 'filter2']);
```

Filters are registered in the specification separately. Only their names are passed to facet creation methods.

During the calculation of the available values for each facet, all set filters are applied except those associated with this facet.

Search for documents
--------------------

[](#search-for-documents)

```
use Ensi\LaravelElasticQuerySpecification\SearchQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsSearchQuery extends SearchQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::query(), new ProductSpecification(), $request);
    }
}
```

```
class ProductsController
{
    // ...
    public function index(ProductsSearchQuery $query)
    {
        return ProductResource::collection($query->get());
    }
}
```

Calculation of summary indicators
---------------------------------

[](#calculation-of-summary-indicators)

```
use Ensi\LaravelElasticQuerySpecification\AggregateQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsAggregateQuery extends AggregateQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::aggregate(), new ProductSpecification(), $request);
    }
}
```

```
class ProductsController
{
    // ...
    public function totals(ProductsAggregateQuery $query)
    {
        return new ProductAggregateResource($query->get());
    }
}
```

Determining the available facet values
--------------------------------------

[](#determining-the-available-facet-values)

```
use Ensi\LaravelElasticQuerySpecification\FacetQueryBuilder;
use Ensi\LaravelElasticQuerySpecification\QueryBuilderRequest;

class ProductsFacetsQuery extends FacetQueryBuilder
{
    public function __construct(QueryBuilderRequest $request)
    {
        parent::__construct(ProductsIndex::aggregate(), new ProductSpecification(), $request);
    }
}
```

```
class ProductsController
{
    // ...
    public function facets(ProductsFacetsQuery $query)
    {
        return new ProductFacetsResource($query->get());
    }
}
```

Elasticsearch 7 and 8 support.
------------------------------

[](#elasticsearch-7-and-8-support)

Due to the incompatibility of clients for Elasticsearch 7 and 8, separate releases will be created for these versions. Development for each version is carried out in the corresponding branch.

To make changes to version 7, you need to create a task branch based on v7 and make a pull request to it. For version 8 it is similar, but based on the v8 branch.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.

### Testing

[](#testing)

1. composer install
2. start Elasticsearch in your preferred way
3. if you need change `ELASTICSEARCH_HOSTS`, copy `phpunit.xml.dist` to `phpunit.xml` and fill value
4. composer test

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](.github/SECURITY.md) on how to report security vulnerabilities.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

55

—

FairBetter than 98% of packages

Maintenance90

Actively maintained with recent releases

Popularity30

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~49 days

Recently: every ~0 days

Total

33

Last Release

53d ago

Major Versions

7.0.1 → 8.0.12023-08-22

7.0.2 → 8.0.22023-09-14

7.1.0 → 8.1.02024-06-26

7.1.1 → 8.1.22025-03-13

7.1.3 → 8.1.32026-01-30

PHP version history (2 changes)0.1.0PHP ^8.0

7.1.0PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8089373?v=4)[Наталия](/maintainers/MsNatali)[@MsNatali](https://github.com/MsNatali)

![](https://avatars.githubusercontent.com/u/7352966?v=4)[Andrey](/maintainers/dimionx)[@DimionX](https://github.com/DimionX)

---

Top Contributors

[![zix2](https://avatars.githubusercontent.com/u/51262118?v=4)](https://github.com/zix2 "zix2 (28 commits)")[![MsNatali](https://avatars.githubusercontent.com/u/8089373?v=4)](https://github.com/MsNatali "MsNatali (18 commits)")[![arrilot](https://avatars.githubusercontent.com/u/2826480?v=4)](https://github.com/arrilot "arrilot (6 commits)")[![setopt](https://avatars.githubusercontent.com/u/38655841?v=4)](https://github.com/setopt "setopt (4 commits)")[![gkarkavin](https://avatars.githubusercontent.com/u/119850859?v=4)](https://github.com/gkarkavin "gkarkavin (3 commits)")[![valerialukinykh](https://avatars.githubusercontent.com/u/123940772?v=4)](https://github.com/valerialukinykh "valerialukinykh (3 commits)")[![DimionX](https://avatars.githubusercontent.com/u/7352966?v=4)](https://github.com/DimionX "DimionX (2 commits)")[![C0rTeZ13](https://avatars.githubusercontent.com/u/120840631?v=4)](https://github.com/C0rTeZ13 "C0rTeZ13 (2 commits)")[![koopaTro0pa](https://avatars.githubusercontent.com/u/60116849?v=4)](https://github.com/koopaTro0pa "koopaTro0pa (1 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ensi-laravel-elastic-query-specification/health.svg)

```
[![Health](https://phpackages.com/badges/ensi-laravel-elastic-query-specification/health.svg)](https://phpackages.com/packages/ensi-laravel-elastic-query-specification)
```

###  Alternatives

[abbasudo/laravel-purity

elegant way to add filter and sort in laravel

514313.6k1](/packages/abbasudo-laravel-purity)[mehradsadeghi/laravel-filter-querystring

Filter your queries based on url query string parameters like a breeze.

170115.4k](/packages/mehradsadeghi-laravel-filter-querystring)[vizra/vizra-adk

Vizra Agent Development Kit - A comprehensive Laravel package for building intelligent AI agents.

28621.7k](/packages/vizra-vizra-adk)[pos-lifestyle/laravel-nova-date-range-filter

A Laravel Nova date range filter.

16178.1k](/packages/pos-lifestyle-laravel-nova-date-range-filter)[millat/laravel-hooks

The WordPress filter, action system in Laravel

5715.1k](/packages/millat-laravel-hooks)[romangrinev/laravel-opensearch-engine

Custom Laravel Scout OpenSearch Engine

1319.8k](/packages/romangrinev-laravel-opensearch-engine)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
