PHPackages                             ensi/laravel-elastic-query - 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. ensi/laravel-elastic-query

ActiveLibrary

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

laravel elastic query

8.2.12(2mo ago)919.8k↓41.7%7[2 issues](https://github.com/ensi-platform/laravel-elastic-query/issues)[1 PRs](https://github.com/ensi-platform/laravel-elastic-query/pulls)1MITPHPPHP ^8.1CI passing

Since Jul 21Pushed 2mo ago2 watchersCompare

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

READMEChangelog (10)Dependencies (22)Versions (152)Used By (1)

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

[](#laravel-elastic-query)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b61dd76c0e48ded36e9606e4d4e8f44638051e53c8c050cfce2c3433dd8d2a53/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f656e73692f6c61726176656c2d656c61737469632d71756572792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ensi/laravel-elastic-query)[![Tests](https://github.com/ensi-platform/laravel-elastic-query/actions/workflows/run-tests.yml/badge.svg?branch=v8)](https://github.com/ensi-platform/laravel-elastic-query/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/9413622e42fa7c36e56e3b068d835d6bc322cbdba762a1580b38f1c7e90573a8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f656e73692f6c61726176656c2d656c61737469632d71756572792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ensi/laravel-elastic-query)

Working with Elasticsearch in an Eloquent-like fashion.

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

[](#installation)

You can install the package via composer:

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

Publish config file like this:

```
php artisan vendor:publish --provider="Ensi\LaravelElasticQuery\ElasticQueryServiceProvider"
```

Set `ELASTICSEARCH_HOSTS` in your `.env` file. `,` can be used as a delimeter.

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

[](#version-compatibility)

Laravel Elastic QueryLaravelPHPElasticsearch^0.1.0^8.0^8.07.\*^0.2.0^8.0^8.07.\*^0.3.0^8.0^8.07.\*^0.3.2^8.0 || ^9.0^8.07.\*^7.x ([see details](https://github.com/ensi-platform/laravel-elastic-query/tree/v7))^9.0 || ^10.0 || ^11.0^8.17.\*^8.0.0^8.0 || ^9.0^8.08.\*^8.0.13^8.0 || ^9.0 || ^10.0^8.08.\*^8.0.23^8.0 || ^9.0 || ^10.0 || ^11.0^8.08.\*^8.1.0^9.0 || ^10.0 || ^11.0^8.18.\*^8.2.3^9.0 || ^10.0 || ^11.0 || ^12.0^8.18.\*Basic usage
-----------

[](#basic-usage)

Let's create and index class. It's someting like Eloquent model.

```
use Ensi\LaravelElasticQuery\ElasticIndex;

class ProductsIndex extends ElasticIndex
{
    protected string $name = 'test_products';
    protected string $tiebreaker = 'product_id';
}
```

You should set a unique in document attribute name in `$tiebreaker`. It is used as an additional sort in `search_after`

Now we can get some documents

```
$searchQuery = ProductsIndex::query();

$hits = $searchQuery
             ->where('rating', '>=', 5)
             ->whereDoesntHave('offers', fn(BoolQuery $query) => $query->where('seller_id', 10)->where('active', false))
             ->sortBy('rating', 'desc')
             ->sortByNested('offers', fn(SortableQuery $query) => $query->where('active', true)->sortBy('price', mode: 'min'))
             ->take(25)
             ->get();
```

### Filtering

[](#filtering)

```
$searchQuery->where('field', 'value');
$searchQuery->where('field', '>', 'value'); // supported operators: `=` `!=` `>` `=` `whereNot('field', 'value'); // equals `where('field', '!=', 'value')`
```

```
$searchQuery->whereIn('field', ['value1', 'value2']);
$searchQuery->whereNotIn('field', ['value1', 'value2']);
```

```
$searchQuery->whereNull('field');
$searchQuery->whereNotNull('field');
```

```
$searchQuery->whereHas('nested_field', fn(BoolQuery $subQuery) => $subQuery->where('field_in_nested', 'value'));
$searchQuery->whereDoesntHave(
    'nested_field',
    function (BoolQuery $subQuery) {
        $subQuery->whereHas('nested_field', fn(BoolQuery $subQuery2) => $subQuery2->whereNot('field', 'value'));
    }
);
```

`nested_field` must have `nested` type. Subqueries cannot use fields of main document only subdocument.

### Full text search

[](#full-text-search)

```
$searchQuery->whereMatch('field_one', 'query string');
$searchQuery->whereMultiMatch(['field_one^3', 'field_two'], 'query string', MatchType::MOST_FIELDS);
$searchQuery->whereMultiMatch([], 'query string');  // search by all text fields
```

`field_one` and `field_two` must be of text type. If no type is given, the `MatchType::BEST_FIELDS` is used.

### Sorting

[](#sorting)

```
$searchQuery->sortBy('field', SortOrder::DESC, SortMode::MAX, MissingValuesMode::FIRST); // field is from main document
$searchQuery->sortByNested(
    'nested_field',
    fn(SortableQuery $subQuery) => $subQuery->where('field_in_nested', 'value')->sortBy('field')
);
```

Second attribute is a direction. It supports `asc` and `desc` values. Defaults to `asc`.
Third attribute - sorting type. List of supporting types: `min, max, avg, sum, median`. Defaults to `min`.

There are also dedicated sort methods for each sort type.

```
$searchQuery->minSortBy('field', 'asc');
$searchQuery->maxSortBy('field', 'asc');
$searchQuery->avgSortBy('field', 'asc');
$searchQuery->sumSortBy('field', 'asc');
$searchQuery->medianSortBy('field', 'asc');
```

### Pinned query

[](#pinned-query)

Promotes selected documents to rank higher than those matching a given query. This feature is typically used to guide searchers to curated documents that are promoted over and above any "organic" matches for a search. The promoted or "pinned" documents are identified using the document IDs stored in the \_id field.

```
$searchQuery->pinned(['doc-3', 'doc-1', 'doc-2']);
```

### Pagination

[](#pagination)

#### Offset Pagination

[](#offset-pagination)

```
$page = $searchQuery->paginate(15, 45);
```

Offset pagination returns total documents count as `total` and current position as `size/offset`.

#### Cursor pagination

[](#cursor-pagination)

```
$page = $searchQuery->cursorPaginate(10);
$pageNext = $searchQuery->cursorPaginate(10, $page->next);
```

`current`, `next`, `previous` is returned in this case instead of `total`, `size` and `offset`. You can check Laravel docs for more info about cursor pagination.

Aggregation
-----------

[](#aggregation)

Aggregaction queries can be created like this

```
$aggQuery = ProductsIndex::aggregate();

/** @var \Illuminate\Support\Collection $aggs */
$aggs = $aggQuery
            ->where('active', true)
            ->terms('codes', 'code')
            ->count('product_count', 'product_id')
            ->nested(
                'offers',
                fn(AggregationsBuilder $builder) => $builder->where('seller_id', 10)->minmax('price', 'price')
            );

```

Type of `$aggs->price` is `MinMax`. Type of `$aggs->codes` is `BucketCollection`. Aggregate names must be unique for whole query.

### Aggregate types

[](#aggregate-types)

Get all variants of attribute values:

```
$aggQuery->terms('agg_name', 'field', 25);
```

Get min and max attribute values. E.g for date:

```
$aggQuery->minmax('agg_name', 'field');
```

Get count unique attribute values:

```
$aggQuery->count('agg_name', 'field');
```

Aggregation plays nice with nested documents.

```
$aggQuery->nested('nested_field', function (AggregationsBuilder $builder) {
    $builder->terms('name', 'field_in_nested');
});
```

There is also a special virtual `composite` aggregate on the root level. You can set special conditions using it.

```
$aggQuery->composite(function (AggregationsBuilder $builder) {
    $builder->where('field', 'value')
        ->whereHas('nested_field', fn(BoolQuery $query) => $query->where('field_in_nested', 'value2'))
        ->terms('field1', 'agg_name1')
        ->minmax('field2', 'agg_name2');
});
```

Suggesting
----------

[](#suggesting)

Suggest queries can be created like this

```
$sugQuery = ProductsIndex::suggest();

/** @var \Illuminate\Support\Collection $suggests */
$suggests = $sugQuery->phrase('suggestName', 'name.trigram')
    ->text('glves')
    ->size(1)
    ->shardSize(3)
    ->get();

```

### Global suggest text

[](#global-suggest-text)

User can set global text like this

```
$sugQuery = ProductsIndex::suggest()->text('glves');

$sugQuery->phrase('suggestName1', 'name.trigram')->size(1)->shardSize(3);

$sugQuery->phrase('suggestName2', 'name.trigram');

/** @var \Illuminate\Support\Collection $suggests */
$suggests = $sugQuery->get();

```

### Suggester types

[](#suggester-types)

Term suggester:

```
$aggQuery->term('suggestName', 'name.trigram')->text('glves')->...->get();
```

Phrase Suggester:

```
$aggQuery->phrase('suggestName', 'name.trigram')->text('glves')->...->get();
```

Additional methods
------------------

[](#additional-methods)

```
$index = new ProductsIndex();

$index->isCreated(); // Check if index are created
$index->create(); // Create index with structure from settings() method
$index->bulk(); // Send bulk request
$index->get(); // Send get request
$index->documentDelete(); // Send documentDelete request
$index->deleteByQuery(); // Send deleteByQuery request
$index->termvectors(); // Send termvectors request

$index->catIndices();
$index->indicesDelete();
$index->indicesRefresh();
$index->indicesReloadSearchAnalyzers();
```

Query Log
---------

[](#query-log)

Just like Eloquent ElasticQuery has its own query log, but you need to enable it manually Each message contains `indexName`, `query` and `timestamp`

```
ElasticQuery::enableQueryLog();

/** @var \Illuminate\Support\Collection|Ensi\LaravelElasticQuery\Debug\QueryLogRecord[] $records */
$records = ElasticQuery::getQueryLog();

ElasticQuery::disableQueryLog();
```

Environment Variables
---------------------

[](#environment-variables)

Below see the environment variables that you can configure with the default values, Hosts should be comma seperated string of hosts with protocol prefix and port suffix, e.g. `http://localhost:9200,http://localhost:9201`

```
 ELASTICSEARCH_HOSTS=https://localhost:9200'
 ELASTICSEARCH_RETRIES=2
 ELASTICSEARCH_USERNAME=admin
 ELASTICSEARCH_PASSWORD=admin
 ELASTICSEARCH_SSL_VERIFICATION=true,
```

Async Usage
-----------

[](#async-usage)

All methods can return a `Promise`.
To enable this, you will need to add `http_async_client` to your config and then execute `ElasticSearch::getClient()->setAsync(true).`
To disable: `ElasticQuery::getClient()->setAsync(false)`.

For example:

laravel-elastic-query.php:

```
return [
    'connection' => [

        // ..

        'http_async_client' => [HttpClientOptionsBuilder::class, 'getAsyncClient'],
    ],
];
```

HttpClientOptionsBuilder:

```
use Http\Adapter\Guzzle7\Client as GuzzleAdapter;
use Http\Client\HttpAsyncClient;

class HttpClientOptionsBuilder
{
    public static function getAsyncClient(): HttpAsyncClient
    {
        return GuzzleAdapter::createWithConfig([]);
    }
}
```

Action:

```
use Ensi\LaravelElasticQuery\ElasticQuery;

ElasticQuery::getClient()->setAsync(true);

// With async
$promises = [
    'key1' => FirstIndex::query()->get(),
    'key2' => FirstIndex::suggest()->paginate(/* ... */),
];

$results = [];
foreach ($promises as $key => $promise) {
    $results[$key] = $promise->wait();
}

$firstResponse = $results['key1'];

ElasticQuery::getClient()->setAsync(false);

// Without async
$firstResponse = FirstIndex::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

58

—

FairBetter than 98% of packages

Maintenance82

Actively maintained with recent releases

Popularity34

Limited adoption so far

Community24

Small or concentrated contributor base

Maturity78

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 ~12 days

Recently: every ~4 days

Total

137

Last Release

66d ago

Major Versions

7.2.7 → 8.2.72026-02-22

7.2.8 → 8.2.82026-02-22

7.2.9 → 8.2.92026-02-23

7.2.10 → 8.2.112026-02-25

7.2.11 → 8.2.122026-03-13

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

[![MsNatali](https://avatars.githubusercontent.com/u/8089373?v=4)](https://github.com/MsNatali "MsNatali (91 commits)")[![DimionX](https://avatars.githubusercontent.com/u/7352966?v=4)](https://github.com/DimionX "DimionX (41 commits)")[![zix2](https://avatars.githubusercontent.com/u/51262118?v=4)](https://github.com/zix2 "zix2 (30 commits)")[![fedorf4](https://avatars.githubusercontent.com/u/79131391?v=4)](https://github.com/fedorf4 "fedorf4 (17 commits)")[![arrilot](https://avatars.githubusercontent.com/u/2826480?v=4)](https://github.com/arrilot "arrilot (13 commits)")[![egmanoylin](https://avatars.githubusercontent.com/u/77325193?v=4)](https://github.com/egmanoylin "egmanoylin (10 commits)")[![setopt](https://avatars.githubusercontent.com/u/38655841?v=4)](https://github.com/setopt "setopt (5 commits)")[![valerialukinykh](https://avatars.githubusercontent.com/u/123940772?v=4)](https://github.com/valerialukinykh "valerialukinykh (3 commits)")[![gkarkavin](https://avatars.githubusercontent.com/u/119850859?v=4)](https://github.com/gkarkavin "gkarkavin (2 commits)")[![SiriusV](https://avatars.githubusercontent.com/u/25031126?v=4)](https://github.com/SiriusV "SiriusV (2 commits)")[![C0rTeZ13](https://avatars.githubusercontent.com/u/120840631?v=4)](https://github.com/C0rTeZ13 "C0rTeZ13 (1 commits)")[![koopaTro0pa](https://avatars.githubusercontent.com/u/60116849?v=4)](https://github.com/koopaTro0pa "koopaTro0pa (1 commits)")[![SadDone](https://avatars.githubusercontent.com/u/47665715?v=4)](https://github.com/SadDone "SadDone (1 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[bagisto/bagisto

Bagisto Laravel E-Commerce

26.2k161.6k7](/packages/bagisto-bagisto)[unopim/unopim

UnoPim Laravel PIM

9.4k1.8k](/packages/unopim-unopim)[jeroen-g/explorer

Next-gen Elasticsearch driver for Laravel Scout.

397612.3k](/packages/jeroen-g-explorer)[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11120.2M21](/packages/anourvalar-eloquent-serialize)[veewee/xml

XML without worries

1835.9M29](/packages/veewee-xml)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135192.6k5](/packages/statamic-rad-pack-runway)

PHPackages © 2026

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