Weave Code
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

Zendsearch Laravel Package

handcraftedinthealps/zendsearch

Laravel-friendly integration of ZendSearch/Lucene for fast full‑text search in your app. Index Eloquent models, run queries with relevance scoring, and manage indexing via simple services/commands—ideal for lightweight search without external engines.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require handcraftedinthealps/zendsearch
    

    Add to composer.json if not auto-discovered:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "ZendSearch\\": "vendor/handcraftedinthealps/zendsearch/"
        }
    }
    

    Run composer dump-autoload.

  2. First Use Case: Basic Indexing

    use ZendSearch\Lucene\Document\Field\Field;
    use ZendSearch\Lucene\Document\Document as LuceneDocument;
    use ZendSearch\Lucene\Document\DocumentStore\Filesystem as DocumentStore;
    
    // Create a document store (default: ./data/zendsearch)
    $store = new DocumentStore();
    $index = $store->open('my_index');
    
    // Add a document
    $doc = new LuceneDocument();
    $doc->addField(Field::Text('title', 'Laravel Assessment'));
    $doc->addField(Field::UnStored('content', 'This is a test document...'));
    $index->addDocument($doc);
    $index->commit();
    
  3. First Query

    $query = new \ZendSearch\Lucene\Query\Query\Query();
    $query->addTerm('Laravel', 'title');
    $hits = $index->find($query);
    foreach ($hits as $hit) {
        echo $hit->title . "\n";
    }
    

Where to Look First

  • Documentation: Check the ZendSearch original docs (still applicable).
  • Source: vendor/handcraftedinthealps/zendsearch/src/ for core classes.
  • Laravel Integration: Look for config/zendsearch.php (if provided) or create your own config file.

Implementation Patterns

Workflows

  1. Indexing Workflow

    • Batch Indexing: Use DocumentStore to manage multiple indexes.
      $store = new DocumentStore();
      $index = $store->open('products');
      foreach ($products as $product) {
          $doc = new LuceneDocument();
          $doc->addField(Field::Text('name', $product['name']));
          $doc->addField(Field::UnStored('description', $product['desc']));
          $index->addDocument($doc);
      }
      $index->commit(); // Critical for persistence
      
    • Incremental Updates: Use addDocument() for new/updated docs and removeDocument() for deletions.
  2. Querying Workflow

    • Boolean Queries:
      $query = new \ZendSearch\Lucene\Query\Query\Query();
      $query->addTerm('php', 'tags');
      $query->addTerm('laravel', 'tags');
      $query->setMinTermFrequency(1); // Require both terms
      
    • Paginated Results:
      $hits = $index->find($query, 0, 10); // Offset, limit
      
  3. Laravel Service Provider Create a provider to bind the index store as a singleton:

    // app/Providers/ZendSearchServiceProvider.php
    public function register()
    {
        $this->app->singleton('zendsearch.store', function () {
            return new DocumentStore();
        });
    }
    

    Then inject it into controllers/services:

    public function __construct(DocumentStore $store) {
        $this->store = $store;
    }
    

Integration Tips

  • Artisan Commands: Create a command for bulk indexing:
    // app/Console/Commands/IndexProducts.php
    public function handle()
    {
        $index = $this->store->open('products');
        $index->deleteAll(); // Clear old data
        // ... add documents ...
        $index->commit();
    }
    
  • Events: Trigger events after indexing/querying for logging/analytics.
  • Caching: Cache query results if the index rarely changes:
    $cacheKey = 'search_results_' . md5($query->__toString());
    $results = Cache::remember($cacheKey, now()->addHours(1), function () use ($index, $query) {
        return $index->find($query);
    });
    

Gotchas and Tips

Pitfalls

  1. Case Sensitivity

    • Lucene is case-sensitive by default. Use Field::Text() with Field::STORE_NO for case-insensitive searches:
      $doc->addField(Field::Text('title', 'Laravel', Field::STORE_NO, Field::INDEX_ANALYZED));
      
    • Or configure an analyzer (see Analyzers).
  2. Memory Leaks

    • Always call $index->commit() after bulk operations to flush documents to disk.
    • Avoid holding large $hits collections in memory for long periods.
  3. Concurrent Writes

    • The package is not thread-safe. Use file locking or a queue (e.g., Laravel Queues) for concurrent writes:
      if (file_put_contents($index->getPath() . '.lock', '1', LOCK_EX)) {
          // Safe to write
          file_put_contents($index->getPath() . '.lock', '', LOCK_EX);
      }
      
  4. Field Limits

    • Lucene has a default field length limit (~2GB per field). For large text, use Field::UnStored() or chunk the content.

Debugging

  • Verify Index Exists:
    if (!$store->indexExists('my_index')) {
        throw new \RuntimeException('Index does not exist!');
    }
    
  • Check Document Count:
    $count = $index->count();
    
  • Inspect Index Structure: Use a tool like Luke to browse the index directory (./data/zendsearch/my_index).

Config Quirks

  • Default Path: Indexes are stored in ./data/zendsearch/ by default. Override with:
    $store = new DocumentStore('./custom/path');
    
  • Analyzers: Customize tokenization with analyzers (e.g., lowercase, stemming):
    use ZendSearch\Lucene\Analysis\Analyzer\Analyzer;
    use ZendSearch\Lucene\Analysis\Analyzer\Standard\StandardAnalyzer;
    
    $analyzer = new StandardAnalyzer();
    $field = Field::Text('content', $text, Field::STORE_NO, Field::INDEX_ANALYZED, $analyzer);
    

Extension Points

  1. Custom Field Types Extend ZendSearch\Lucene\Document\Field\AbstractField for domain-specific fields (e.g., Field::Date()).

  2. Query Parsers Use ZendSearch\Lucene\QueryParser\QueryParser for complex queries:

    $parser = new QueryParser('title');
    $query = $parser->parse('laravel OR php');
    
  3. Laravel Eloquent Integration Create a trait to auto-index models:

    trait Searchable {
        public static function bootSearchable()
        {
            static::saved(function ($model) {
                $index = app('zendsearch.store')->open('models');
                $doc = new LuceneDocument();
                $doc->addField(Field::Text('class', get_class($model)));
                $doc->addField(Field::Text('id', $model->id));
                // ... other fields ...
                $index->addDocument($doc);
                $index->commit();
            });
        }
    }
    
  4. High Availability For production, consider:

    • Replication: Copy the index directory to a backup server.
    • Read Replicas: Serve read queries from a secondary index.
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui