Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bouncy Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install via Composer (use l5 branch for Laravel 5):
    composer require fadion/bouncy:dev-l5
    
  2. Publish Config (optional but recommended):
    php artisan vendor:publish --provider="Fadion\Bouncy\BouncyServiceProvider"
    
  3. Configure Elasticsearch Connection in config/bouncy.php:
    'connections' => [
        'default' => [
            'host' => 'localhost',
            'port' => 9200,
            'scheme' => 'http',
        ],
    ],
    
  4. Extend a Model (e.g., User.php):
    use Fadion\Bouncy\Traits\Bouncy;
    
    class User extends Model
    {
        use Bouncy;
    
        protected $bouncyIndex = 'users';
        protected $bouncyType = 'user';
    }
    

First Use Case: Basic Search

// Search with query
$results = User::search('john')->get();

// Get first result
$first = User::search('john')->first();

Implementation Patterns

Core Workflows

1. Indexing Models

  • Manual Indexing:
    $user = new User(['name' => 'John Doe']);
    $user->save(); // Auto-indexed if `autoIndexOnSave` is true (default)
    
  • Bulk Indexing:
    User::reindex(); // Rebuilds entire index
    User::updateIndex($user); // Updates a single document
    User::deleteIndex($user); // Removes a document
    

2. Searching with Eloquent

  • Basic Query:
    $users = User::search('admin')->get();
    
  • Filtered Search:
    $users = User::search('admin')
                 ->filter(['age' => ['gt' => 25]])
                 ->get();
    
  • Pagination:
    $users = User::search('admin')->paginate(10);
    

3. Custom Mappings

Define mappings in config/bouncy.php or via model:

protected $bouncyMapping = [
    'properties' => [
        'name' => ['type' => 'text', 'analyzer' => 'english'],
        'email' => ['type' => 'keyword'],
    ],
];

4. Highlights and Metadata

$results = User::search('john', ['highlight' => ['fields' => ['name']]]);
$highlights = $results->first()->getHighlights();

// Get Elasticsearch metadata
$total = $results->getTotal();
$took = $results->getTook();

Integration Tips

Leveraging Existing Logic

  • Use BouncyCollection to extend Eloquent’s Collection with Elasticsearch metadata:
    $collection = User::search('query')->get();
    $collection->getTotal(); // Returns Elasticsearch total hits
    

Hybrid Queries

Combine Eloquent and Elasticsearch queries:

// Start with Eloquent, then search
$activeUsers = User::where('active', true)->search('admin')->get();

Dynamic Indexing

Use autoIndexOnSave, autoIndexOnUpdate, and autoIndexOnDelete in config/bouncy.php to control automatic indexing.

Custom Document Fields

Override getBouncyDocument() in your model:

protected function getBouncyDocument()
{
    return [
        'name' => $this->name,
        'custom_field' => $this->custom_field ?? null,
    ];
}

Gotchas and Tips

Pitfalls

  1. Deprecated Branch:

    • The l5 branch is outdated (last release 2015). Test thoroughly or fork for Laravel 8/9+ compatibility.
    • Fix: Override deprecated methods or use a wrapper like laravel-elasticsearch.
  2. Concurrency Issues:

    • Automatic indexing may cause race conditions if multiple processes update the same model.
    • Fix: Disable auto-indexing and manually trigger updateIndex() in transactions:
      DB::transaction(function () use ($user) {
          $user->update(['name' => 'New Name']);
          $user->updateIndex(); // Explicit update
      });
      
  3. Mapping Conflicts:

    • Elasticsearch mappings are immutable by default. Changing a field’s type after indexing requires a reindex.
    • Fix: Use reindex() or delete/recreate the index:
      User::deleteIndex(); // Drops the index
      User::createIndex(); // Recreates with updated mappings
      
  4. Pagination Quirks:

    • Elasticsearch’s from/size pagination can skip documents due to ties in scoring.
    • Fix: Use searchAfter for deep pagination or accept slight inaccuracies.
  5. Case Sensitivity:

    • keyword fields are case-sensitive by default. Use lowercase or normalizer for case-insensitive searches.

Debugging Tips

  1. Raw Elasticsearch Queries: Enable logging in config/bouncy.php:

    'debug' => true,
    

    Check Laravel logs for raw Elasticsearch queries.

  2. Index Existence: Verify the index exists before querying:

    if (!User::indexExists()) {
        User::createIndex();
    }
    
  3. Document Existence: Check if a document exists before deleting:

    if (User::documentExists($user->getKey())) {
        User::deleteIndex($user);
    }
    

Extension Points

  1. 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;
    });
    
  2. Event Hooks: Listen for indexing events:

    User::saved(function ($model) {
        // Custom logic after indexing
    });
    
  3. Custom Highlighting: Override getHighlights() in your model:

    public function getHighlights()
    {
        return $this->bouncyHighlights['name'] ?? [];
    }
    

Performance Tips

  1. Bulk Operations: Use chunk() for large reindexing:

    User::chunk(100, function ($users) {
        foreach ($users as $user) {
            $user->updateIndex();
        }
    });
    
  2. Index Aliases: Use Elasticsearch aliases to switch between indices without downtime:

    User::setIndexAlias('users_v2');
    
  3. Refresh Interval: Disable _refresh during bulk operations to improve speed:

    User::reindex(['refresh' => false]);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums