Pencarian dengan Scout
Pencarian adalah fitur yang paling sering dibangun dua kali: sekali dengan LIKE, lalu sekali lagi dengan benar setelah datanya bertambah. Scout memberi antarmuka yang sama untuk beberapa mesin di belakangnya.
Intisari
LIKE '%kata%'tidak bisa memakai index โ ia memindai seluruh tabel.- Scout menyediakan satu API untuk beberapa driver: database, Meilisearch, Typesense, Algolia.
- Indeks diperbarui otomatis lewat event model โ dan sebaiknya dijalankan lewat antrean.
- Query builder mentah tidak memicu pembaruan indeks; itu penyebab indeks jadi basi.
- Mulai dari driver
database; pindah ke mesin sungguhan saat hasilnya sudah terasa kurang.
Kenapa LIKE tidak cukup
Artikel::where('judul', 'like', "%{$kata}%")->get();
| Masalah | Akibat |
|---|---|
| Wildcard di awal | Index tidak terpakai; seluruh tabel dipindai |
| Tanpa peringkat relevansi | Hasil diurutkan sembarangan, bukan berdasarkan kecocokan |
| Tanpa stemming | "berlari" tidak menemukan "lari" |
| Tanpa toleransi salah ketik | "jakrata" tidak menemukan apa pun |
| Sulit mencari beberapa kolom | OR berlapis yang makin lambat |
| Membebani database utama | Pencarian bersaing dengan lalu lintas biasa |
Memakai Scout
composer require laravel/scout
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
class Artikel extends Model
{
use Searchable;
// Kolom apa yang masuk indeks โ jangan kirim semuanya
public function toSearchableArray(): array
{
return [
'id' => (int) $this->id,
'judul' => $this->judul,
'ringkasan' => $this->ringkasan,
'isi' => strip_tags($this->isi),
'kategori' => $this->kategori->nama,
'penulis' => $this->penulis->nama,
'terbit_pada' => $this->terbit_pada?->timestamp,
];
}
// Hanya artikel terbit yang boleh muncul di pencarian
public function shouldBeSearchable(): bool
{
return $this->terbit_pada?->isPast() ?? false;
}
}
Artikel::search('banjir jakarta')->paginate(20);
Artikel::search('banjir')
->where('kategori_id', 3)
->orderBy('terbit_pada', 'desc')
->take(10)
->get();
php artisan scout:import "App\Models\Artikel" # indeks awal
php artisan scout:flush "App\Models\Artikel"
Memilih driver
| Driver | Kapan | Catatan |
|---|---|---|
database | Awal proyek, data kecil | Tanpa layanan tambahan; memakai kemampuan pencarian database |
collection | Tes | Semuanya di memori |
| Meilisearch | Pilihan mandiri yang baik | Cepat, toleran salah ketik, mudah dijalankan |
| Typesense | Serupa Meilisearch | Kuat untuk pencarian dengan facet |
| Algolia | Terkelola penuh | Sangat cepat; biaya naik seiring jumlah pencarian |
| OpenSearch (via paket) | Sudah di AWS | Terkelola AWS; lebih rumit dioperasikan |
Mulai dari database, dan pindah ketika ada keluhan nyata. Driver database memakai
kemampuan pencarian yang sudah ada di MySQL atau PostgreSQL โ cukup untuk puluhan ribu baris, tanpa satu pun
layanan tambahan yang harus dioperasikan. Berpindah driver nanti tidak mengubah kode aplikasimu, hanya
konfigurasi dan satu perintah scout:import. Itulah nilai utama Scout.
Indeksasi lewat antrean
// config/scout.php
'queue' => true,
Tanpa ini, setiap penyimpanan artikel akan menunggu panggilan jaringan ke mesin pencari sebelum responsnya dikirim. Dengan antrean, penyimpanan tetap cepat dan indeks menyusul beberapa detik kemudian โ pertukaran yang hampir selalu tepat.
Query builder mentah tidak memperbarui indeks. Artikel::where(...)->update([...]) dan
DB::table('artikel')->delete() melewati event Eloquent, jadi Scout tidak tahu apa-apa. Indeksnya
jadi basi tanpa ada yang menyadarinya. Untuk operasi massal, panggil ->searchable() atau
->unsearchable() secara eksplisit setelahnya โ atau jadwalkan
scout:import berkala sebagai jaring pengaman.
Yang perlu diperhatikan di situs konten
- Batasi laju endpoint pencarian. Ia mahal, dan sasaran empuk pengerukan (Fase 4).
- Cache hasil untuk kata yang populer โ
s-maxagependek sudah sangat menolong. - Jangan kirim seluruh isi artikel ke indeks kalau yang dicari cuma judul dan ringkasan; ukuran indeks memengaruhi biaya dan kecepatan.
- Sediakan hasil yang baik untuk kueri kosong โ halaman pencarian tanpa hasil adalah jalan buntu.
- Catat kueri yang tidak menghasilkan apa pun; itu daftar konten yang dicari pembacamu tapi belum kamu punya.
Alternatif: pencarian vektor
// Laravel 13: kemiripan vektor langsung dari query builder
$dokumen = DB::table('dokumen')
->whereVectorSimilarTo('embedding', 'banjir di ibu kota')
->limit(10)
->get();
Pencarian semantik menemukan dokumen yang bermakna mirip, bukan yang mengandung kata yang sama persis. Ia pelengkap pencarian kata kunci, bukan penggantinya: pembaca yang mengetik nama tokoh tetap mengharapkan kecocokan harfiah. Pola yang lazim adalah menggabungkan keduanya.
Latihan: pasang Scout dengan driver database pada model artikel, batasi indeks ke judul
dan ringkasan, lalu bandingkan waktu search() dengan where('judul','like','%โฆ%') pada
100.000 baris. Setelah itu pasang throttle pada rute pencariannya.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.