· Admin

Laravel ile Elasticsearch Entegrasyonu

Büyük veri setlerinde hızlı ve performanslı arama yapmak istiyorsanız Elasticsearch güçlü bir çözüm. Açık kaynaklı, RESTful API destekli ve gerçek zamanlı indeksleme sunuyor. Bu yazıda Laravel projesine Elasticsearch entegrasyonunu adım adım anlatıyorum.

Elasticsearch Nedir?

Açık kaynaklı bir arama ve analiz motoru. Temel özellikleri:

  • Tam metin arama: Büyük veri setlerinde bile hızlı sonuç
  • Gerçek zamanlı indeksleme: Veriler anında sorgulanabilir
  • Ölçeklenebilirlik: Yatayda kolayca genişletilebilir
  • RESTful API: HTTP üzerinden entegrasyon

1. Elasticsearch Kurulumu (Docker)

# docker-compose.yml
version: '3.7'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    ports:
      - "9200:9200"

  kibana:
    image: docker.elastic.co/kibana/kibana:8.10.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200
docker-compose up -d

Elasticsearch: localhost:9200, Kibana: localhost:5601

2. Laravel'e Elasticsearch Ekleme

composer require elasticsearch/elasticsearch

config/services.php:

'elasticsearch' => [
    'hosts' => [
        env('ELASTICSEARCH_HOST', 'localhost:9200'),
    ],
],

.env:

ELASTICSEARCH_HOST=localhost:9200

3. Service Provider

php artisan make:provider ElasticsearchServiceProvider
namespace App\Providers;

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

class ElasticsearchServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton('elasticsearch', function () {
            return ClientBuilder::create()
                ->setHosts(config('services.elasticsearch.hosts'))
                ->build();
        });
    }
}

4. Model Entegrasyonu

Searchable trait oluşturalım:

namespace App\Traits;

trait ElasticSearchable
{
    public function toSearchableArray(): array
    {
        return $this->toArray();
    }

    public function indexInElasticsearch(): void
    {
        app('elasticsearch')->index([
            'index' => $this->getTable(),
            'id' => $this->getKey(),
            'body' => $this->toSearchableArray(),
        ]);
    }

    public function removeFromElasticsearch(): void
    {
        app('elasticsearch')->delete([
            'index' => $this->getTable(),
            'id' => $this->getKey(),
        ]);
    }
}

Model'de kullanım:

class Post extends Model
{
    use ElasticSearchable;

    public function toSearchableArray(): array
    {
        return [
            'title' => $this->title,
            'content' => $this->content,
            'author' => $this->author->name ?? null,
            'published_at' => $this->published_at?->toISOString(),
        ];
    }
}

Model observer ile otomatik senkronizasyon:

class PostObserver
{
    public function saved(Post $post): void
    {
        $post->indexInElasticsearch();
    }

    public function deleted(Post $post): void
    {
        $post->removeFromElasticsearch();
    }
}

5. Arama Repository

namespace App\Repositories;

class ElasticsearchRepository
{
    private $client;

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

    public function search(string $index, string $query, array $fields = ['*']): array
    {
        return $this->client->search([
            'index' => $index,
            'body' => [
                'query' => [
                    'multi_match' => [
                        'query' => $query,
                        'fields' => $fields,
                        'fuzziness' => 'AUTO',
                    ],
                ],
            ],
        ]);
    }

    public function searchWithFilters(string $index, string $query, array $filters = []): array
    {
        $must = [
            ['multi_match' => [
                'query' => $query,
                'fields' => ['title^3', 'content'],
                'fuzziness' => 'AUTO',
            ]],
        ];

        foreach ($filters as $field => $value) {
            $must[] = ['term' => [$field => $value]];
        }

        return $this->client->search([
            'index' => $index,
            'body' => [
                'query' => ['bool' => ['must' => $must]],
            ],
        ]);
    }
}

6. Controller

class SearchController extends Controller
{
    public function __construct(
        private ElasticsearchRepository $search
    ) {}

    public function index(Request $request)
    {
        $query = $request->get('q', '');
        $results = $this->search->search('posts', $query, ['title^3', 'content']);

        return response()->json($results['hits']['hits']);
    }
}

7. Toplu İndeksleme

Mevcut verileri indekslemek için artisan komutu:

class IndexPostsCommand extends Command
{
    protected $signature = 'search:index-posts';

    public function handle(): void
    {
        $bar = $this->output->createProgressBar(Post::count());

        Post::chunk(100, function ($posts) use ($bar) {
            foreach ($posts as $post) {
                $post->indexInElasticsearch();
                $bar->advance();
            }
        });

        $bar->finish();
        $this->newLine();
        $this->info('Tüm postlar indekslendi!');
    }
}

Performans İpuçları

  • Queue kullanın: Büyük veri setlerini indekslerken queue ile arka planda çalıştırın
  • Redis cache: Sık tekrarlanan sorguları cache'leyin
  • Bulk API: Toplu indekslemede tek tek yerine bulk API kullanın
  • Index aliasing: Zero-downtime reindex için alias kullanın

Ne Zaman Elasticsearch?

Uygun: Büyük veri setleri, karmaşık sorgular, log analizi, e-ticaret arama, gerçek zamanlı analytics.

Uygun değil: Küçük projeler (Meilisearch daha pratik), basit LIKE sorguları yeterli olan durumlar.

Sonuç

Elasticsearch büyük veri setlerinde güçlü ve performanslı bir arama altyapısı sunuyor. Laravel ile entegrasyonu biraz kurulum gerektirse de sonuç etkileyici. Küçük-orta projeler için Meilisearch'ü de değerlendirin — daha basit ve hafif.