Laravel-Elasticsearch

repository·main·Indexed 40 days ago

https://github.com/pdphilip/laravel-elasticsearch

An implementation of Laravel's Eloquent ORM for Elasticsearch, allowing developers to use Eloquent syntax for full-text, geo-spatial, and fuzzy searches. It includes support for Elasticsearch relationships, schema management via mappingDefinition, and a suite of Artisan commands including elastic:indices, elastic:make, elastic:re-index, elastic:show, and elastic:status. Compatible with Laravel 8 through 13.

Tokens
14.8K
Snippets
58
Records
69
Agent score
99%

What's inside Laravel-Elasticsearch

  1. Work with Elasticsearch relationships

    main

    The package supports built-in relationships, allowing you to eager load related models even when working with Elasticsearch data. This includes relationships between Elasticsearch models and standard SQL models.

    // Eager loading a relationship (e.g., to a SQL model)
    UserLog::where('status', 1)->orderByDesc('created_at')->with('user')->get();
  2. Install Laravel-Elasticsearch

    main

    Install the package via Composer depending on your Laravel version.

    For Laravel 11.x, 12.x, and 13.x (Maintained):

    composer require pdphilip/elasticsearch

    For older versions (LTS):

    • Laravel 10/11/12: composer require pdphilip/elasticsearch:~5.5
    • Laravel 10/11 (v4): composer require pdphilip/elasticsearch:~4
    • Laravel 9: composer require pdphilip/elasticsearch:~3.9
    • Laravel 8: composer require pdphilip/elasticsearch:~3.8
  3. Register the Elasticsearch service provider

    main

    If your package is not automatically autoloaded, you must manually register the PDPhilip\Elasticsearch\ElasticServiceProvider.

    For Laravel 11+: Add it to bootstrap/providers.php.

    For Laravel 10 and below: Add it to the providers array in config/app.php.

    // For Laravel 11+ in bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
        PDPhilip\Elasticsearch\ElasticServiceProvider::class,
    ];
    
    // For Laravel 10 and below in config/app.php
    'providers' => [
        ...
        PDPhilip\Elasticsearch\ElasticServiceProvider::class,
        ...
    ],
  4. Define Elasticsearch index structures with Blueprint

    main

    The PDPhilip\Elasticsearch\Schema\Blueprint class is used within migrations to define the structure of an Elasticsearch index. It extends the standard Laravel Blueprint but adds specialized methods for Elasticsearch-specific features like analyzers, tokenizers, filters, and index settings.

    use PDPhilip\Elasticsearch\Schema\Blueprint;
    
    // Inside a migration's up() method
    Schema::create('my_index', function (Blueprint $table) {
        $table->property('text', 'description', ['analyzer' => 'standard']);
        $table->property('integer', 'count');
    });
  5. Compile Aggregations and Distinct queries

    main

    The Grammar handles complex Elasticsearch aggregations through compileSelectAggregations. It supports several modes:

    1. Distinct/Bulk Distinct: Uses term aggregations to simulate DISTINCT behavior. It intelligently groups fields by their nested paths to minimize the number of nested aggregation wrappers.
    2. Bucket Aggregations: Compiles groupBy style queries into bucket aggregations.
    3. Metric Aggregations: Compiles specific metric requests (e.g., sum, avg) into metric aggregations.

    When performing aggregations, the grammar automatically adjusts the size parameter to the query's setLimit() to ensure only the necessary aggregation results are returned.

  6. Understand document structure and inner hits

    main

    The documentFromResult method transforms a raw Elasticsearch hit into a usable document array. It performs the following transformations:

    1. Moves _source fields to the top level of the array.
    2. Adds _id, _index, and _score to the document.
    3. If highlight data is present, it is added to the _meta property (with keywords sanitized).
    4. If inner_hits are present (common in nested object queries), they are extracted and attached to the document under their respective type keys, while removing the original key from the top level to prevent duplication.
    // Conceptual structure of a processed document
    $document = [
        'field1' => 'value1',
        'field2' => 'value2',
        '_id' => 'doc_123',
        '_meta' => MetaDTO instance containing index, score, etc.
    ];
  7. Query models using the Elasticsearch Eloquent Builder

    main

    The PDPhilip\Elasticsearch\Eloquent\Builder extends Laravel's standard Eloquent Builder to provide Elasticsearch-specific querying capabilities. You can use standard Eloquent methods like where(), get(), and count(), which are translated into Elasticsearch DSL queries under the hood.

    Key features include:

    • Nested Queries: Use closures within where() to create nested Elasticsearch queries.
    • ElasticCollection: Results returned by get() are wrapped in an ElasticCollection, which carries Elasticsearch metadata (like query info) alongside the hydrated models.
    // Standard Eloquent-style querying
    $users = User::where('email', 'like', '%@gmail.com')
        ->where(function ($query) {
            $query->where('active', true);
        })
        ->get();
    
    // Accessing metadata from the ElasticCollection
    $metadata = $users->getMeta();
  8. How the ES Query Grammar translates queries

    main

    The PDPhilip\Elasticsearch\Query\Grammar\Grammar class is responsible for translating Laravel-style Eloquent queries into Elasticsearch Domain Specific Language (DSL). It handles the conversion of various query types into the JSON structures required by Elasticsearch.

    Key responsibilities include:

    • Where clauses: Compiling filters and queries.
    • Aggregations: Compiling groupBy, metrics, and distinct operations.
    • Ordering: Compiling orderBy and sort instructions.
    • Field Utilities: Handling field mapping and value conversion (e.g., DateTime to strings).
    • CRUD Operations: Translating Insert, Update, Select, Delete, and Truncate commands into their respective Elasticsearch API equivalents.
  9. Configure Elasticsearch environment variables

    main

    Set up your .env file with the necessary Elasticsearch connection settings.

    Key settings include:

    • ES_AUTH_TYPE: Set to http or cloud.
    • ES_HOSTS: Comma-separated list of hosts (e.g., "http://localhost:9200,http://es02:9200").
    • ES_INDEX_PREFIX: A prefix added to all indexes created by the package with an underscore (e.g., my_app_ results in my_app_user_logs for a UserLog model).
    • ES_CLOUD_ID and ES_API_KEY: Required if using ES_AUTH_TYPE=cloud.
    ES_AUTH_TYPE=http
    ES_HOSTS="http://localhost:9200"
    ES_USERNAME=
    ES_PASSWORD=
    ES_CLOUD_ID=
    ES_API_ID=
    ES_API_KEY=
    ES_SSL_CA=
    ES_INDEX_PREFIX=my_app_
    ES_SSL_CERT=
    ES_SSL_CERT_PASSWORD=
    ES_SSL_KEY=
    ES_SSL_KEY_PASSWORD=
    ES_OPT_ID_SORTABLE=false
    ES_OPT_VERIFY_SSL=true
    ES_OPT_RETRIES=
    ES_OPT_META_HEADERS=true
    ES_ERROR_INDEX=
    ES_OPT_BYPASS_MAP_VALIDATION=false
    ES_OPT_DEFAULT_LIMIT=1000
  10. Manage Elasticsearch query metadata with ElasticCollection

    main

    The ElasticCollection class extends the standard Laravel Eloquent Collection to carry metadata from the Elasticsearch query that generated the result set. This allows you to access information like total hits, execution time (took), shard information, and the underlying DSL query directly from the collection instance.

    Key behaviors:

    • Metadata Presence: Use hasQueryMeta() to check if the collection contains query metadata. Standard Eloquent operations like map(), filter(), or values() will result in a collection where hasQueryMeta() returns false.
    • Fallback Behavior: If you call getQueryMeta() on a collection without metadata, it returns an empty QueryMeta object rather than null, ensuring getters do not fail but return default/unknown values.
    • Loading Metadata: You can manually attach metadata using loadMeta(QueryMeta $meta) or setQueryMeta(MetaDTO $meta).
    // Assuming $collection is an instance of ElasticCollection returned from an Elasticsearch query
    if ($collection->hasQueryMeta()) {
        $total = $collection->getTotal();
        $took = $collection->getTook();
        $dsl = $collection->getDsl();
    }
  11. Run Elasticsearch and Kibana via Docker Compose

    main

    You can use the provided docker-compose.yml to set up a local development environment containing Elasticsearch (8.18.0), Kibana (8.18.0), and Redis (7-alpine).

    Key configurations in this setup:

    • Elasticsearch: Runs in single-node mode with security (xpack.security.enabled) disabled for easier local development. It is accessible on port 9200.
    • Kibana: Accessible on port 5601 and configured to wait until the Elasticsearch service is healthy before starting.
    • Redis: Runs on port 6379.
    services:
      elasticsearch:
        image: elasticsearch:8.18.0
        ports:
          - "9200:9200"
        environment:
          - discovery.type=single-node
          - xpack.security.enabled=false
        restart: unless-stopped
        healthcheck:
          test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\|\"status\":\"yellow\"'"]
          interval: 10s
          timeout: 5s
          retries: 3
          start_period: 30s
      kibana:
        depends_on:
          elasticsearch:
            condition: service_healthy
        image: kibana:8.18.0
        restart: unless-stopped
        ports:
          - "5601:5601"
      redis:
        image: redis:7-alpine
        ports:
          - "6379:6379"
        restart: unless-stopped
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 3s
          retries: 3
          start_period: 5s
  12. Use Elasticsearch search features in Eloquent

    main

    The package extends Eloquent models with powerful Elasticsearch-specific query methods.

    Common Search Methods:

    • searchTerm('query'): Performs a standard search.
    • orSearchTerm('query'): Performs an OR search.
    • searchPhrasePrefix('phrase'): Performs a phrase prefix search.
    • highlight(): Enables highlighting on the query.
    • whereMatch('field', 'value'): Performs a match query on a specific field.
    • whereGeoDistance('field', 'distance', [lat, lon]): Performs a geographic distance search.
    • whereFuzzy('field', 'query'): Performs a fuzzy search for typos.
    // Search term
    UserProfile::searchTerm('Laravel')->orSearchTerm('Elasticsearch')->get();
    
    // Phrase prefix with highlighting
    UserProfile::searchPhrasePrefix('loves espressos and t')->highlight()->get();
    
    // Match query
    UserProfile::whereMatch('bio', 'PHP')->get();
    
    // Geo distance
    UserLog::whereGeoDistance('location', '10km', [40.7185,-74.0025])->get();
    
    // Fuzzy search
    UserProfile::whereFuzzy('description', 'qick brwn fx')->get();