Erken Optimizasyon Tuzağı
B2B SaaS platformu geliştirirken, hızlı ve akıllı bir arama çubuğu temel bir gereksinimdir. Kullanıcı “fatura pazarlama” yazdığında, panonun yanlış yazılmış kelimeleri veya farklı zaman kullanımları olsa bile tüm ilgili belgeleri anında geri döndürmesini bekler. Birçok geliştirici için hemen devreye almak istenen çözüm, Elasticsearch, Algolia veya Meilisearch gibi özel arama motorlarına yönelmektir.
Bu araçlar harika olsa da, büyük mimari karmaşıklıklar getirir. Artık ana veritabanınızı dış bir hizmetle senkronize etmeniz, senkronizasyon düştüğünde başarısız iş kuyruklarını yönetmeniz ve pahalı aylık abonelik ücretleriyle ilgilenmeniz gerekecek. Platformunuz 10 milyon satırın altında ise, harici bir arama kümesi kurmak erken optimizasyon anlamına gelir. Zaten veritabanınızda dünya standartlarında bir arama motoru var: PostgreSQL Full-Text Search (FTS).
PostgreSQL FTS Nasıl Çalışır
Standart SQL LIKE '%query%' kullanmak son derece yavaştır çünkü indeks kullanamaz ve tam tablo taraması yapar. Ayrıca dil anlayışına sahip olmadığından, yalnızca tam string eşleşmeler arar.
PostgreSQL FTS farklı çalışır. Metni, köklerine normalleştirilen farklı kelimelerin sıralı bir listesini (bir tsvector) ayrıştırır—bu, “running” kelimesini “run” olarak dönüştürür. Bir kullanıcı arama yaptığında, sorgusunu bir tsquery‘ye dönüştürür. GIN Index‘i tsvector kolonumuza ekleyebildiğimiz için, veritabanı eşleşmeleri milisaniyeler içinde bulur.
Adım 1: Veritabanı Migration’ı
Yüksek performanslı bir arama indeksi oluşturmak için bir articles tablosuna ihtiyacımız var. PostgreSQL, satır eklendiğinde veya güncellendiğinde arama indeksimizi otomatik olarak güncelleyebilmesi için bir Üretilmiş Kolon kullanacağız.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateArticlesTable extends Migration
{
public function up(): void
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
// 1. Başlık ve içeriği birleştirerek bir üretilmiş 'tsvector' kolonu ekleyin.
// 'title' elemana baskı (A) vereceğiz, 'content' (B) ise daha düşük olacak.
DB::statement("
ALTER TABLE articles ADD COLUMN search_vector tsvector GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(content, '')), 'B')
) STORED;
");
// 2. Aramayı son derece hızlı hale getirmek için bir GIN indeksi ekleyin.
DB::statement("CREATE INDEX articles_search_idx ON articles USING GIN(search_vector);");
}
}
Adım 2: Laravel’de FTS İndeksine Sorgulama
Artık bu optimize edilmiş kolonu Laravel’in whereRaw metodunu kullanarak sorgulayabiliyoruz. PostgreSQL’in yerel @@ eşleşme operatörünü ve kullanıcıların Google’da olduğu gibi doğal bir şekilde yazmalarına olanak tanıyan websearch_to_tsquery‘yi kullanıyoruz.
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function index(Request $request)
{
$query = $request->input('q');
if (!$query) {
return response()->json([]);
}
// Üretilmiş vektör kolonda arama yapıyoruz ve alaka derecesine göre sıralıyoruz (Rank)
$results = Article::query()
->select('id', 'title')
->whereRaw("search_vector @@ websearch_to_tsquery('english', ?)", [$query])
->orderByRaw("ts_rank(search_vector, websearch_to_tsquery('english', ?)) DESC", [$query])
->limit(20)
->get();
return response()->json($results);
}
}
Mühendislik ROI’si
PostgreSQL’in yerel FTS’sini kullanarak mimarinizi merkezi tutabilirsiniz. Yönetilecek webhook’lar yok, dış senkronizasyon kuyrukları yok ve aylık API maliyetleri yok. Baştan sona hataları tolere etme, kök kelime türetme ve sıralama algoritmalarını tamamen ücretsiz elde edersiniz ve milyonlarca satıra kadar ölçeklenirken dış çözümleri gün yüzüne çıkartmanıza gerek kalmaz.
Kaynak: Orijinal Makale


