Elasticsearch và Laravel - Làm thế nào để tối ưu query?!?!?

Bạn đang đau đầu để tối ưu query cho hệ thống? Bạn đang lãng phí thời gian để chuyển từ eloquent sang raw query? Hay đơn giản là hệ thống của bạn đang chậm khi search các item. Đừng lo nữa vì bây giờ chúng ta đã có tool rất xịn để có thể kết hợp với Laravel đó là Elasticsearch.

Elasticsearch là một công cụ tìm kiếm và phân tích dữ liệu mã nguồn mở, được xây dựng dựa trên Apache Lucene và phát triển bởi công ty Elastic. Được giới thiệu lần đầu tiên vào năm 2010, Elasticsearch đã nhanh chóng trở thành một công cụ phổ biến trong việc xây dựng các hệ thống tìm kiếm phân tán, lưu trữ dữ liệu phức tạp và phân tích dữ liệu.

Lợi ích của Elasticsearch

Elasticsearch mang đến nhiều lợi ích quan trọng cho các ứng dụng và dự án:

  1. Hiệu suất cao: Elasticsearch sử dụng cơ chế tìm kiếm ngược và index băm, giúp tăng hiệu suất và thời gian truy xuất dữ liệu một cách nhanh chóng.
  2. Tính phân tán và mở rộng: Với khả năng phân tán và mở rộng, bạn có thể thêm các node và dữ liệu để tăng khả năng xử lý và lưu trữ.
  3. Tích hợp dễ dàng: Elasticsearch hỗ trợ tích hợp với nhiều ngôn ngữ lập trình và framework, bao gồm cả Laravel, giúp bạn dễ dàng tích hợp nó vào các ứng dụng web và dịch vụ.
  4. Tính năng tìm kiếm phức tạp: Elasticsearch hỗ trợ nhiều tính năng tìm kiếm phức tạp, bao gồm tìm kiếm full-text, tìm kiếm fuzzy, kết hợp điều kiện, tìm kiếm dựa trên khoảng thời gian, và nhiều tính năng phân tích dữ liệu khác.
  5. Phân tích dữ liệu: Ngoài tính năng tìm kiếm, Elasticsearch còn hỗ trợ phân tích dữ liệu, cho phép bạn thực hiện các thống kê và phân tích trên dữ liệu.

Vậy cùng tích hợp nó vào Laravel nhé.

Laravel Scout là một gói mở rộng cho Laravel, cho phép tích hợp Elasticsearch vào ứng dụng Laravel một cách dễ dàng. Scout cung cấp một cách trừu tượng để tìm kiếm và tạo chỉ mục cho các mô hình Eloquent của bạn.

Bước 1: Cài đặt Elasticsearch và Elasticsearch Scout Driver

Trước tiên, bạn cần cài đặt Elasticsearch. Bạn có thể tải xuống và cài đặt Elasticsearch hoặc sử dụng Elasticsearch thông qua Docker.

Sau đó, cài đặt Elasticsearch Scout Driver:

composer require babenkoivan/scout-elasticsearch-driver

Bước 2: Cấu hình Elasticsearch và Scout Driver

Tiếp theo, cấu hình Elasticsearch và Elasticsearch Scout Driver trong tệp config/scout.php:

return [
    'driver' => env('SCOUT_DRIVER', 'elasticsearch'),
    'prefix' => env('SCOUT_PREFIX', ''),
    'queue' => env('SCOUT_QUEUE', false),

    'elasticsearch' => [
        'index' => env('ELASTICSEARCH_INDEX', 'laravel_app'),
        'hosts' => [
            env('ELASTICSEARCH_HOST', 'http://localhost'),
        ],
    ],
];

Bước 3: Tạo các mô hình và chỉ định quan hệ

Tiếp theo, tạo các mô hình Eloquent cho các bảng và chỉ định quan hệ giữa chúng. Ví dụ, bạn có thể có các bảng products, tags, product_tagcategories.

// app/Models/Product.php

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Product extends Model
{
    use Searchable;

    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class, 'product_tag', 'product_id', 'tag_id');
    }
}
// app/Models/Tag.php

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Tag extends Model
{
    use Searchable;

    public function products()
    {
        return $this->belongsToMany(Product::class, 'product_tag', 'tag_id', 'product_id');
    }
}
// app/Models/Category.php

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Category extends Model
{
    use Searchable;

    public function products()
    {
        return $this->hasMany(Product::class);
    }
}

Bước 4: Tạo các chỉ mục và cập nhật tìm kiếm

Tiếp theo, tạo các chỉ mục cho các sản phẩm và cập nhật tìm kiếm mỗi khi có thay đổi dữ liệu.

// app/Providers/AppServiceProvider.php

use App\Models\Product;
use Elasticsearch\ClientBuilder;
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\EngineManager;
use ScoutEngines\Elasticsearch\ElasticsearchEngine;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Product::observe(ProductObserver::class);
    }

    public function register()
    {
        $this->app->singleton(ElasticsearchEngine::class, function ($app) {
            $elasticsearchConfig = config('scout.elasticsearch');

            return new ElasticsearchEngine(
                ClientBuilder::create()->setHosts($elasticsearchConfig['hosts'])->build(),
                $elasticsearchConfig['index']
            );
        });

        resolve(EngineManager::class)->extend('elasticsearch', function () {
            return $this->app->make(ElasticsearchEngine::class);
        });
    }
}
// app/Observers/ProductObserver.php

use App\Models\Product;

class ProductObserver
{
    public function saved(Product $product)
    {
        $product->searchable();
    }

    public function deleted(Product $product)
    {
        $product->unsearchable();
    }
}

Bước 5: Tạo chức năng tìm kiếm trong controller

// app/Http/Controllers/ProductController.php

use App\Models\Product;
use App\Models\Category;
use App\Models\Tag;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function search(Request $request)
    {
        $categoryId = $request->input('category_id');
        $tagId = $request->input('tag_id');

        $query = Product::query();

        if ($categoryId) {
            // Lọc theo category_id nếu có được cung cấp
            $query->where('category_id', $categoryId);
        }

        if ($tagId) {
            // Lọc theo tag_id nếu có được cung cấp
            $query->whereHas('tags', function ($query) use ($tagId) {
                $query->where('tag_id', $tagId);
            });
        }

        $products = $query->get();

        return view('products.index', compact('products', 'categoryId', 'tagId'));
    }
}

Bước 6: Tạo view để hiển thị kết quả tìm kiếm

Cuối cùng, tạo view để hiển thị form để người dùng chọn category và tag, và hiển thị kết quả tìm kiếm.

// resources/views/products/index.blade.php

<form action="{{ route('products.search') }}" method="GET">
    <select name="category_id">
        <option value="">Chọn category</option>
        @foreach ($categories as $category)
            <option value="{{ $category->id }}" @if ($category->id == $categoryId) selected @endif>{{ $category->name }}</option>
        @endforeach
    </select>

    <select name="tag_id">
        <option value="">Chọn tag</option>
        @foreach ($tags as $tag)
            <option value="{{ $tag->id }}" @if ($tag->id == $tagId) selected @endif>{{ $tag->name }}</option>
        @endforeach
    </select>

    <button type="submit">Tìm kiếm</button>
</form>

@foreach ($products as $product)
    <h3>{{ $product->name }}</h3>
    <p>Category: {{ $product->category->name }}</p>
    <p>Tags: @foreach ($product->tags as $tag) {{ $tag->name }}, @endforeach</p>
@endforeach

Elasticsearch là một công cụ tìm kiếm và phân tích dữ liệu mạnh mẽ, cung cấp hiệu suất cao và tính phân tán để xử lý dữ liệu phức tạp. Khi tích hợp vào Laravel thông qua Laravel Scout, Elasticsearch trở thành một công cụ mạnh mẽ để tối ưu tìm kiếm và phân tích dữ liệu trong ứng dụng web. Với sự hỗ trợ mạnh mẽ từ Elasticsearch, bạn có thể xây dựng các ứng dụng tìm kiếm phức tạp và cải thiện trải nghiệm người dùng.