Integrate Elasticsearch to laravel

345 0 0 0

Last Updated : 2024-04-28 05:04:17

Elasticsearch is one of the most commonly used packages for laravel for a better searching capabilities. Here is how to integrate ElasticSearch into your laravel application so that whenever you add/update/delete any record it will be automatically indexed and updated in the elasticsearch index.

This tutorial will have three different sections as follows:



  1. Managing indices: Creating, Dropping, Adding mapping or settings ElasticSearch indices.

  2. Integrating ElasticSearch to your models, so that it will be reflected automatically in the corresponding index (This is what we will be discussing in this article)

  3. Creating the search functionality against both DB and ElasticSearc.


Now Let's start on integrating ElasticSearch to our models.


For this to work we will do the follow the coming steps:



  1. We will create a laravel job for indexing documents.

  2. We will create a laravel job for deleting a document from an index.

  3. We will create a model observer to auto-fire the indexing funcationaly whenever a model is saved() or deleted().

  4. We can then generalize the whole concept so that it works with any model and not a specific mode.


Let's start on creating the laravel jobs for indexing (new|updated) document, and the one for deleting one. This step is supposed to be done after you have your models set correctly.


//Run the following artisan command
php artisan make:job IndexArticleElasticsearchJob

//This will create the following file
<?php

namespace App\Jobs;

use App\Models\Article;
use Elasticsearch\Client;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class IndexArticleElasticsearchJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

protected $article;

/**
* IndexArticleElasticsearchJob constructor.
* @param Article $article
*/
public function __construct(Article $article)
{
$this->article = $article;
}

/**
* Execute the job.
* @param Client $client
*/
public function handle(Client $client)
{
$params = [
'index' => 'articles',
'id' => $this->article->id,
'body' => $this->article->toArray(),
];

$client->index($params);
}
}

And now we will create another job for deleteing a document


php artisan make:job RemoveArticleElasticsearchJob

//This will create the following file
<?php

namespace App\Jobs;

use Elasticsearch\Client;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class RemoveArticleElasticsearchJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

protected $articleId;

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

public function handle(Client $client)
{
$params = [
'index' => 'articles-v1',
'id' => $this->articleId,
];

try {
$client->delete($params);
} catch (Missing404Exception $exception) {
// Already deleted..
}
}
}

Now we will create an observer so that it will fire automatically when ever any record is added/updated or deleted:


php artisan make:observer ArticleObserver --model=Article

//This will create the following file:
<?php

namespace App\Observers;

use App\Jobs\IndexArticleElasticsearchJob;
use App\Jobs\RemoveArticleElasticsearchJob;
use App\Models\Article;

class ArticleObserver
{
/**
* Handle the Article "created" event.
*
* @param \App\Models\Article $article
* @return void
*/
public function created(Article $article)
{
if ($article->is_active) {
dispatch(new IndexArticleElasticsearchJob($article));
}
}

/**
* Handle the Article "updated" event.
*
* @param \App\Models\Article $article
* @return void
*/
public function updated(Article $article)
{
if ($article->is_active) {
dispatch(new IndexArticleElasticsearchJob($article));
} else {
dispatch(new RemoveArticleElasticsearchJob($article->id));
}
}

/**
* Handle the Article "deleted" event.
*
* @param \App\Models\Article $article
* @return void
*/
public function deleted(Article $article)
{
dispatch(new RemoveArticleElasticsearchJob($article->id));
}

/**
* Handle the Article "restored" event.
*
* @param \App\Models\Article $article
* @return void
*/
public function restored(Article $article)
{
if ($article->is_active) {
dispatch(new IndexArticleElasticsearchJob($article));
}
}

/**
* Handle the Article "force deleted" event.
*
* @param \App\Models\Article $article
* @return void
*/
public function forceDeleted(Article $article)
{
dispatch(new RemoveArticleElasticsearchJob($article->id));
}
}

Now we need to activate the Observer using one of the following two methods:



  1. Usingthe AppServiceProvider's boot section
    /**
    * Bootstrap any application services.
    *
    * @return void
    */
    public function boot()
    {
    \App\Models\Article::observe(\App\Observers\ArticleObserver::class);
    }​


  2. Or when generalizing the idea to all models, you can set it in the model itself or in a trait such as Searchable trait
    public static function bootSearchable() {
    static::observe(\App\Observers\ArticleObserver::class);
    }​



Now you are set and whenever any article is being added, updated or deleted it will be automatically indexed,Re-indexed or deleted from the corresponding elasticsearch index.

Mohammed Anwar

Mohammed Anwar

Experienced technical lead PHP, MySQL and Laravel Developer for 15+ years, with proven ability to develop and create high-quality and optimized web applications. Great ability to build and optimize database design, schema and queries. Versed programing trainer and instructor delivering web courses and helping others to get into the field in a timely manner. Fast and eager learner for new technologies .