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

Elasticsearch Query Builder Laravel Package

spatie/elasticsearch-query-builder

Lightweight fluent PHP query builder for Elasticsearch. Build and execute searches with an ergonomic Builder API: set index, add queries, aggregations, sorting, and filters, then run search() against the official Elasticsearch PHP client.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/elasticsearch-query-builder
    

    Ensure you have the official Elasticsearch PHP client (elasticsearch/elasticsearch) installed.

  2. Basic Query:

    use Spatie\ElasticsearchQueryBuilder\Builder;
    use Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery;
    
    $client = Elastic\Elasticsearch\ClientBuilder::create()->build();
    $results = (new Builder($client))
        ->index('products')
        ->addQuery(MatchQuery::create('name', 'laptop'))
        ->search();
    
  3. First Use Case:

    • Search for documents matching a keyword in a specific field (e.g., title).
    • Example: Search for blog posts with "Laravel" in the title:
      $posts = (new Builder($client))
          ->index('posts')
          ->addQuery(MatchQuery::create('title', 'Laravel'))
          ->search();
      

Key Entry Points

  • Builder class: Central class for constructing queries.
  • Queries namespace: Predefined query types (e.g., MatchQuery, BoolQuery).
  • Aggregations namespace: Aggregation functions (e.g., MaxAggregation, AvgAggregation).

Implementation Patterns

Common Workflows

1. Basic Search with Filtering

$results = (new Builder($client))
    ->index('users')
    ->addQuery(MatchQuery::create('name', 'John'))
    ->addFilter(TermQuery::create('status', 'active')) // Filter (not scored)
    ->search();

2. Nested Queries (Bool Queries)

Combine multiple conditions with logical operators:

$boolQuery = BoolQuery::create()
    ->should(MatchQuery::create('title', 'Laravel'))
    ->must(TermQuery::create('category', 'tutorial'))
    ->mustNot(TermQuery::create('published_at', null));

$results = (new Builder($client))
    ->index('posts')
    ->addQuery($boolQuery)
    ->search();

3. Aggregations for Analytics

Group and analyze data:

$results = (new Builder($client))
    ->index('orders')
    ->addAggregation(AvgAggregation::create('amount'))
    ->addAggregation(TermsAggregation::create('status'))
    ->search();

4. Pagination and Sorting

$results = (new Builder($client))
    ->index('products')
    ->addQuery(MatchAllQuery::create())
    ->sort('price', 'asc')
    ->from(0)
    ->size(10)
    ->search();

5. Dynamic Field Mapping

Use keyword or text fields dynamically:

$query = MatchQuery::create('name.keyword', 'John Doe'); // Exact match

Integration Tips

Laravel Integration

  • Service Provider: Bind the Builder to the container for dependency injection:

    $this->app->bind(Builder::class, function ($app) {
        return new Builder($app->make(Elastic\Elasticsearch\Client::class));
    });
    
  • Eloquent-like Usage: Create a facade or helper to abstract the builder:

    class Elasticsearch {
        public static function search(string $index, array $queries = []): array {
            return (new Builder(app('elasticsearch')))->index($index)
                ->addQueries($queries)
                ->search();
        }
    }
    

Testing

  • Mock the Elasticsearch client for unit tests:
    $mockClient = Mockery::mock(Elastic\Elasticsearch\Client::class);
    $builder = new Builder($mockClient);
    

Performance

  • Bulk Operations: Use the bulk method for large datasets:

    $builder->bulk($operations);
    
  • Scroll API: For deep pagination, use the scroll method:

    $scroll = $builder->scroll('1m')->search();
    

Gotchas and Tips

Pitfalls

  1. Field Mapping Mismatches:

    • Elasticsearch requires fields to exist in the mapping. Use exists queries to check:
      $builder->addFilter(ExistsQuery::create('field_name'));
      
    • Fix: Ensure your index mappings align with query fields.
  2. Fuzziness Overuse:

    • Fuzzy matching (fuzziness) can degrade performance. Limit to 2 or 3 for most use cases.
  3. Aggregation Limits:

    • Elasticsearch has a default bucket size limit (10,000). Adjust with:
      TermsAggregation::create('field')->size(1000);
      
  4. Boolean Query Logic:

    • must = AND, should = OR, mustNot = NOT. Misuse can lead to unexpected results.
  5. Scroll vs. Search After:

    • Use scroll for large datasets (but consumes cluster resources). Prefer search_after for pagination:
      $builder->searchAfter($lastSortValues);
      

Debugging

  1. Raw Query Inspection: Access the underlying query before execution:

    $query = $builder->getQuery();
    dd($query); // Inspect the raw Elasticsearch query
    
  2. Error Handling: Wrap searches in try-catch:

    try {
        $results = $builder->search();
    } catch (\Elastic\Elasticsearch\Exception\ClientResponseException $e) {
        report($e);
        return back()->withError('Search failed');
    }
    
  3. Logging: Enable Elasticsearch client logging:

    $client = Elastic\Elasticsearch\ClientBuilder::create()
        ->setHosts(['http://localhost:9200'])
        ->setLogger(new \Monolog\Logger('elasticsearch'))
        ->build();
    

Tips and Tricks

  1. Reusable Query Builders: Create static methods for common queries:

    class PostQueryBuilder {
        public static function searchByTitle(string $title): Builder {
            return (new Builder(app('elasticsearch')))
                ->index('posts')
                ->addQuery(MatchQuery::create('title', $title));
        }
    }
    
  2. Dynamic Index Selection: Use environment variables or config for index names:

    $builder->index(config('elasticsearch.indices.posts'));
    
  3. Highlighting Results: Enable highlighting for snippets:

    $builder->highlight('title', ['pre_tags' => ['<em>'], 'post_tags' => ['</em>']]);
    
  4. Custom Analyzers: Pass analyzer parameters directly:

    MatchQuery::create('description', 'search term')->analyzer('custom_analyzer');
    
  5. Versioning: Use the _version parameter for optimistic concurrency:

    $builder->version(2)->update();
    
  6. Extending the Builder: Add custom methods to the Builder class:

    class CustomBuilder extends Builder {
        public function searchByTags(array $tags): self {
            return $this->addQuery(TermsQuery::create('tags', $tags));
        }
    }
    
  7. Handling Nulls: Explicitly filter out null values:

    $builder->addFilter(BoolQuery::create()->mustNot(ExistsQuery::create('field')));
    
  8. Performance Tuning:

    • Use filter instead of query for non-scored filters.
    • Limit _source fields to reduce payload size:
      $builder->source(['title', 'price']);
      
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport