PHPackages                             adeb6600/bouncy - 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. adeb6600/bouncy

ActiveLibrary

adeb6600/bouncy
===============

Map Elasticsearch results to Eloquent models

24PHP

Since Dec 7Pushed 10y ago2 watchersCompare

[ Source](https://github.com/adeb6600/Bouncy)[ Packagist](https://packagist.org/packages/adeb6600/bouncy)[ RSS](/packages/adeb6600-bouncy/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (2)Used By (0)

> To use it in Laravel 5, please see the `l5` branch.

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 [Elasticquent](https://github.com/adamfairholm/Elasticquent/). Basically, it's a rewritten fork of that package. Kudos to the developers.

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](#mappings)
- [Searching](#searching)
    - [Pagination](#pagination)
    - [Limit](#limit)
    - [Results Information](#results-information)
    - [Document Information](#document-information)
    - [Highlights](#highlights)
    - [Searching Shorthands](#searching-shorthands)
- [Customizing Document Fields](#customizing-document-fields)
- [Custom Collection](#custom-collection)
- [Elasticsearch Client Facade](#elasticsearch-client-facade)

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

[](#installation)

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

```
{
    "require": {
        "fadion/bouncy": "dev-master"
    }
}
```

- Add the service provider to your `app/config/app.php` file, inside the `providers` array: `'Fadion\Bouncy\BouncyServiceProvider'`
- Publish the config file by running the following command in the terminal: `php artisan config:publish fadion/bouncy`
- 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';

}
```

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:

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

Index a collection of models:

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

Index an individual model:

```
$product = 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 = 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 = 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:

```
Product::where('quantity', 'update(['price' => 110]);
// or
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.

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

Mappings
--------

[](#mappings)

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;

class Product extends Eloquent {

    use BouncyTrait;

    protected $mappingProperties = [
        'title' => [
            'type' => 'string',
            'store' => true
        ],
        'description' => [
            'type' => 'string',
            'index' => 'analyzed'
        ]
    ]

}
```

Put those mappings:

```
Product::putMapping();
```

Get mappings:

```
Product::getMapping();
```

Delete mappings:

```
Product::deleteMapping();
```

Rebuild (delete and put again) mappings:

```
Product::rebuildMapping();
```

Check if mappings exist:

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

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 = 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 = Product::search($params)->paginate();
```

Paginate to an arbitrary number:

```
$products = 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 = 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 = 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 = 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 = 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 = Product::match($title, $query)
```

multi\_match query:

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

fuzzy query:

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

geoshape query:

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

ids query:

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

more\_like\_this query

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

Customizing Document Fields
---------------------------

[](#customizing-document-fields)

Bouncy will use your model's attributes while indexing and that should be fine for most cases. However, if you want to have control over the Elasticsearch documents structure, you can customize the fields by adding a `$documentFields` method to your model:

```
use Fadion\Bouncy\BouncyTrait;

class Product extends Eloquent {

    use BouncyTrait;

    public function documentFields()
    {
        return [
            'id' => $this->id,
            'price' => $this->price,
            'rating' => 'perfect'
        ];
    };

}
```

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

21

—

LowBetter than 19% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 82.8% 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/50a5986ea3e8bf6ac9e9d564349985eb547a0c86469c220e6872aeb016bb9e96?d=identicon)[adeb6600](/maintainers/adeb6600)

---

Top Contributors

[![fadion](https://avatars.githubusercontent.com/u/374519?v=4)](https://github.com/fadion "fadion (24 commits)")[![lixinqi](https://avatars.githubusercontent.com/u/688197?v=4)](https://github.com/lixinqi "lixinqi (3 commits)")[![adeb6600](https://avatars.githubusercontent.com/u/1121388?v=4)](https://github.com/adeb6600 "adeb6600 (2 commits)")

### Embed Badge

![Health badge](/badges/adeb6600-bouncy/health.svg)

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

PHPackages © 2026

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