PHPackages                             vikram/es - 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. [Database &amp; ORM](/categories/database)
4. /
5. vikram/es

ActiveLibrary[Database &amp; ORM](/categories/database)

vikram/es
=========

Map Elasticsearch results to Eloquent models

v1.0(5y ago)2481MITPHPPHP &gt;=7.2.0

Since Sep 10Pushed 2y ago1 watchersCompare

[ Source](https://github.com/vikram0460/ElasticLaravel)[ Packagist](https://packagist.org/packages/vikram/es)[ RSS](/packages/vikram-es/feed)WikiDiscussions master Synced 4d ago

READMEChangelog (1)Dependencies (3)Versions (5)Used By (0)

Bouncy
======

[](#bouncy)

Elasticsearch is a great search engine, but it takes some work to transform its results to an easy to use dataset. Bouncy does exactly that: it maps Elasticsearch results to Eloquent models, so you can keep using the same logic with some special enhancements. In addition, it handles indexing, either manually or automatically on model creation, update or deletion.

This package was created for a personal project and it's still a work in progress. I don't expect it's API to change however.

I was inspired and most of the implementation is based on [Bouncy](https://github.com/fadion/Bouncy). Basically, it's a fork of that package. Kudos to the developer. Only fixes made to support the Elasticsearch 6.x and Laravel 5.6.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Setting Up](#setting-up)
- [Indexes](#index-and-type-name)
    - [Indexing](#indexing)
    - [Updating Indexes](#updating-indexes)
    - [Removing Indexes](#removing-indexes)
    - [Re-indexing](#re-indexing)
    - [Concurrency Control](#concurrency-control)
    - [Automatic Indexes](#automatic-indexes)
- [Mappings And Customizing Document Fields](#mappings-and-customizing-document-fields)
- [Indexing And Mapping](#indexing-and-mapping)
- [Searching](#searching)
    - [Pagination](#pagination)
    - [Limit](#limit)
    - [Results Information](#results-information)
    - [Document Information](#document-information)
    - [Highlights](#highlights)
    - [Searching Shorthands](#searching-shorthands)
- [Custom Collection](#custom-collection)
- [Elasticsearch Client Facade](#elasticsearch-client-facade)

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

[](#installation)

composer require vikram/es

For Laravel 10
--------------

[](#for-laravel-10)

composer require vikram/es:dev-L10

OR

- Add the package to your `composer.json` file and run `composer update`:

```
{
    "require": {
        "vikram/es": "^1.0",
    }
}
```

- Publish the config file by running the following command in the terminal: `php artisan vendor:publish --provider="Fadion\Bouncy\BouncyServiceProvider"`
- Edit the config files (located in `app/config/packages/bouncy/`) and set the Elasticsearch index name, server configuration, etc.

Setting Up
----------

[](#setting-up)

There's only one step to tell your models that they should use Bouncy. Just add a trait! I'll be using a fictional `Product` model for the examples.

```
use Fadion\Bouncy\BouncyTrait;

class Product extends Eloquent {

    use BouncyTrait;

    // ...other Eloquent attributes
    // or methods.
}
```

Index and Type Name
-------------------

[](#index-and-type-name)

The index can be set in the configuration file, while the type name is retrieved automatically from the model's table name. This is generally a good way to structure your documents, as you configure it once and forget about it.

When you need to set the index or type name specifically, just add the following attributes to your models:

```
class Product extends Eloquent {

    protected $indexName = 'awesome_index';
    protected $typeName = 'cool_type';

}
```

Mappings And Customizing Document Fields
----------------------------------------

[](#mappings-and-customizing-document-fields)

Mappings can be created as easily as anything you've seen until now. They are defined as a class property of a model and handled using some simple methods.

Add mapping properties to a model:

```
use Fadion\Bouncy\BouncyTrait;
use Carbon\Carbon;

class Product extends Eloquent {

    use BouncyTrait;

    protected $mappingProperties = [
        'title' => [
            'type' => 'keyword'
        ],
        'description' => [
            'type' => 'text',
            'fielddata' => true
        ],
        'created_at' => [
            'type' => 'date',
            'format' => 'yyyy-MM-dd HH:mm:ss'
        ]
    ]

    /**
     * You need to have documentFields in order to have the columns indexed
     */
    public function documentFields()
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => 'description',
            'created_at' => ($this->created_at != '') ? Carbon::parse($this->created_at)->format('Y-m-d H:i:s'): null,
        ];
    }
}
```

Indexing And Mapping
--------------------

[](#indexing-and-mapping)

After all the above steps, now you need to create the indexing for the your model then map.

```
App\Product::createIndex();
```

Put those mappings:

```
App\Product::putMapping();
```

Get mappings:

```
App\Product::getMapping();
```

Check if mappings exist:

```
if (App\Product::hasMapping()) {
    // do something
}
```

If we have any issues in the mapping, we can delete the index and create index again

```
App\Product::deleteIndex();
```

Indexing
--------

[](#indexing)

Before doing any search query, Elasticsearch needs an index to work on. What's normally a tedious task, is rendered as easy as it gets.

Index all records:

```
App\Product::all()->index();
```

Index a collection of models:

```
App\Product::where('sold', true)->get()->index();
```

Index an individual model:

```
$product = App\Product::find(10);
$product->index();
```

Collection indexes will be added in bulk, which Elasticsearch handles quite fast. However, keep in mind that indexing a big collection is an exhaustive process. Hitting the SQL database and iterating over each row needs time and resources, so try to keep the collection relatively small. You'll have to experiment with the number of data you can index at a time, depending on your server resources and configuration.

Updating Indexes
----------------

[](#updating-indexes)

Updating is the safe way, in version conflict terms, to reindex an existing document. When the model exists and any of it's attributes have changed, it's index will be updated. Otherwise, it will be added to the index as if calling the `index()` method.

Updating a model's index:

```
$product = App\Product::find(10);
$product->price = 100;
$product->updateIndex();
```

Updating a model's index using custom attributes. There are few use cases for this, as it's preferable to keep models and indexes in sync, but it's there when needed.

```
$product = App\Product::find(10);
$product->updateIndex([
    'price' => 120,
    'sold' => false
]);
```

Removing Indexes
----------------

[](#removing-indexes)

When you're done with a model's index, obviously you can remove it.

Removing the indexes of a collection:

```
App\Product::where('quantity', 'update(['price' => 110]);
// or
App\Product::where('price', 100)->delete();
```

You can still call the indexing methods manually and work the limitation. It will add an extra database query, but at least it will keep your data in sync.

```
App\Product::where('price', 100)->get()->updateIndex(['price' => 110]);
App\Product::where('price', 100)->update(['price' => 110]);
// or
App\Product::where('price', 100)->get()->removeIndex();
App\Product::where('price', 100)->delete();
```

Searching
---------

[](#searching)

Now on the real deal! Searching is where Elasticsearch shines and why you're bothering with it. Bouncy doesn't get in the way, allowing you to build any search query you can imagine in exactly the same way you do with Elasticsearch's client. This allows for great flexibility, while providing your results with a collection of Eloquent models.

An example match query:

```
$params = [
    'query' => [
        'match' => [
            'title' => 'github'
        ]
    ],
    'size' => 20
];

$products = App\Product::search($params);

foreach ($products as $product) {
    echo $product->title;
}
```

The `$params` array is exactly as Elasticsearch expects for it to build a JSON request. Nothing new here! You can easily build whatever search query you want, be it a match, multi\_match, more\_like\_this, etc.

Pagination
----------

[](#pagination)

Paginated results are important in an application and it's generally a pain with raw Elasticsearch results. Another good reason for using Bouncy! It paginates results in exactly the same way as Eloquent does, so you don't have to learn anything new.

Paginate to 15 models per page (default):

```
$products = App\Product::search($params)->paginate();
```

Paginate to an arbitrary number:

```
$products = App\Product::search($params)->paginate(30);
```

In your views, you can show pagination links exactly as you've done before:

```
$products->links();
```

Limit
-----

[](#limit)

For performance, limiting your search results should be done on the Elasticsearch parameter list with the `size` keyword. However, for easy limiting, Bouncy provides that functionality.

Limit to 50 results:

```
$products = App\Product::search($params)->limit(50);
```

Results Information
-------------------

[](#results-information)

Elasticsearch provides some information for the query, as the total number of hits or time taken. Bouncy's results collections have methods for easily accessing that information.

```
$products = App\Product::search($params);

$products->total(); // Total number of hits
$products->maxScore(); // Maximum score of the results
$products->took(); // Time in ms it took to run the query
$products->timedOut(); // Wheather the query timed out, or not.

$products->shards(); // Array of shards information
$products->shards($key); // Information on specific shard
```

Document Information
--------------------

[](#document-information)

Elasticsearch documents have some information such as score and version. You can access those data using the following methods:

```
$products = App\Product::search($params);

foreach ($products as $product) {
    $product->isDocument(); // Checks if it's an Elasticsearch document
    $product->documentScore(); // Score set in search results
    $product->documentVersion(); // Document version if present
}
```

Highlights
----------

[](#highlights)

Highlights are a nice visual feature to enhance your search results. Bouncy makes it really easy to access the highlighted fields.

```
$params = [
    'query' => [
        'match' => [
            'title' => 'github'
        ]
    ],
    'highlight' => [
        'fields' => [
            'title' => new \stdClass
        ]
    ]
];

$products = App\Product::search($params);

foreach ($products as $product) {
    echo $product->highlight('title');
}
```

The `highlight()` method will access any highlighted field with the provided name or fail silently (actually, returns false) if it doesn't find one.

Searching Shorthands
--------------------

[](#searching-shorthands)

Having flexibility and all is great, but there are occassions when you just need to run a simple match query and get the job done, without writting a full parameters array. Bouncy offers some shorthand methods for the most common search queries. They work and handle results in the same way as `search()` does, so all of the above applies to them too.

match query:

```
$products = App\Product::match($title, $query)
```

multi\_match query:

```
$products = App\Product::multiMatch(Array $fields, $query)
```

fuzzy query:

```
$products = App\Product::fuzzy($field, $value, $fuzziness = 'AUTO')
```

geoshape query:

```
$products = App\Product::geoshape($field, Array $coordinates, $type = 'envelope')
```

ids query:

```
$products = App\Product::ids(Array $values)
```

more\_like\_this query

```
$products = App\Product::moreLikeThis(Array $fields, Array $ids, $minTermFreq = 1, $percentTermsToMatch = 0.5, $minWordLength = 3)
```

Scroll
------

[](#scroll)

The Scrolling functionality of Elasticsearch is used to paginate over many documents in a bulk manner, such as exporting all the documents belonging to a single user. It is more efficient than regular search because it doesn’t need to maintain an expensive priority queue ordering the documents. [Reference](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_search_operations.html#_scrolling)

```
$params =	[
	'query' => [
		'match_all' => new \stdClass()
	]
];

// how long between scroll requests. should be small!
$scrollTime = "30s";

// how many results *per shard* you want back
$size =  10000;

$products = App\Product::scroll($params, $scrollTime, $size);
```

Suggest
-------

[](#suggest)

The suggest functionality of Elasticsearch is used to suggest when we search the text, which requires different mapping [Reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html)

```
$mappingProperties = [
						'name' => [
							'type' => 'completion'
						]
					 ];

$searchText = 'a'; // example
$params = [
                'suggest' => [
                    'name-suggest' => [
                        'prefix' => $searchText,
                        'completion' => [
                            'field' => 'name',
                            'size' => 10,
                        ]
                    ]
                ]
            ];
$productsCollection = App\Product::suggest($params);
// To get the products from the code
$products = $productsCollection['suggest']['name-suggest'][0]['options'];
```

Custom Collection
-----------------

[](#custom-collection)

If you are using a custom collection for Eloquent, you can still keep using Bouncy's methods. You'll just need to add a trait to your collection class.

```
use Illuminate\Database\Eloquent\Collection;
use Fadion\Bouncy\BouncyCollectionTrait;

class MyAwesomeCollection extends Collection {

    use BouncyCollectionTrait;

}
```

Elasticsearch Client Facade
---------------------------

[](#elasticsearch-client-facade)

Finally, when you'll need it, you can access Elasticsearch's native client in Laravel fashion using a Facade. For this step to work, you'll need to add an alias in `app/config/app.php` in the aliases array: `'Elastic' => 'Fadion\Bouncy\Facades\Elastic'`.

```
Elastic::index();
Elastic::get();
Elastic::search();
Elastic::indices()->create();

// and any other method it provides
```

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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

Unknown

Total

1

Last Release

2073d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/63489b1b4a32ee4c42ccbb1161495c387c514c9bbaaff6968ad30272b920a1b4?d=identicon)[vikram0460](/maintainers/vikram0460)

---

Top Contributors

[![vikram0460](https://avatars.githubusercontent.com/u/29538155?v=4)](https://github.com/vikram0460 "vikram0460 (33 commits)")

---

Tags

elasticsearchelasticsearch-php-extensionselasticsearch6eloquent-modelslaravellaravel-frameworklaravel56paginatephp72laravelelasticsearcheloquent

### Embed Badge

![Health badge](/badges/vikram-es/health.svg)

```
[![Health](https://phpackages.com/badges/vikram-es/health.svg)](https://phpackages.com/packages/vikram-es)
```

###  Alternatives

[pdphilip/elasticsearch

An Elasticsearch implementation of Laravel's Eloquent ORM

145360.2k4](/packages/pdphilip-elasticsearch)[sleimanx2/plastic

Plastic is an Elasticsearch ODM and mapper for Laravel. It renders the developer experience more enjoyable while using Elasticsearch by providing a fluent syntax for mapping , querying and storing eloquent models.

508141.9k1](/packages/sleimanx2-plastic)[fadion/bouncy

Map Elasticsearch results to Eloquent models

7116.3k](/packages/fadion-bouncy)

PHPackages © 2026

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