Takip et

PostgreSQL’de Tam Metin Arama (Full-Text Search) Kullanımı: Ubuntu 16.04 Üzerinde Kapsamlı Bir Rehber

PostgreSQL’de Tam Metin Arama (Full-Text Search) Kullanımı: Ubuntu 16.04 Üzerinde Kapsamlı Bir Rehber Giriş: Tam Metin Arama Neden Önemli? Gü

PostgreSQL’de Tam Metin Arama (Full-Text Search) Kullanımı: Ubuntu 16.04 Üzerinde Kapsamlı Bir Rehber

Giriş: Tam Metin Arama Neden Önemli?

Günümüzün veri odaklı dünyasında, büyük metin koleksiyonları içinde hızlı ve alakalı bilgi bulma yeteneği kritik bir öneme sahiptir. Blog gönderileri, makaleler, ürün açıklamaları, yorumlar veya dokümanlar gibi metin tabanlı veriler, kullanıcıların aradıkları içeriğe kolayca ulaşabilmeleri için etkin bir arama mekanizmasına ihtiyaç duyar. Geleneksel veritabanı sorgulama yöntemleri, bu tür karmaşık arama ihtiyaçlarını karşılamakta yetersiz kalabilir. İşte bu noktada Tam Metin Arama (Full-Text Search – FTS) devreye girer.

Geleneksel Arama Yöntemlerinin Sınırlılıkları

Veritabanlarında metin arama denilince akla ilk gelen yöntemlerden biri LIKE operatörü veya ILIKE (büyük/küçük harf duyarsız) operatörü ile yapılan aramalardır. Örneğin:

SELECT * FROM makaleler WHERE icerik LIKE '%PostgreSQL%';

Bu yöntem basit aramalar için yeterli olabilirken, ciddi sınırlamalara sahiptir:

1. Performans: Büyük metin sütunları üzerinde LIKE '%kelime%' şeklinde yapılan aramalar, indeks kullanamadığı için tam tablo taramasına (full table scan) yol açar ve çok yavaş çalışır.
2. Alaka Düzeyi: LIKE sadece tam eşleşmeler veya kısmi dizgi eşleşmeleri bulur. Bir kelimenin farklı çekimlerini (örneğin “koşmak”, “koştu”, “koşuyor”) veya eşanlamlılarını (örneğin “araba”, “otomobil”) dikkate almaz. Bu da arama sonuçlarının alaka düzeyini düşürür.
3. Dil Desteği: Dilin morfolojik yapısı (kök bulma, ekler) göz ardı edilir. Örneğin, “kitaplar” kelimesini arayan bir kullanıcı “kitap” kelimesini içeren belgeleri bulamayabilir.
4. Sıralama: Arama sonuçları için doğal bir sıralama (en alakalıdan en az alakalıya) mekanizması sunmaz.

Tam Metin Aramanın Temelleri

Tam Metin Arama, bu sınırlamaları aşmak için özel olarak tasarlanmış bir teknolojidir. Bir metin belgesini kelimelere ayırma (tokenization), gereksiz kelimeleri çıkarma (stop words removal), kelimeleri köklerine indirgeme (stemming) ve bu kelimeleri indeksleme gibi adımları içerir. Bu sayede:

* Hızlı Arama: Özel indeks yapıları (PostgreSQL’de genellikle GIN indeksleri) sayesinde çok büyük veri setlerinde bile saniyeler içinde arama yapılabilir.
* Alakalı Sonuçlar: Kelime köklerini ve bazen eşanlamlıları dikkate alarak daha doğru ve alakalı sonuçlar sunar.
* Dil Desteği: Farklı dillerin morfolojik yapılarına uygun analizler yapabilir.
* Sıralama (Ranking): Arama sorgusuyla belgenin ne kadar alakalı olduğunu gösteren bir skor üreterek sonuçları en alakalıdan başlayarak sıralar.
* Vurgulama (Highlighting): Arama terimlerinin metin içinde vurgulanarak gösterilmesini sağlar.

PostgreSQL, kendi içinde güçlü ve esnek bir Tam Metin Arama motoruna sahiptir. Bu makalede, Ubuntu 16.04 işletim sistemi üzerinde PostgreSQL’in bu yeteneklerini nasıl kullanacağımızı adım adım inceleyeceğiz.

PostgreSQL Tam Metin Arama Bileşenleri

PostgreSQL’in Tam Metin Arama sistemi, bir dizi özel veri türü, fonksiyon ve operatör etrafında inşa edilmiştir. Bu bileşenleri anlamak, etkili FTS çözümleri geliştirmek için kritik öneme sahiptir.

tsvector: Belgelerin Vektörel Temsili

tsvector (text search vector), bir metin belgesinin tam metin arama için optimize edilmiş bir gösterimidir. Bir metin tsvector‘a dönüştürüldüğünde, aşağıdaki işlemlerden geçer:

1. Tokenization (Kelimeye Ayırma): Metin kelimelere, sayılara, sembollere vb. ayrılır.
2. Normalization (Normalleştirme): Tüm kelimeler küçük harfe dönüştürülür.
3. Stop Word Removal (Durak Kelime Çıkarma): “ve”, “bir”, “bu”, “o” gibi anlamsız veya yaygın kelimeler (stop words) listeden çıkarılır. Bu kelimeler arama sonuçlarının alaka düzeyini etkilemediği ve indeks boyutunu artırdığı için genellikle göz ardı edilir.
4. Stemming (Kök Bulma): Kelimeler, dilin kurallarına göre köklerine indirgenir. Örneğin, İngilizce’de “running”, “runs”, “ran” kelimeleri “run” köküne indirgenir. Türkçe’de “kitaplar”, “kitabı”, “kitaplık” gibi kelimeler “kitap” köküne indirgenebilir.

Sonuç olarak tsvector, kelime köklerinin ve metin içindeki konumlarının (isteğe bağlı) bir listesidir. Örneğin, “The quick brown fox jumps over the lazy dog” cümlesi İngilizce yapılandırmayla tsvector‘a dönüştürüldüğünde, “quick”:2 “brown”:3 “fox”:4 “jump”:5 “lazi”:8 “dog”:9 gibi bir çıktı verebilir (sayılar kelimelerin orijinal metindeki konumlarını belirtir).

tsquery: Arama Sorgularının Temsili

tsquery (text search query), kullanıcının arama sorgusunun tam metin arama motoru tarafından anlaşılabilecek bir gösterimidir. tsquery‘ler, AND (&), OR (|), NOT (!) ve FOLLOWED BY (<->) gibi mantıksal operatörleri kullanarak kelime köklerini birleştirir. Ayrıca, önek aramaları için :* gibi özel operatörler de kullanılabilir.

Örnekler:
* PostgreSQL & veritabanı: “PostgreSQL” ve “veritabanı” kelimelerini içeren belgeleri bul.
* PostgreSQL | Ubuntu: “PostgreSQL” veya “Ubuntu” kelimelerini içeren belgeleri bul.
* !Windows: “Windows” kelimesini içermeyen belgeleri bul.
* web <-> servis: “web” kelimesinin hemen ardından “servis” kelimesinin geldiği belgeleri bul (cümle arama).
prog:: “prog” ile başlayan kelimeleri bul (programlama, programcı vb.).

Temel Operatörler: @@, &&, ||

PostgreSQL’de tsvector ve tsquery arasındaki eşleşmeyi kontrol etmek için @@ operatörü kullanılır.

* tsvector @@ tsquery: Bir tsvector‘ın bir tsquery ile eşleşip eşleşmediğini kontrol eder. Eğer eşleşiyorsa TRUE, eşleşmiyorsa FALSE döner.

Örnek:

SELECT 'elma armut muz'::tsvector @@ 'elma'::tsquery; -- TRUE
SELECT 'elma armut muz'::tsvector @@ 'kiraz'::tsquery; -- FALSE

&& ve || operatörleri ise tsquery‘lerin kendi içinde mantıksal birleşimini sağlar. Bunlar tsquery veri tipinin bir parçasıdır.

Metin Arama Yapılandırmaları (Text Search Configurations)

PostgreSQL’in Tam Metin Arama sisteminin en güçlü özelliklerinden biri, farklı diller ve özel ihtiyaçlar için özelleştirilebilen “metin arama yapılandırmaları”dır (text search configurations). Bir yapılandırma, bir metni tsvector‘a dönüştürmek için hangi parsörün (ayrıştırıcının), hangi sözlüklerin (dictionaries) ve hangi durak kelime listelerinin kullanılacağını tanımlar.

Her dil için (İngilizce, Almanca, Türkçe vb.) önceden tanımlanmış yapılandırmalar bulunur. Örneğin, english yapılandırması İngilizce kelimeleri köklerine indirgemek için tasarlanmıştır, turkish yapılandırması ise Türkçe kelimeler için benzer işlemleri yapar.

Bir yapılandırma genellikle şu bileşenleri içerir:
* Parser (Ayrıştırıcı): Metni kelimelere, sayılara vb. ayırır.
* Dictionaries (Sözlükler): Kelimelerin köklerini bulmak (stemming), eşanlamlıları işlemek veya durak kelimeleri filtrelemek için kullanılır. PostgreSQL, simple, snowball (kök bulma için), ispell (gelişmiş kök bulma ve eşanlamlılar için), synonym gibi farklı sözlük türlerini destekler.
* Stop Word Lists (Durak Kelime Listeleri): Hangi kelimelerin göz ardı edileceğini belirler.

Ubuntu 16.04 Üzerinde Ortam Kurulumu

Bu bölümde, Ubuntu 16.04 sisteminizde PostgreSQL’i kurmayı ve tam metin arama özelliklerini kullanmaya başlamak için gerekli temel adımları ele alacağız. Ubuntu 16.04 genellikle PostgreSQL 9.5 veya 9.6 sürümünü depolarında barındırır. Bu sürümler FTS için gerekli tüm temel fonksiyonları ve veri tiplerini içerir.

PostgreSQL Kurulumu ve Veritabanı Bağlantısı

Eğer sisteminizde PostgreSQL kurulu değilse, aşağıdaki komutlarla kurabilirsiniz:

sudo apt update
sudo apt install postgresql postgresql-contrib

postgresql-contrib paketi, tam metin arama için gerekli olan pg_trgm gibi ek modülleri ve bazı yararlı fonksiyonları içerir.

Kurulumdan sonra, varsayılan postgres kullanıcısı ile veritabanına bağlanabilirsiniz:

sudo -i -u postgres psql

Bu komut sizi PostgreSQL komut satırı arayüzüne (psql) bağlayacaktır.

Örnek Veritabanı ve Tablo Oluşturma

Şimdi tam metin arama yeteneklerini test etmek için basit bir veritabanı ve tablo oluşturalım:

CREATE DATABASE fts_demo;
\c fts_demo; -- fts_demo veritabanına bağlan

CREATE TABLE makaleler (
    id SERIAL PRIMARY KEY,
    baslik TEXT NOT NULL,
    icerik TEXT NOT NULL,
    yazar TEXT,
    yayin_tarihi DATE DEFAULT CURRENT_DATE
);

-- Örnek veriler ekleyelim
INSERT INTO makaleler (baslik, icerik, yazar) VALUES
('PostgreSQL ile Tam Metin Arama', 'Bu makale PostgreSQL veritabanında tam metin arama özelliklerinin nasıl kullanılacağını anlatmaktadır. Performanslı ve alakalı sonuçlar elde etmek için idealdir.', 'Ali Can'),
('Ubuntu 16.04 Kurulum Rehberi', 'Ubuntu 16.04 LTS sürümünün kurulum adımları ve temel yapılandırmaları bu rehberde detaylıca açıklanmıştır.', 'Ayşe Demir'),
('Veritabanı Optimizasyonu İpuçları', 'PostgreSQL veritabanı performansını artırmak için indeksleme, sorgu optimizasyonu ve bağlantı havuzu kullanımı gibi konulara değinilmektedir.', 'Mehmet Kaya'),
('Yeni Nesil Veritabanı Teknolojileri', 'NoSQL, Graph Database ve NewSQL gibi yeni nesil veritabanı teknolojileri ve kullanım alanları hakkında genel bir bakış sunulmaktadır.', 'Ali Can'),
('Tam Metin Arama Motorları Karşılaştırması', 'PostgreSQL FTS, Elasticsearch ve Solr gibi farklı tam metin arama motorlarının avantajları ve dezavantajları karşılaştırılmaktadır.', 'Zeynep Yılmaz');

Artık makaleler tablomuzda üzerinde arama yapabileceğimiz metin verilerimiz var.

Temel Tam Metin Arama Uygulamaları

Şimdi, PostgreSQL’in FTS bileşenlerini kullanarak basit aramalar yapmaya başlayalım.

Metni tsvector’a Dönüştürme

Bir metin sütununu doğrudan aramak yerine, önce onu tsvector formatına dönüştürmemiz gerekir. Bu işlem to_tsvector() fonksiyonu ile yapılır. Bu fonksiyon, ilk argüman olarak kullanılacak metin arama yapılandırmasının adını (örneğin ‘english’, ‘turkish’) ve ikinci argüman olarak da metin sütununu alır.

Örnek: makaleler tablosundaki icerik sütununu turkish yapılandırmasını kullanarak tsvector‘a dönüştürelim:

SELECT to_tsvector('turkish', icerik) FROM makaleler WHERE id = 1;

Çıktı şöyle bir şeye benzeyecektir:

'anlat':9 'ara':8 'bu':1 'elde':15 'ed':16 'etmek':14 'ideal':17 'kullan':10 'makale':2 'metin':6,12 'ozellik':11 'performans':13 'postgresql':5 'sonuc':15 'tam':7 'veritaban':4

Gördüğünüz gibi, kelimeler köklerine indirgenmiş (“anlatmaktadır” -> “anlat”, “kullanılacağını” -> “kullan”) ve durak kelimeler çıkarılmıştır (“Bu”).

tsquery Oluşturma ve Arama Yapma

Arama yapmak için to_tsquery() fonksiyonunu kullanarak arama terimlerini tsquery formatına dönüştürmeliyiz. Bu fonksiyon da to_tsvector() gibi bir yapılandırma adı ve arama terimlerini alır.

Örnek: “PostgreSQL veritabanı” kelimelerini arayalım.

SELECT id, baslik FROM makaleler
WHERE to_tsvector('turkish', icerik) @@ to_tsquery('turkish', 'PostgreSQL & veritabanı');

Bu sorgu, hem “PostgreSQL” hem de “veritabanı” kelimelerini içeren makaleleri bulacaktır. & operatörü “AND” anlamına gelir.

Eğer “Ubuntu” veya “PostgreSQL” kelimelerinden birini aramak istersek:

SELECT id, baslik FROM makaleler
WHERE to_tsvector('turkish', icerik) @@ to_tsquery('turkish', 'Ubuntu | PostgreSQL');

to_tsvector ve to_tsquery Fonksiyonları

Bu iki fonksiyon, PostgreSQL FTS’nin temelini oluşturur:

* to_tsvector([config regconfig], text text): Belirtilen metni, belirtilen yapılandırmayı kullanarak bir tsvector‘a dönüştürür. config belirtilmezse, varsayılan default_text_search_config kullanılır.
* to_tsquery([config regconfig], text text): Belirtilen metin sorgusunu, belirtilen yapılandırmayı kullanarak bir tsquery‘ye dönüştürür. config belirtilmezse, varsayılan default_text_search_config kullanılır.

Bu fonksiyonların doğru yapılandırmalarla kullanılması, dilin morfolojik yapısına uygun arama yapılmasını sağlar.

Dile Özel Yapılandırmalar ve Türkçe Desteği

PostgreSQL’in FTS gücünün önemli bir kısmı, dile özel yapılandırmaları desteklemesinden gelir. Bu, her dilin kendine özgü kurallarına (kök bulma, durak kelimeler vb.) göre metin analizinin yapılabilmesi demektir.

Neden Dile Özel Yapılandırmalar Kullanmalıyız?

Farklı dillerin farklı morfolojik yapıları vardır. İngilizce basit bir kök bulma algoritmasına sahipken, Türkçe gibi sondan eklemeli diller çok daha karmaşık bir yapıya sahiptir. “Gelecek”, “gelecektir”, “gelecekteki” kelimelerinin hepsinin kökü “gel” olabilir. Eğer doğru dile özel yapılandırma kullanılmazsa, arama sonuçları alakasız veya eksik olabilir. Örneğin, İngilizce yapılandırmasıyla Türkçe bir metni aramak, çoğu zaman hatalı sonuçlar verecektir çünkü İngilizce kök bulucular Türkçe eklerini tanımaz.

Mevcut Yapılandırmaları İnceleme

PostgreSQL’de hangi metin arama yapılandırmalarının yüklü olduğunu \dF komutuyla görebilirsiniz:

\dF

Bu komut, sistemde tanımlı tüm metin arama yapılandırmalarını listeler. Genellikle english, simple, french, german, turkish gibi yapılandırmalar bulunur.

Mevcut varsayılan yapılandırmayı öğrenmek için:

SHOW default_text_search_config;

Bu değeri değiştirmek için:

SET default_text_search_config = 'turkish';
-- veya kalıcı olarak postgresql.conf dosyasında ayarlayabilirsiniz.

Türkçe İçin Yapılandırma: ‘turkish’

PostgreSQL, Türkçe için özel bir metin arama yapılandırması (turkish) sunar. Bu yapılandırma, Türkçe kelimeleri doğru bir şekilde köklerine indirgemek (stemming) ve Türkçe durak kelimeleri filtrelemek için tasarlanmıştır.

turkish yapılandırmasını kullanarak bir metni tsvector‘a dönüştürelim ve arama yapalım:

-- Türkçe metin örneği
SELECT to_tsvector('turkish', 'kitaplarımı okuyordum ve çok ilginçti') AS turkce_tsvector;

-- Çıktı: 'ilginç':6 'kitap':1 'oku':2

-- Türkçe arama sorgusu örneği
SELECT to_tsquery('turkish', 'kitap & ilginç') AS turkce_tsquery;

-- Çıktı: 'kitap' & 'ilginç'

Gördüğünüz gibi, “kitaplarımı” kelimesi “kitap” köküne indirgenmiş, “okuyordum” kelimesi “oku” köküne indirgenmiş ve “ve”, “çok” gibi durak kelimeler çıkarılmıştır.

Özel Sözlükler ve Kök Bulma (Stemming)

PostgreSQL’in turkish yapılandırması genellikle ispell veya snowball tabanlı bir sözlük kullanır. ispell sözlükleri, daha gelişmiş kök bulma ve eşanlamlı desteği sunabilir. Türkçe için ispell sözlüğü kullanmak, daha doğru kök bulma sonuçları elde etmenizi sağlar.

Ubuntu 16.04’te ispell sözlüklerini kurmak için:

sudo apt install ispell-tr

Bu paket, Türkçe ispell sözlük dosyalarını sisteme kurar. PostgreSQL, bu dosyaları otomatik olarak bulup kullanabilir veya özel bir yapılandırma oluşturarak bunları belirtebilirsiniz.

PostgreSQL’de özel bir metin arama yapılandırması oluşturmak, var olan yapılandırmaları kopyalamak ve sözlükleri, durak kelimeleri veya ayrıştırıcıları değiştirmek anlamına gelir. Bu, özellikle iş alanına özgü terimler veya özel durak kelimeleriniz olduğunda faydalıdır. Ancak çoğu durumda, turkish yapılandırması yeterli olacaktır.

Performans İçin İndeksleme: GIN ve GiST

Tam metin aramanın asıl gücü, indeksleme ile ortaya çıkar. to_tsvector() ve to_tsquery() fonksiyonları ile arama yapabilirsiniz, ancak bu, her sorguda tüm metni taramak ve dönüştürmek anlamına gelir ki bu da çok büyük veri setlerinde kabul edilemez derecede yavaş olacaktır. Bu nedenle, tsvector sütunları üzerinde özel indeksler oluşturmak zorunludur.

İndekslemenin Önemi

İndeksler, veritabanının belirli bir sütundaki verileri daha hızlı bulmasını sağlayan veri yapılarıdır. Tam metin arama için, PostgreSQL iki ana indeks türünü destekler:

1. GIN (Generalized Inverted Index): Tam metin arama için en yaygın ve genellikle en iyi performansı sunan indeks türüdür. Tersine indeks (inverted index) prensibine dayanır; yani her kelime kökü için, o kelimeyi içeren belgelerin bir listesini tutar. Bu, belirli bir kelimeyi içeren belgeleri hızla bulmayı sağlar.
2. GiST (Generalized Search Tree): GiST indeksleri de kullanılabilir, ancak GIN indeksleri tam metin arama için genellikle daha hızlıdır ve daha az depolama alanı kullanır. GiST, özellikle arama terimlerinin belge içindeki konumlarına dayalı karmaşık aramalar (örneğin cümle aramaları) için bazen tercih edilebilir. Ancak genel amaçlı FTS için GIN daha yaygındır.

Bizim örneğimizde, GIN indeksini kullanacağız.

makaleler tablosuna bir tsvector sütunu ekleyelim ve bu sütun üzerinde bir GIN indeksi oluşturalım:

ALTER TABLE makaleler ADD COLUMN tsv TSVECTOR;

Şimdi bu tsv sütununu mevcut verilerimizle dolduralım:

UPDATE makaleler SET tsv = to_tsvector('turkish', baslik || ' ' || icerik);

Burada baslik ve icerik sütunlarını birleştirerek tek bir tsvector oluşturduk. || ' ' || ile aralarına bir boşluk koymak, kelimelerin birbirine yapışmasını engeller.

Şimdi tsv sütunu üzerinde GIN indeksini oluşturalım:

CREATE INDEX tsv_gin ON makaleler USING GIN (tsv);

Bu indeks, makaleler tablosundaki tsv sütunundaki tüm tsvector verilerini indeksleyecektir. Artık aramalarımız bu indeks üzerinden çok daha hızlı yapılacaktır.

tsvector Sütununu Otomatik Güncelleme: Trigger Kullanımı

tsv sütununu manuel olarak güncellemek pratik değildir. makaleler tablosuna yeni bir kayıt eklendiğinde veya mevcut bir kayıt güncellendiğinde tsv sütununun otomatik olarak güncellenmesini sağlamalıyız. PostgreSQL 12 ve üzeri sürümlerde “Generated Columns” bu işi daha zarif bir şekilde yaparken, Ubuntu 16.04’teki PostgreSQL sürümleri (9.5/9.6) için trigger (tetikleyici) kullanmak en uygun yöntemdir.

Bir trigger fonksiyonu oluşturalım:

CREATE OR REPLACE FUNCTION update_makaleler_tsv() RETURNS TRIGGER AS $$
BEGIN
    NEW.tsv := to_tsvector('turkish', NEW.baslik || ' ' || NEW.icerik);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Bu fonksiyon, NEW (yeni eklenecek veya güncellenecek satır) üzerindeki baslik ve icerik sütunlarını kullanarak tsv sütununu doldurur.

Şimdi bu fonksiyonu makaleler tablosuna bir trigger olarak bağlayalım:

CREATE TRIGGER tsv_update_trigger
BEFORE INSERT OR UPDATE ON makaleler
FOR EACH ROW EXECUTE PROCEDURE update_makaleler_tsv();

Bu trigger, her INSERT veya UPDATE işleminden önce update_makaleler_tsv fonksiyonunu çalıştıracak ve tsv sütununu otomatik olarak güncelleyecektir.

Artık aramalarımızı tsv sütunu üzerinden yapabiliriz:

SELECT id, baslik FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'PostgreSQL & veritabanı');

Bu sorgu, GIN indeksini kullanarak çok daha hızlı çalışacaktır.

Gelişmiş Tam Metin Arama Özellikleri

PostgreSQL FTS, sadece temel eşleşmelerin ötesinde, arama deneyimini zenginleştiren birçok gelişmiş özellik sunar.

Arama Sonuçlarını Sıralama (Ranking): ts_rank ve ts_rank_cd

Arama sonuçlarının alaka düzeyine göre sıralanması, kullanıcı deneyimi için kritik öneme sahiptir. PostgreSQL, ts_rank() ve ts_rank_cd() fonksiyonları ile bu sıralamayı sağlar. Bu fonksiyonlar, bir tsvector ile bir tsquery arasındaki eşleşmenin ne kadar güçlü olduğunu gösteren bir sayısal skor (rank) döndürür.

Rank skoru, arama terimlerinin belgede kaç kez geçtiği, ne kadar seyrek olduğu, belge içindeki konumları (örneğin başlıkta mı, içerikte mi) gibi faktörlere dayanır.

* ts_rank(weights float4[], vector tsvector, query tsquery [, normalization integer]): Ağırlıklar (weights) ile belirli alanlara öncelik verebilirsiniz.
* ts_rank_cd(vector tsvector, query tsquery [, normalization integer]): “Cover Density” algoritmasını kullanan bir diğer sıralama fonksiyonudur. Genellikle ts_rank‘ten daha iyi sonuçlar verdiği düşünülür.

Ağırlıklandırma ve Sıralama Algoritmaları:
ts_rank fonksiyonunda weights parametresi, farklı alanlara (örneğin başlık, anahtar kelimeler, içerik) farklı ağırlıklar vermenizi sağlar. PostgreSQL, tsvector içinde kelimelerin konumlarını saklayabilir ve bu konumları A, B, C, D etiketleriyle işaretleyebilir. A en yüksek ağırlık, D en düşük ağırlıktır.

Örneğin, baslik sütununa daha yüksek ağırlık vermek için tsvector oluştururken bu etiketleri kullanabiliriz:

-- Trigger fonksiyonunu güncelleyelim
CREATE OR REPLACE FUNCTION update_makaleler_tsv() RETURNS TRIGGER AS $$
BEGIN
    NEW.tsv :=
        setweight(to_tsvector('turkish', NEW.baslik), 'A') ||
        setweight(to_tsvector('turkish', NEW.yazar), 'B') || -- Yazar ismine de ağırlık verelim
        setweight(to_tsvector('turkish', NEW.icerik), 'C');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Mevcut verileri güncelleyelim
UPDATE makaleler SET tsv = update_makaleler_tsv(); -- Trigger manuel çalıştırılmaz, update ile tetiklenir
-- veya basitçe:
UPDATE makaleler SET baslik = baslik; -- tüm satırları tetiklemek için basit bir update

-- Şimdi sıralı arama yapalım:
SELECT
    id,
    baslik,
    ts_rank_cd(tsv, to_tsquery('turkish', 'PostgreSQL & veritabanı')) AS rank_score
FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'PostgreSQL & veritabanı')
ORDER BY rank_score DESC;

Bu sorgu, arama terimlerini içeren makaleleri, alaka düzeyine göre en yüksek skordan başlayarak sıralayacaktır.

Vurgulama ve Özet Oluşturma: ts_headline

Kullanıcılar, arama terimlerinin metin içinde nerede geçtiğini görmek isterler. ts_headline() fonksiyonu, arama terimlerinin geçtiği ilgili kısımları (snippet) çıkarır ve bu terimleri vurgulayarak gösterir.

ts_headline([config regconfig], document text, query tsquery [, options text])

Örnek:

SELECT
    id,
    baslik,
    ts_headline('turkish', icerik, to_tsquery('turkish', 'PostgreSQL & veritabanı'),
                'StartSel=, StopSel=, MaxFragments=3, FragmentDelimiter=..., MaxWords=10, MinWords=5') AS snippet
FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'PostgreSQL & veritabanı')
ORDER BY ts_rank_cd(tsv, to_tsquery('turkish', 'PostgreSQL & veritabanı')) DESC;

Bu sorgu, “PostgreSQL” ve “veritabanı” kelimelerini içeren makalelerden ilgili kısımları etiketleri arasına alarak vurgulayacak ve bir özet sunacaktır. options parametresiyle vurgulama biçimini ve özetin uzunluğunu ayarlayabilirsiniz.

Cümle Arama (Phrase Search)

Bazen belirli bir kelime grubunun belirli bir sırada ve yakınlıkta bulunması istenir (örneğin “tam metin arama”). Bu tür aramalar için <-> operatörü kullanılır. Bu operatör, iki terimin art arda gelmesini gerektirir. N <-> M ise N kelimenin M kelime ile arasında bir boşluk olmasını ifade eder.

-- "tam metin" ifadesini ara
SELECT id, baslik FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'tam <-> metin');

-- "veritabanı performans" ifadesini ara
SELECT id, baslik FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'veritabanı <-> performans');

Önek Eşleştirme (Prefix Matching)

Kullanıcılar genellikle bir kelimenin sadece başlangıcını yazarak arama yapmak isterler (örneğin “prog” yazarak “programlama”, “programcı” gibi kelimeleri bulmak). tsquery içinde :* operatörü ile önek eşleştirme yapabilirsiniz.

-- "veritabanı" kelimesinin kökünden türeyen ve "opt" ile başlayan kelimeleri ara (optimizasyon, optimize vb.)
SELECT id, baslik FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'veritabanı & opt:*');

Eşanlamlılar ve Özel Sözlükler

PostgreSQL FTS, eşanlamlılar (synonyms) ve özel sözlükler kullanarak arama sonuçlarının alaka düzeyini daha da artırmanıza olanak tanır.

* synonym Sözlüğü: Belirli kelimeler veya kelime grupları için eşanlamlılar tanımlamanızı sağlar. Örneğin, “otomobil” arandığında “araba” içeren belgelerin de bulunmasını sağlayabilirsiniz.
Bir synonym sözlüğü oluşturmak için önce bir sözlük dosyası hazırlamanız gerekir (örneğin my_synonyms.txt):

araba otomobil
    pc bilgisayar

Daha sonra PostgreSQL içinde bu sözlüğü tanımlarsınız:

CREATE TEXT SEARCH DICTIONARY my_synonym_dict (
        TEMPLATE = synonym,
        SYNONYMS = '/path/to/my_synonyms.txt' -- Dosya yolu
    );

Ve ardından bu sözlüğü bir yapılandırmaya eklersiniz.

* ispell Sözlüğü: Daha gelişmiş kök bulma ve eşanlamlı desteği sunar. Özellikle Türkçe gibi diller için snowball‘dan daha iyi sonuçlar verebilir. ispell-tr paketi ile kurulan Türkçe ispell sözlükleri, PostgreSQL yapılandırmalarında kullanılabilir.

Bu gelişmiş özellikler, arama motorunuzu kullanıcıların beklentilerine daha uygun hale getirmenizi sağlar.

Uçtan Uca Bir Örnek Uygulama

Şimdiye kadar öğrendiklerimizi bir araya getirerek daha kapsamlı bir örnek oluşturalım.

Örnek Tablo Yapısı ve Veri Ekleme

Yukarıda oluşturduğumuz makaleler tablosunu ve verileri kullanmaya devam edelim.

tsvector Sütunu ve Trigger Oluşturma

Daha önce oluşturduğumuz tsv sütununu ve tsv_update_trigger‘ı kullandığımızı varsayıyoruz. Eğer henüz yapmadıysanız, yukarıdaki “tsvector Sütununu Otomatik Güncelleme: Trigger Kullanımı” bölümündeki adımları uygulayın. Özellikle setweight kullanarak baslik ve yazar sütunlarına daha yüksek ağırlık verdiğimiz trigger fonksiyonunu kullanmak önemlidir.

-- Trigger fonksiyonunu tekrar oluşturalım veya güncelleyelim
CREATE OR REPLACE FUNCTION update_makaleler_tsv() RETURNS TRIGGER AS $$
BEGIN
    NEW.tsv :=
        setweight(to_tsvector('turkish', NEW.baslik), 'A') ||
        setweight(to_tsvector('turkish', NEW.yazar), 'B') ||
        setweight(to_tsvector('turkish', NEW.icerik), 'C');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger'ı oluşturalım (eğer yoksa)
CREATE TRIGGER tsv_update_trigger
BEFORE INSERT OR UPDATE ON makaleler
FOR EACH ROW EXECUTE PROCEDURE update_makaleler_tsv();

-- Mevcut verileri trigger ile güncelleyelim (herhangi bir sütunu update ederek trigger'ı tetikleyebiliriz)
UPDATE makaleler SET baslik = baslik;

GIN İndeksi Ekleme

tsv sütunu üzerinde GIN indeksini zaten oluşturmuştuk:

CREATE INDEX IF NOT EXISTS tsv_gin ON makaleler USING GIN (tsv);

IF NOT EXISTS ile indeksin zaten var olup olmadığını kontrol ederek hata almayı önleriz.

Sıralı ve Vurgulu Arama Sorguları

Şimdi, kullanıcıların en alakalı sonuçları görmelerini sağlayan ve arama terimlerini vurgulayan bir sorgu oluşturalım. “PostgreSQL performans” kelime grubunu arayalım.

SELECT
    id,
    baslik,
    ts_rank_cd(tsv, to_tsquery('turkish', 'PostgreSQL & performans')) AS rank_score,
    ts_headline('turkish', icerik, to_tsquery('turkish', 'PostgreSQL & performans'),
                'StartSel=, StopSel=, MaxFragments=3, FragmentDelimiter=..., MaxWords=15, MinWords=5') AS snippet
FROM makaleler
WHERE tsv @@ to_tsquery('turkish', 'PostgreSQL & performans')
ORDER BY rank_score DESC;

Bu sorgu:
1. to_tsquery('turkish', 'PostgreSQL & performans') ile arama sorgusunu oluşturur.
2. tsv @@ ... ile tsv sütununda eşleşen belgeleri bulur (GIN indeksi sayesinde hızlı).
3. ts_rank_cd() ile her belgenin alaka skorunu hesaplar.
4. ORDER BY rank_score DESC ile en alakalıdan en az alakalıya doğru sıralar.
5. ts_headline() ile arama terimlerinin geçtiği yerleri vurgulayarak bir özet (snippet) oluşturur.

Bu örnek, PostgreSQL’in Tam Metin Arama yeteneklerini birleştirerek gerçek dünya senaryolarında nasıl kullanılabileceğini göstermektedir.

Dikkat Edilmesi Gerekenler ve En İyi Uygulamalar

PostgreSQL FTS güçlü bir araç olsa da, en iyi performansı ve doğru sonuçları elde etmek için bazı önemli noktaları göz önünde bulundurmak gerekir.

Performans Ayarları ve Optimizasyon

* GIN İndeksi Bakımı: GIN indeksleri, UPDATE ve DELETE işlemleri sonrasında şişebilir (bloat). Düzenli VACUUM FULL veya REINDEX işlemleri gerekebilir, ancak bunlar tablo kilitlenmelerine neden olabilir. PostgreSQL’in otomatik VACUUM ayarlarını doğru yapmak önemlidir.
* work_mem Ayarı: Karmaşık sorgular ve büyük veri setleri için work_mem ayarını artırmak, indeks oluşturma ve bazı arama işlemleri sırasında performansı artırabilir.
* shared_buffers Ayarı: Veritabanının genel performansı için shared_buffers ayarının doğru yapılması kritik öneme sahiptir.
* default_text_search_config: Oturum veya veritabanı genelinde varsayılan yapılandırmayı doğru dile ayarlamak, her to_tsvector ve to_tsquery çağrısında yapılandırma belirtme ihtiyacını ortadan kaldırır ve kod okunabilirliğini artırır.
* Trigger vs. Generated Columns (PostgreSQL 12+): Ubuntu 16.04’teki PostgreSQL sürümlerinde trigger kullanmak zorunlu olsa da, daha yeni sürümlerde GENERATED sütunlar, tsvector sütununu otomatik güncellemenin daha temiz ve verimli bir yoludur. Mümkünse daha yeni bir PostgreSQL sürümüne geçiş düşünülmelidir.

Doğru Yapılandırma Seçimi

Kullanılan dil için en uygun metin arama yapılandırmasını seçmek çok önemlidir. Türkçe metinler için turkish yapılandırması kullanılmalıdır. Eğer iş alanına özgü terimler veya özel eşanlamlılar varsa, özel sözlükler ve yapılandırmalar oluşturmak gerekebilir.

Kaynak Tüketimi ve Ölçeklenebilirlik

* Depolama Alanı: tsvector sütunları ve GIN indeksleri, orijinal metin verilerinden önemli ölçüde daha fazla depolama alanı kaplayabilir. Özellikle çok büyük metin verileriyle çalışırken bu durum göz önünde bulundurulmalıdır.
* Bellek Kullanımı: GIN indeksleri, arama sırasında bellek yoğun olabilir. Büyük sorgular veya çok sayıda eşzamanlı sorgu, sistem belleğini zorlayabilir.
* İşlemci Yükü: to_tsvector dönüşüm işlemi ve indeksleme, özellikle INSERT ve UPDATE işlemleri sırasında CPU yoğun olabilir. Trigger kullanılıyorsa, her yazma işleminde bu maliyet oluşacaktır.

Harici Arama Motorları ile Karşılaştırma

PostgreSQL FTS, birçok uygulama için mükemmel bir çözümdür. Ancak çok büyük ölçekli uygulamalar (terabaytlarca metin verisi, saniyede binlerce arama sorgusu) veya çok karmaşık, çok dilli arama gereksinimleri için Elasticsearch veya Apache Solr gibi harici arama motorları daha uygun olabilir. Bu sistemler, dağıtık mimarileri, gelişmiş analiz yetenekleri ve daha zengin API’leri ile bu tür senaryolarda öne çıkarlar.

PostgreSQL FTS’nin avantajları:
* Veritabanı içinde entegre olması, ayrı bir sistem kurma ve yönetme ihtiyacını ortadan kaldırır.
* İşlem bütünlüğünü (ACID) korur.
* Daha küçük ve orta ölçekli projeler için kurulumu ve kullanımı kolaydır.

Nihayetinde, doğru arama çözümünü seçmek, projenin ölçeğine, bütçesine, geliştirici becerilerine ve spesifik arama gereksinimlerine bağlıdır.

Sonuç

PostgreSQL’in Tam Metin Arama (Full-Text Search) yetenekleri, metin tabanlı veriler içinde hızlı, alakalı ve dilbilgisel olarak doğru aramalar yapma konusunda güçlü ve esnek bir çözüm sunar. Ubuntu 16.04 gibi bir Linux ortamında PostgreSQL’i kurup yapılandırarak, tsvector ve tsquery veri tipleri, to_tsvector(), to_tsquery(), ts_rank(), ts_headline() fonksiyonları ve GIN indeksleri gibi temel bileşenleri kullanarak etkili bir arama motoru oluşturabilirsiniz.

Dile özel yapılandırmalar, özellikle Türkçe gibi morfolojik açıdan zengin diller için, arama sonuçlarının kalitesini önemli ölçüde artırır. Trigger’lar aracılığıyla tsvector sütunlarının otomatik olarak güncellenmesi ve GIN indekslerinin kullanılması, büyük veri setlerinde bile yüksek performanslı aramaların yapılmasını sağlar.

Bu makalede sunulan bilgiler ve örnekler, PostgreSQL FTS’nin temel prensiplerini ve pratik uygulamalarını anlamanıza yardımcı olmayı amaçlamaktadır. Kendi projelerinizde bu güçlü özelliği kullanarak kullanıcılarınıza daha iyi bir arama deneyimi sunabilirsiniz. Unutmayın, her sistemde olduğu gibi, performans optimizasyonu ve doğru yapılandırma seçimi, başarılı bir FTS uygulamasının anahtarlarıdır.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorumları Göster (2)

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.