avatar
Implement Elasticsearch in Laravel Laravel

• The assumption that implementing Elasticsearch is a better and more accurate method than using full-text search to retrieve query results. Let generate and install the Elasticsearch PHP client library via Composer

composer require elasticsearch/elasticsearch

• To proceed, you need to create a new instance of Elasticsearch client in your Laravel application. You can achieve this by either adding the following code to your `config/elasticsearch.php` file or creating a new service provider:

'elasticsearch' => [
    'hosts' => [
        env('ELASTICSEARCH_HOST', 'http://127.0.0.1:9200'),
    ],
],

• Create new service provider for Elasticsearch. Run the following command in your terminal to create a new service provider:

php artisan make:provider ElasticsearchServiceProvider

• The next step, open the ElasticsearchServiceProvider.php file that was created in the app/Providers directory and modify content in `register` method:

use Elastic\Elasticsearch\ClientBuilder;
use Illuminate\Support\ServiceProvider;

class ElasticsearchServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(ClientBuilder::class, function () {
            $config = config('elasticsearch');
            return ClientBuilder::create()
                ->setHosts($config['hosts'])
                ->build();
        });
    }
...

• Binds `ElasticsearchServiceProvider.php` to the Laravel applications' container.

return [
    // ...
    'providers' => [
        // ...
        App\Providers\ElasticsearchServiceProvider::class,
    ],
    // ...
];

• To fulfill your requirements, it is necessary to create a new Elasticsearch index with the name `flagtick_index` and configure it to have two shards and one replica.

(*) ElasticsearchServiceProvider.php

public function boot()
{
    $client = ClientBuilder::create()->build();
    $params = [
        'index' => 'flagtick_index',
        'body' => [
            'settings' => [
                'number_of_shards' => 2,
                'number_of_replicas' => 1
            ]
        ]
    ];
    $exists = $client->indices()->exists(['index' => 'flagtick_index']);
    if (!$exists) {
        $client->indices()->create($params);
    }
}

• Next, you need to make sure that `http://127.0.0.1:9200` is up and running on your local machine. This will confirm that Elasticsearch has been successfully installed and is ready for the subsequent steps.

• Using the link as below to retrieve the health status of Elasticsearch cluster in a human-readable format.

http://127.0.0.1:9200/_cat/health?v

• Using `Snippet::all()` method and creates an array of documents to index into Elasticsearch.

<?php

namespace App\Console\Commands;

use App\Model\Snippet;
use Elastic\Elasticsearch\ClientBuilder;
use Illuminate\Console\Command;

class ElasticSnippet extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:elasticsnippet';
    protected $documents;
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Retrieves all the snippets and indexing into elasticsearch';
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
    /**
     * Execute the console command.
     *
     * @return bool
     */
    public function handle()
    {
        $snippets = Snippet::all();
        $this->documents = [];
        foreach ($snippets as $snippet) {
            $document = [
                'index' => [
                    '_index' => 'flagtick_index',
                    '_id' => $snippet->id,
                ],
            ];
            $document['body'] = [
                'title' => $snippet->name_en,
                'content' => $snippet->description_en,
            ];
            $this->documents[] = $document;
        }
        $client = ClientBuilder::create()->build();
        $params = [
            'body' => $this->documents,
        ];
        $client->bulk($params);
        return true;
    }
}

• To retrieve all data from Elasticsearch, you can use the following URL:

http://127.0.0.1:9200/flagtick_index/_search

If you want to use `Search API`, let try with other URL here:

{
    "_index": "flagtick_index",
    "_id": "23",
    "_score": 1,
    "_source": {
      "index": {
        "_index": "flagtick_index",
        "_id": 24
      },
      "body": {
        "title": "update package",
        "content": "<pre><code class=\"os\" data-ln-start-from=\"10\">&nbsp;sudo apt update &amp;&amp; sudo apt y upgrade\n</code></pre>"
      }
    }
  },

(*) Retrieve all records

http://localhost:9200/flagtick_index/_search?q=*

(*) Retrieve by id

http://localhost:9200/flagtick_index/_doc/23

(*) Retrieve by body.title

http://localhost:9200/flagtick_index/_search?q=body.title:update

Note: In here <search_term> means `update`.

You can use the Query String Query to search both the title and content fields but give priority to the title field. The title field can be boosted using the caret ^ notation. Here's an example URL:

http://localhost:9200/flagtick_index/_search?q=(body.title:update)^2 OR body.content:update

• We can leverage the responses from the examples above to define the REST response in Laravel application as follows:

(*) api.php

Route::get('elastic/snippet', array(
    'as' => 'search snippet',
    'uses' => 'API\V1\Search\SearchController@snippet'
));

(*) SearchController.php

public function snippet(Request $request) {
    $params = [
        'index' => 'flagtick_index',
        'body' => [
            'query' => [
                'match' => [
                    'body.title' => 'update'
                ]
            ]
        ]
    ];
    $client = ClientBuilder::create()->build();
    $response = $client->search($params);
    return $response['hits']['hits'];
}

Note: To avoid encountering errors, you should run the command `composer require symfony/psr-http-message-bridge` in your terminal to install Symfony's PSR-7 bridge. This package is necessary to ensure proper integration of PSR-7 with Symfony.

(*) You can enhance the query by incorporating multiple fields, such as `title` and `content`, and by setting `title` as the top priority field over 'content' in the search results.

public function snippet(Request $request) {
    $params = [
        'index' => 'flagtick_index',
        'body' => [
            'query' => [
                'bool' => [
                    'should' => [
                        [
                            'match' => [
                                'body.title' => [
                                    'query' => 'update',
                                    'boost' => 2.0 // Boost title field
                                ]
                            ]
                        ],
                        [
                            'match' => [
                                'body.content' => 'update'
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ];
    $client = ClientBuilder::create()->build();
    $response = $client->search($params);
    return $response['hits']['hits'];
}

You can see the result in Postman:

You need to login to do this manipulation!