Saturday, 7 September 2024

Using Elastic Search In Laravel

1. Composer install Elasticsearch

"elasticsearch/elasticsearch": "^8.15",

* Note: Elasticsearch clients have the same version as Elasticsearch (in this case version 8)

1. Add observer for model

#[ObservedBy([ViolationObserver::class])]
class Violation extends Model

observer code

<?php

namespace App\Observers;

use App\Models\Violation;

class ViolationObserver
{
    public const VIOLATION_INDEX_NAME = 'violations_index';
    protected $client;

    public function __construct()
    {
        $this->client = app('elasticsearch');
    }

    /**
     * Handle the Violation "created" event.
     */
    public function created(Violation $model): void
    {
        $this->indexDocument($model);
    }

    /**
     * Handle the Violation "updated" event.
     */
    public function updated(Violation $model): void
    {
        $this->indexDocument($model);
    }

    /**
     * Handle the Violation "deleted" event.
     */
    public function deleted(Violation $model): void
    {
        $this->deleteDocument($model);
    }

    /**
     * Handle the Violation "restored" event.
     */
    public function restored(Violation $violation): void
    {
        //
    }

    /**
     * Handle the Violation "force deleted" event.
     */
    public function forceDeleted(Violation $violation): void
    {
        //
    }

    protected function indexDocument(Violation $model)
    {
        $params = [
            'index' => self::VIOLATION_INDEX_NAME,
            'id'    => $model->id,
            'body'  => [
                'id'     => $model->id,
                'control_plate'   => $model->control_plate,
                'status' => $model->status,
            ],
        ];

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

    protected function deleteDocument(Violation $model)
    {
        $params = [
            'index' => self::VIOLATION_INDEX_NAME,
            'id'    => $model->id,
        ];

        $this->client->delete($params);
    }
}

3. Register service

  $this->app->singleton('elasticsearch', function ($app) {
            return ClientBuilder::create()
                ->setHosts(config('elasticsearch.hosts'))
                ->build();
        });

4. Create a command demo

<?php

namespace App\Console\Commands;

use App\Models\Violation;
use App\Observers\ViolationObserver;
use Illuminate\Console\Command;

class CreateAndSyncElasticsearchIndexForViolation extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:create-and-sync-elasticsearch-index-for-violation';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $indexName = ViolationObserver::VIOLATION_INDEX_NAME;
        $client = app('elasticsearch');

        try {
            // $this->createIndex($client, $indexName);
            // $this->syncData($client, $indexName);
            $this->searchData($client, $indexName);
        } catch (\Exception $e) {
            dump("Error: " . $e->getMessage());
        }

        $this->info('Index created and data synchronized successfully');
    }

    /*
    * searchData
    */
    protected function searchData($client, $indexName)
    {
        // Violation::create([
        //     'control_plate' => '21'
        // ]);

        // Violation::destroy(9);

        $params = [
            'index' => $indexName, // Replace with your index name
            'body'  => [
                'query' => [
                    // 'match_all' => new \stdClass() // Match all documents
                    // 'match' => [
                    //     'control_plate' => '11111111' // Replace with your field and search value
                    // ],
                    'wildcard' => [
                        'control_plate' => '*2*'
                    ]
                ]
            ]
        ];
       
        try {
            $response = $client->search($params);
            if (isset($response['hits']['hits'])) {
                foreach ($response['hits']['hits'] as $hit) {
                    echo 'ID: ' . $hit['_id'] . '<br>';
                    echo 'Source: ' . print_r($hit['_source'], true) . '<br><br>';
                }
            } else {
                echo 'No results found';
            }
       
        } catch (\Exception $e) {
            echo 'Error: ', $e->getMessage();
        }
    }

    /*
    * createIndex
    */
    protected function createIndex($client, $indexName)
    {
        $params = [
            'index' => $indexName,
            'body'  => [
                'mappings' => [
                    'properties' => [
                        'id' => [
                            'type' => 'integer'
                        ],
                        'control_plate' => [
                            'type' => 'text'
                        ],
                        'status' => [
                            'type' => 'keyword'
                        ]
                    ]
                ]
            ]
        ];

        try {
            $response = $client->indices()->create($params);
            $this->info('Index created: ' . $indexName);
        } catch (\Exception $e) {
            $this->error('Error creating index: ' . $e->getMessage());
        }
    }

    /*
    * syncData
    */
    protected function syncData($client, $indexName)
    {
        $models = Violation::all(); // Fetch all records from the table

        $params = ['body' => []];

        foreach ($models as $model) {
            // Add index operation for each record
            $params['body'][] = [
                'index' => [
                    '_index' => $indexName,
                    '_id'    => $model->id,
                ]
            ];

            $params['body'][] = [
                'id'     => $model->id,
                'control_plate'   => $model->control_plate,
                'status' => $model->status,
            ];
        }

        try {
            // Bulk index the records
            $response = $client->bulk($params);
            $this->info('Data synchronized successfully');
        } catch (\Exception $e) {
            $this->error('Error synchronizing data: ' . $e->getMessage());
        }
    }
}


run command: php artisan app:create-and-sync-elasticsearch-index-for-violation

5. Docker-composer for Elasticsearch 8.15.1

### kibana815 ########################################
    kibana815:
        image: kibana:8.15.1
        container_name: kibana
        environment:
          - ELASTICSEARCH_URL=http://elasticsearch815:9200
        ports:
          - "5601:5601"
        networks:
          - frontend
          - backend

### ElasticSearch ########################################
    elasticsearch815:
      image: elasticsearch:8.15.1
      container_name: elasticsearch
      environment:
        - discovery.type=single-node
        - ES_JAVA_OPTS=-Xmx2g -Xms2g  # Adjust JVM heap size as needed
        - xpack.security.enabled=false  # Disable security features
      ports:
        - "9200:9200"  # HTTP port
        - "9300:9300"  # Transport port
      volumes:
        - elasticsearch815:/usr/share/elasticsearch/data
      healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:9200"]
        interval: 30s
        retries: 3
        start_period: 30s
        timeout: 10s
      networks:
        - frontend
        - backend

Thank you

No comments:

Post a Comment

Golang Advanced Interview Q&A