fadion/bouncy
Bouncy maps Elasticsearch search results to Laravel Eloquent models, letting you work with familiar collections while adding indexing and search enhancements. Supports manual or automatic indexing on create/update/delete, plus mappings, pagination, highlights, and client access.
l5 branch for Laravel 5):
composer require fadion/bouncy:dev-l5
php artisan vendor:publish --provider="Fadion\Bouncy\BouncyServiceProvider"
config/bouncy.php:
'connections' => [
'default' => [
'host' => 'localhost',
'port' => 9200,
'scheme' => 'http',
],
],
User.php):
use Fadion\Bouncy\Traits\Bouncy;
class User extends Model
{
use Bouncy;
protected $bouncyIndex = 'users';
protected $bouncyType = 'user';
}
// Search with query
$results = User::search('john')->get();
// Get first result
$first = User::search('john')->first();
$user = new User(['name' => 'John Doe']);
$user->save(); // Auto-indexed if `autoIndexOnSave` is true (default)
User::reindex(); // Rebuilds entire index
User::updateIndex($user); // Updates a single document
User::deleteIndex($user); // Removes a document
$users = User::search('admin')->get();
$users = User::search('admin')
->filter(['age' => ['gt' => 25]])
->get();
$users = User::search('admin')->paginate(10);
Define mappings in config/bouncy.php or via model:
protected $bouncyMapping = [
'properties' => [
'name' => ['type' => 'text', 'analyzer' => 'english'],
'email' => ['type' => 'keyword'],
],
];
$results = User::search('john', ['highlight' => ['fields' => ['name']]]);
$highlights = $results->first()->getHighlights();
// Get Elasticsearch metadata
$total = $results->getTotal();
$took = $results->getTook();
BouncyCollection to extend Eloquent’s Collection with Elasticsearch metadata:
$collection = User::search('query')->get();
$collection->getTotal(); // Returns Elasticsearch total hits
Combine Eloquent and Elasticsearch queries:
// Start with Eloquent, then search
$activeUsers = User::where('active', true)->search('admin')->get();
Use autoIndexOnSave, autoIndexOnUpdate, and autoIndexOnDelete in config/bouncy.php to control automatic indexing.
Override getBouncyDocument() in your model:
protected function getBouncyDocument()
{
return [
'name' => $this->name,
'custom_field' => $this->custom_field ?? null,
];
}
Deprecated Branch:
l5 branch is outdated (last release 2015). Test thoroughly or fork for Laravel 8/9+ compatibility.laravel-elasticsearch.Concurrency Issues:
updateIndex() in transactions:
DB::transaction(function () use ($user) {
$user->update(['name' => 'New Name']);
$user->updateIndex(); // Explicit update
});
Mapping Conflicts:
reindex() or delete/recreate the index:
User::deleteIndex(); // Drops the index
User::createIndex(); // Recreates with updated mappings
Pagination Quirks:
from/size pagination can skip documents due to ties in scoring.searchAfter for deep pagination or accept slight inaccuracies.Case Sensitivity:
keyword fields are case-sensitive by default. Use lowercase or normalizer for case-insensitive searches.Raw Elasticsearch Queries:
Enable logging in config/bouncy.php:
'debug' => true,
Check Laravel logs for raw Elasticsearch queries.
Index Existence: Verify the index exists before querying:
if (!User::indexExists()) {
User::createIndex();
}
Document Existence: Check if a document exists before deleting:
if (User::documentExists($user->getKey())) {
User::deleteIndex($user);
}
Custom Search Builder:
Extend Fadion\Bouncy\Query\Builder to add custom clauses:
namespace App\Extensions;
use Fadion\Bouncy\Query\Builder as BouncyBuilder;
class CustomBuilder extends BouncyBuilder
{
public function customFilter($field, $value)
{
$this->query['bool']['filter'][] = [
'term' => [$field => $value],
];
return $this;
}
}
Bind it in AppServiceProvider:
Bouncy::extend('custom', function () {
return CustomBuilder::class;
});
Event Hooks: Listen for indexing events:
User::saved(function ($model) {
// Custom logic after indexing
});
Custom Highlighting:
Override getHighlights() in your model:
public function getHighlights()
{
return $this->bouncyHighlights['name'] ?? [];
}
Bulk Operations:
Use chunk() for large reindexing:
User::chunk(100, function ($users) {
foreach ($users as $user) {
$user->updateIndex();
}
});
Index Aliases: Use Elasticsearch aliases to switch between indices without downtime:
User::setIndexAlias('users_v2');
Refresh Interval:
Disable _refresh during bulk operations to improve speed:
User::reindex(['refresh' => false]);
How can I help you explore Laravel packages today?