Halo developer Laravel! Selamat datang di Part 3, sekaligus bagian penutup dari seri tutorial membangun fitur AI Search. Pada bagian sebelumnya, kita telah berhasil menghidupkan fitur pencarian semantik dengan mengubah input pengguna menjadi vector menggunakan OpenAI, dan menghitung nilai Cosine Similarity di level memori (PHP).

Hasilnya sangat mengagumkan, bukan? Sistem kita kini bisa memahami konteks dan sinonim kata. Namun, seperti yang sempat kita singgung di akhir Part 2, pendekatan menghitung kemiripan secara in-memory menggunakan Laravel Collection memiliki satu kelemahan fatal: Skalabilitas.

Jika platform LaraCourse kita berkembang dari 500 kursus menjadi 50.000 kursus, menarik semua data tersebut ke dalam RAM server (PHP) untuk dihitung satu per satu akan menyebabkan server kehabisan memori (Out of Memory) dan proses pencarian akan memakan waktu hingga hitungan detik. Di sinilah kita membutuhkan solusi level Enterprise: Vector Database.

Mengapa Kita Butuh Vector Database?

Database relasional tradisional (seperti MySQL atau PostgreSQL standar) dirancang untuk mencari data teks, angka eksak, atau tanggal menggunakan struktur index seperti B-Tree. Mereka tidak dirancang untuk membandingkan ribuan array angka desimal (vector) dan mencari jarak terdekat di antara mereka secara efisien.

Vector database hadir secara khusus untuk menyimpan data embeddings dan melakukan operasi matematika (seperti Cosine Similarity) langsung di level database. Ada banyak pilihan layanan cloud seperti Pinecone, Qdrant, atau Meilisearch. Namun, untuk ekosistem Laravel yang sering kali sudah menggunakan PostgreSQL, solusi paling elegan dan gratis adalah menggunakan ekstensi pgvector.

Berkenalan dengan pgvector di PostgreSQL

pgvector adalah ekstensi open-source untuk PostgreSQL yang memungkinkan kita menyimpan data vector dan melakukan pencarian kemiripan (similarity search) langsung menggunakan query SQL. Mari kita integrasikan ke dalam aplikasi Laravel kita!

1. Mengaktifkan Ekstensi pgvector

Pastikan Anda menggunakan database PostgreSQL. Buka terminal database Anda atau gunakan GUI seperti DBeaver/TablePlus, lalu jalankan perintah SQL berikut untuk mengaktifkan ekstensi:

CREATE EXTENSION IF NOT EXISTS vector;

2. Memperbarui Migration Laravel

Pada Part 1, kita menggunakan tipe kolom json untuk menyimpan array angka. Sekarang, kita harus mengubahnya menjadi tipe data vector asli. Buat file migration baru untuk mengubah struktur tabel courses.

php artisan make:migration alter_embedding_column_in_courses_table

Kemudian, modifikasi file migration tersebut. Karena tipe vector belum didukung secara luas di versi Laravel lama tanpa package tambahan, kita akan menggunakan Raw SQL Statement untuk kompatibilitas maksimal:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
    public function up()
    {
        // Hapus kolom JSON lama
        Schema::table('courses', function (Blueprint $table) {
            $table->dropColumn('embedding');
        });

        // Tambahkan kolom vector dengan dimensi 1536 (standar OpenAI text-embedding-3-small)
        DB::statement('ALTER TABLE courses ADD COLUMN embedding vector(1536)');
    }

    public function down()
    {
        Schema::table('courses', function (Blueprint $table) {
            $table->dropColumn('embedding');
        });
        
        DB::statement('ALTER TABLE courses ADD COLUMN embedding json NULL');
    }
};

Catatan: Jika Anda menggunakan Laravel 11, Laravel sudah mulai memberikan dukungan native untuk kolom vector di beberapa driver database.

3. Refactoring Controller Pencarian

Inilah keajaiban utamanya. Kita akan menghapus Helper VectorHelper.php yang kita buat di Part 2, dan memindahkan seluruh beban komputasi ke database PostgreSQL. pgvector menggunakan operator <=> untuk menghitung Cosine Distance (Jarak Cosine). Semakin kecil jaraknya, semakin mirip datanya.

Buka kembali CourseSearchController.php dan ubah isinya menjadi seperti ini:

<?php

namespace App\Http\Controllers;

use App\Models\Course;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OpenAI\Laravel\Facades\OpenAI;

class CourseSearchController extends Controller
{
    public function search(Request $request)
    {
        $query = $request->input('q');
        
        if (!$query) {
            return response()->json(['message' => 'Query pencarian kosong'], 400);
        }

        // 1. Ubah input pencarian user menjadi Vector Embedding
        $response = OpenAI::embeddings()->create([
            'model' => 'text-embedding-3-small',
            'input' => $query,
        ]);

        // Format array PHP menjadi format string vector PostgreSQL: "[0.1, 0.2, ...]"
        $queryVector = '[' . implode(',', $response->embeddings[0]->embedding) . ']';

        // 2. Lakukan pencarian langsung di Database menggunakan pgvector
        $courses = Course::select('id', 'title', 'description', 'category')
            // Hitung skor kemiripan: 1 - Cosine Distance = Cosine Similarity
            ->selectRaw('1 - (embedding <=> ?::vector) AS similarity_score', [$queryVector])
            ->whereNotNull('embedding')
            // Urutkan berdasarkan jarak terdekat (paling mirip)
            ->orderByRaw('embedding <=> ?::vector', [$queryVector])
            ->limit(5)
            ->get();

        return response()->json([
            'query' => $query,
            'results' => $courses
        ]);
    }
}

Dengan kode di atas, memori PHP (RAM) Anda akan tetap aman meskipun database Anda memiliki 1 juta baris data. PostgreSQL yang akan melakukan kerja kerasnya!

4. Indexing: Kunci Pencarian Milidetik

Untuk membuat pencarian vektor sangat cepat pada skala jutaan data, kita perlu menambahkan Index khusus. pgvector mendukung index HNSW (Hierarchical Navigable Small World). Anda bisa menambahkannya via raw SQL di migration:

DB::statement('CREATE INDEX courses_embedding_hnsw_idx ON courses USING hnsw (embedding vector_cosine_ops)');

Dengan index ini, pencarian kemiripan yang tadinya memakan waktu bermenit-menit pada data masif dapat diselesaikan dalam hitungan milidetik!

Rangkuman Seri: AI Search vs Pencarian Tradisional (Non-AI)

Untuk menutup seri tutorial ini, mari kita rangkum dan bandingkan perbedaan utama antara sistem pencarian tradisional (Non-AI) yang biasa kita gunakan, dengan sistem AI Semantic Search yang baru saja kita bangun.

1. Pencarian Tradisional (Non-AI / Keyword Matching)

  • Mekanisme: Menggunakan klausa LIKE '%keyword%' atau Full-Text Search bawaan database. Mencocokkan huruf per huruf atau kata per kata.
  • Kelebihan: Sangat mudah diimplementasikan, tidak memerlukan biaya API pihak ketiga, dan sangat cepat untuk pencarian data eksak (misal: mencari nomor invoice atau ID produk).
  • Kelemahan: Sangat kaku. Jika user salah ketik (typo) atau menggunakan sinonim (contoh: user mencari "bikin web", tapi database tertulis "membuat website"), sistem akan merespons dengan "Data tidak ditemukan". Tidak bisa memahami konteks kalimat.

2. AI Semantic Search (OpenAI Embeddings + Vector DB)

  • Mekanisme: Mengubah teks menjadi representasi angka multidimensi (Vector). Sistem menghitung jarak kedekatan makna antar angka tersebut menggunakan rumus matematika (Cosine Similarity).
  • Kelebihan: Sangat cerdas dan natural. Tahan terhadap typo, memahami sinonim, singkatan, dan konteks bahasa sehari-hari. User bisa mencari dengan gaya bahasa "ngobrol" dan sistem tetap bisa memberikan hasil yang sangat relevan.
  • Kelemahan: Membutuhkan setup infrastruktur tambahan (Vector Database seperti pgvector), bergantung pada API eksternal (OpenAI) yang berbayar (meskipun sangat murah, sekitar $0.02 per 1 juta token), dan membutuhkan proses komputasi awal saat generate embeddings pertama kali.

Kesimpulan Akhir

Selamat! Anda telah menyelesaikan perjalanan dari dasar hingga arsitektur tingkat lanjut dalam membangun AI Search di Laravel. Anda kini tidak hanya tahu cara memanggil API OpenAI, tetapi juga memahami ilmu di baliknya (Vector dan Cosine Similarity), serta cara merancangnya agar siap untuk skala produksi (Enterprise level) menggunakan Vector Database.

Kini saatnya Anda mengimplementasikan fitur luar biasa ini di proyek Anda sendiri. Baik itu untuk platform e-learning, e-commerce, maupun sistem manajemen dokumen internal perusahaan, pencarian semantik akan secara drastis meningkatkan User Experience (UX) aplikasi Anda. Teruslah bereksperimen, dan Happy Coding!