LIKE Ölçeklenmiyor
SQLite'da daha önce metin araması yaptıysanız muhtemelen LIKE '%kelime%' kullanmışsınızdır. Küçük tablolarda iş görür, büyüklerde ise çöker. İşin içine girebilecek hiçbir index yoktur — SQLite her satırı tek tek taramak, küçük harfe çevirmek ve içinde aranan parçayı kontrol etmek zorundadır. Kelime sınırları, sıralama, çok kelimeli sorgular ve önek eşleşmesi gibi her şeyi de elinizle çözmeniz gerekir.
İşte tam burada SQLite'ın yerleşik cevabı olan FTS5 devreye giriyor. FTS5, metin sütunlarınız üzerinde ters indeks (inverted index) tutan, küçük bir sorgu dili anlayan ve sonuçları BM25 ile sıralayan bir virtual table türüdür. SQLite ile birlikte hazır gelir — kurulacak ek bir eklenti yok.
FTS5 Virtual Table Oluşturma
FTS5 tablosunu CREATE VIRTUAL TABLE ... USING fts5(...) söz dizimiyle, indekslemek istediğiniz metin sütunlarını listeleyerek oluşturursunuz:
Dikkat etmeniz gereken üç nokta var. Sütunların tipi yok — FTS5 her şeyi metin olarak ele alır. MATCH operatörü bir sütuna değil, tablo adına karşı çalışır (posts MATCH ...). Ve sorgu büyük/küçük harfe duyarsız olup tokenize edildiği için 'sqlite' ifadesi satırlardaki SQLite kelimesini de bulur.
MATCH Sorgu Dili
MATCH tek bir kelimeyle sınırlı değildir. Sorgu metninin kendine ait küçük bir grameri vardır:
Her birinin işi şu:
'fts5 AND prefix'— her iki kelime de bulunmalı (sıra fark etmez, satırın herhangi bir yerinde).'"keep fts"'— birebir bu sırayla geçen ifade.'trig*'— önek aramasıdır;trigger,triggers,trigonometrygibi sonuçları yakalar.'index NOT trigger'—indexgeçen amatriggergeçmeyen kayıtlar.
Tek bir sütunda arama yapmak isterseniz column:term kalıbını kullanabilirsiniz, örneğin 'title:sqlite'. Dilbilgisinin tamamı; gruplama için parantezleri ve alternatifler için OR operatörünü de kapsıyor — yani tipik bir arama motorundan beklediğiniz yapının aynısı.
BM25 ile sqlite sıralama
FTS5 varsayılan olarak her satıra gizli bir rank sütunu ekler. Bu sütun BM25 alaka skorunu tutar — değer ne kadar küçükse eşleşme o kadar isabetlidir. En alakalı sonuçları üste almak için bu sütuna göre sıralamanız yeterli:
Bazı sütunlara diğerlerinden daha fazla ağırlık vermek ister misin? bm25() fonksiyonunu, sütun ağırlıklarını tanım sırasına göre vererek çağırmanız yeterli:
İlk gönderi kazanır çünkü sqlite kelimesi sadece body (1× ağırlıklı) yerine title alanında (10× ağırlıklı) geçiyor. Ağırlıkları uygulamanızın nasıl sıralama yapmasını istediğinize göre seçin.
FTS5 indeksini veriyle senkron tutmak
En basit FTS5 tablosu, metnin kendi kopyasını içinde tutar. Sadece veri eklediğiniz log tarzı senaryolar için bu yeterli; ama çoğu uygulamanın zaten asıl bir tablosu vardır ve FTS'in bu tabloyu takip etmesini ister. Burada temiz çözüm, external content (dış içerikli) bir FTS tablosu ve buna eşlik eden üç trigger kullanmaktır.
content='articles' ifadesi FTS5'e metni kendi içinde tutmamasını söyler — gerektiğinde gidip articles tablosundan okur. Trigger'lar ise yapılan yazma işlemlerini FTS dizinine yansıtır. Böylece articles asıl kaynak olur, articles_fts da onun yanında duran salt arama yapısına dönüşür.
İlk bakışta tuhaf duran o INSERT INTO articles_fts(articles_fts, ...) VALUES ('delete', ...) satırı aslında FTS5'in komut sözdizimidir; dizine "şu satırı sil" demenin yoludur.
Snippet ve vurgulama ile sonuç önizlemesi
Arama sonuçlarında genellikle eşleşen kelimelerin öne çıkarıldığı kısa bir önizleme isteriz. FTS5 bunun için iki hazır fonksiyon sunar:
highlight(tablo, sütun_indeksi, aç, kapat): ilgili sütunun tam metnini döndürür ve eşleşen tokenları sarar.snippet(tablo, sütun_indeksi, aç, kapat, üç_nokta, token_sayısı): eşleşmenin etrafında kısa bir alıntı döndürür.
Sütun indeksleri sıfırdan başlar ve tanımlama sırasına göredir. Her arama arayüzünde gördüğümüz "eşleşen kelimeler sarı renkte" davranışının temel yapı taşları bunlar.
Dikkat Edilmesi Gereken Tuzaklar
İnsanların sıkça takıldığı birkaç nokta var:
MATCHyalnızca FTS tablolarında çalışır. Normal bir sütundaMATCHkullanamazsınız. Mevcut bir tablo üzerinde arama yapmanız gerekiyorsa, yukarıda anlatılan external-content (harici içerik) yaklaşımını tercih edin.rank'e göre sıralamayı unutmayın. Bunu yapmazsanız FTS5 satırları depolama sırasına göre döndürür ve bunun alaka düzeyiyle hiçbir alakası yoktur.- Tokenizer seçimi önemlidir. Varsayılan tokenizer (
unicode61) Unicode kelime sınırlarına göre böler ve küçük harfe çevirir. Kök bulma (yanirun'ınrunningile eşleşmesi) içinportertokenizer'ını kullanın:USING fts5(body, tokenize='porter'). - FTS5 bir yazım hatası toleranslı motor değildir. Önek (prefix) eşleştirmesi yapar, bulanık (fuzzy) eşleştirme yapmaz. "Bunu mu demek istediniz?" gibi bir davranış istiyorsanız, bunu FTS5'in üzerine kendiniz inşa etmelisiniz.
- İçeriksiz tablolar (
content='') daha küçüktür ama kayıplıdır. Arama yapabilirsiniz fakat orijinal metni geri alamazsınız — yalnızca rowid'e ulaşabilirsiniz. Metni başka bir yerde sakladığınız durumlarda işe yarar.
Sırada: Pencere Fonksiyonları (Window Functions)
FTS5, metin arama tarafını hallediyor. Bir sonraki sayfada farklı türden bir gelişmiş sorgu yapısını ele alacağız: pencere fonksiyonları. Bunlar, satırlarınızı agregasyonla tek satıra düşürmeden çalışan toplamlar, sıralamalar ve grup bazlı analitikler hesaplamanızı sağlar.
Sıkça Sorulan Sorular
SQLite'ta FTS5 nedir?
FTS5, SQLite'ın yerleşik tam metin arama eklentisidir. CREATE VIRTUAL TABLE ... USING fts5(...) ile özel bir sanal tablo oluşturup MATCH operatörü ile sorgularsın. Veriyi eklerken metni token'lara ayırır, ters indeks (inverted index) tutar ve sonuçları varsayılan olarak BM25 ile sıralar.
MATCH ile LIKE arasındaki fark nedir?
LIKE satır satır gezerek substring araması yapar ve kelime sınırlarını umursamaz. MATCH ise FTS5'in ters indeksini kullandığı için büyük tablolarda çok hızlıdır; üstelik token'ları, prefix sorgularını (term*), boolean operatörleri (AND, OR, NOT) ve cümle aramasını ("tam ifade") anlar. MATCH yalnızca FTS sanal tablolarında çalışır.
FTS5 indeksini gerçek tabloyla nasıl senkron tutarım?
İki yol var: ya gerçek tablonu işaret eden contentless ya da external-content bir FTS5 tablosu kullanırsın, ya da AFTER INSERT, AFTER UPDATE ve AFTER DELETE trigger'ları yazıp değişiklikleri FTS tablosuna yansıtırsın. External-content yaklaşımı (content='posts') metni iki kez saklamamanı sağlar.
SQLite'ta tam metin arama sonuçlarını nasıl sıralarım?
FTS5'in gizli bir rank kolonu vardır ve BM25 skorunu döner (küçük olan daha iyidir). Doğrudan ORDER BY rank yazabilirsin. Skoru açıkça istiyorsan bm25(table) çağrısını kullan; başlığı gövdeden daha ağırlıklı yapmak için bm25(posts, 10.0, 1.0) gibi kolon ağırlıkları da geçebilirsin.