---
title: 'Sorgu Dili Yapısı'
source: 'https://academia.sh/tr/kurslar/arama-motorlari/sorgu-dili-yapisi'
course: 'Arama Motorları ve Metin Erişimi'
language: tr
updated: '2026-08-17T18:09:01+00:00'
license: 'CC BY-SA 4.0'
---

# Sorgu Dili Yapısı

Aynı katalog sorusu düz metin ve yapılandırılmış biçimde sorulur: koşulların nasıl birleştiği söylenmediğinde dönen küme 4.211 ile 71 belge arasında değişir, bileşik sorgu bir ağaç olarak değerlendirilir ve ağacın değerlendirme sırası kümeyi değiştirmeden taranan gönderi girişini düşürür.

Önceki konu ters dizini kurdu ve belgenin dizine girip çıkmasını ele aldı. Oraya kadar
sorulan her soru tek bir terimdi: bir sözcük verildi, gönderi listesi okundu, belgeler
döndü. Kütüphane katalogunda sorulan sorular böyle değildir. "Çocuk edebiyatı üzerine
kısa öyküler" üç ayrı koşul taşır ve bu koşulların nasıl birleştiği söylenmemiştir:
üçü de zorunlu mudur, biri yeter mi, "kısa" sözcüğü kitabın adında mı özetinde mi
aranır? Daha sessiz ikinci soru şudur: eşleşen belgeler hangi sırayla dönecek?

Bu ders iki katmanı ayırır. **Yaprak sorgu** (leaf query) tek bir alanda tek bir terimi
arar ve karşılığı bir gönderi listesidir. **Bileşik sorgu** (compound query) yaprakları
bir ağaçta birleştirir; ağacın kök düğümü bir bağlaçtır, yaprakları terimlerdir. Aynı
katalog sorusu düz metin olarak da yazılabilir, ağaç olarak da — ikisi farklı sayıda
belge döndürür ve farklı sayıda gönderi girişi okur.

## Derlem

**SI1 (varsayım):** derlem, kütüphane katalogundan üretilmiş 6.000 kitap kaydıdır; tohum
271828'dir ve üretim belirlenimlidir. Her kayıt bir ad, bir özet, iki etiket, bir yazar,
bir yıl ve bir dil taşır. `ad` ile `ozet` metin alanı, `etiket` anahtar sözcük alanıdır.
**SI2 (varsayım):** sorgu terimleri dizindeki biçimle yazılır; ekler ve büyük harf
çözümleyici zincirinin işidir, bu konuda çözümleyici sabittir.

```js
// derlem.mjs — kutuphane katalogu derlemi ve konum bilgili ters dizin.
// Tohum 271828, 6000 belge; konudaki butun olcumler bu modulu paylasir.
export const TOHUM = 271828, N = 6000;
let c = TOHUM;                                       // gorunur tohum, belirlenimli uretec
const r = () => { c = (c + 0x6d2b79f5) | 0; let t = Math.imul(c ^ (c >>> 15), 1 | c);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
const sec = (d, e = 1) => d[Math.floor(r() ** e * d.length)];   // e>1: bas ogeler sik, son ogeler seyrek

const SIFAT = ["eleştirel", "kısa", "resimli", "seçme", "karşılaştırmalı"];
const TUR = ["deneme", "öykü", "roman", "inceleme", "antoloji"];
const ALAN = ["çocuk", "tarih", "bilim", "felsefe", "toplum"];
const SON = ["seçkisi", "kitabı", "derlemesi", "dizisi"];
const NIT = ["kapsamlı", "özlü", "tanıtıcı", "tartışmalı"];
const KONU = ["eğitim", "göç", "kent", "bellek", "doğa", "müzik", "hukuk", "kimlik", "emek", "aile",
  "savaş", "gelenek", "sağlık", "yolculuk", "mimarlık", "arkeoloji", "denizcilik", "gökbilim"];
const YAZAR = ["Ahmet Yıldız", "Zeynep Kaya", "Kemal Demir", "Elif Şahin", "Necati Aydın"];
const DIL = ["türkçe", "ingilizce", "almanca"];

export const KATALOG = [];
for (let i = 1; i <= N; i += 1) {
  const s = sec(SIFAT, 1.5), t = sec(TUR, 1.5), a = sec(ALAN, 2), k = r();
  const desen = k < 0.18 ? `${s} ${t}` : k < 0.30 ? `${s} bir ${t}`
    : k < 0.40 ? `${s} ve kuramsal ${t}` : k < 0.50 ? `${t} ve ${s} inceleme`
    : k < 0.70 ? `${s} bir anlatı` : k < 0.88 ? `çağdaş bir ${t}` : "alan yazını taraması";
  KATALOG.push({ id: `K-${String(i).padStart(4, "0")}`, ad: `${a} ${t} ${sec(SON)}`,
    ozet: `${a} alanında ${desen}; ${sec(KONU, 3)} ve ${sec(KONU, 3)} üzerine ` +
      `${sec(NIT)} bir ${sec(TUR, 1.5)} olarak okunabilir.`,
    etiket: [a, t], yazar: sec(YAZAR), yil: 1990 + Math.floor(r() * 35), dil: sec(DIL, 2) });
}

export const belirtecle = (m) => m.toLocaleLowerCase("tr").split(/[^\p{L}\p{N}]+/u).filter(Boolean);

// Ters dizin: "alan|terim" -> Map(belge sirasi -> konumlar); uzunluk: alan basina belirtec sayisi.
export function tersDizin(alanlar) {
  const g = new Map(), uzunluk = new Map();
  KATALOG.forEach((b, i) => {
    for (const alan of alanlar) {
      const tk = belirtecle(String(b[alan]));
      uzunluk.set(`${alan}|${i}`, tk.length);
      tk.forEach((t, p) => {
        let l = g.get(`${alan}|${t}`); if (!l) g.set(`${alan}|${t}`, (l = new Map()));
        let k2 = l.get(i); if (!k2) l.set(i, (k2 = [])); k2.push(p);
      });
    }
  });
  return { g, uzunluk };
}
```

## Ağaç ve Değerlendirme Sırası

Değerlendirici üç düğüm türü tanır: yaprak, `ve`, `veya`. Kesişim sıçramalı çalışır —
soldaki listenin her girişi için sağdaki listede üstel sıçrama ve ikili arama yapılır.
**SI3 (varsayım):** gönderi listesinin uzunluğu dizin sözlüğünden okunur ve girişleri
okumadan bilinir; **taranan giriş** yalnız kesişim ve birleşim yürüyüşünde sayılır. Bu,
ağacın çocuklarının hangi sırayla değerlendirileceğini gerçek bir karar yapar: kısa
listeyle başlamak, uzun listede yalnız birkaç sıçrama yapmak demektir.

```js
// sorgu.mjs — yaprak ve bilesik sorgu degerlendirici. Ayni dizinde derlem.mjs bulunur.
// Taranan giris: gonderi listelerinde okunan giris sayisi. Liste uzunlugu sozlukten bilinir.
import { KATALOG, TOHUM, tersDizin } from "./derlem.mjs";
const { g } = tersDizin(["ad", "ozet", "etiket"]);
const yaprak = (alan, terim) => [...(g.get(`${alan}|${terim}`)?.keys() ?? [])];

function atla(l, hedef, bas, s) {                    // ustel sicrama, sonra ikili arama
  let adim = 1, i = bas;
  while (i + adim < l.length && l[i + adim] < hedef) { adim *= 2; s.taranan += 1; }
  let alt = i, ust = Math.min(i + adim, l.length - 1);
  while (alt <= ust) { const o = (alt + ust) >> 1; s.taranan += 1;
    if (l[o] === hedef) return o; if (l[o] < hedef) alt = o + 1; else ust = o - 1; }
  return -alt - 1;
}
function kesis(a, b, s) {                            // a taranir, b'de sicranir
  const c = []; let j = 0;
  for (const d of a) {
    s.taranan += 1;
    const k = atla(b, d, j, s);
    if (k >= 0) { c.push(d); j = k + 1; } else j = -k - 1;
    if (j >= b.length) break;
  }
  return c;
}
function birles(listeler, s) {
  const k = new Set();
  for (const l of listeler) { s.taranan += l.length; for (const d of l) k.add(d); }
  return [...k].sort((x, y) => x - y);
}
export function calistir(dugum, s, eniyile = false) {
  if (dugum.terim) return yaprak(dugum.alan, dugum.terim);
  if (dugum.veya) return birles(dugum.veya.map((d) => calistir(d, s, eniyile)), s);
  const cocuk = dugum.ve.map((d) => calistir(d, s, eniyile));
  if (eniyile) cocuk.sort((a, b) => a.length - b.length);
  return cocuk.reduce((acc, l) => kesis(acc, l, s));
}
const olc = (ad, dugum, eniyile = false) => {
  const s = { taranan: 0 };
  const k = calistir(dugum, s, eniyile);
  console.log(`${ad.padEnd(38)} ${String(k.length).padStart(4)} belge   taranan giris ${String(s.taranan).padStart(6)}`);
  return k;
};

console.log(`derlem ${KATALOG.length} belge, tohum ${TOHUM}`);
const T = (alan, terim) => ({ alan, terim });
const ALANLAR = ["ad", "ozet", "etiket"];
const duz = (terim) => ({ veya: ALANLAR.map((a) => T(a, terim)) });
olc("duz metin, herhangi biri", { veya: ["çocuk", "kısa", "öykü"].map(duz) });
olc("duz metin, tumu zorunlu", { ve: ["çocuk", "kısa", "öykü"].map(duz) });
olc("yapilandirilmis", { ve: [T("etiket", "çocuk"), T("ozet", "kısa"), T("ad", "öykü")] });
const agac = { ve: [{ veya: [T("ad", "öykü"), T("ad", "roman")] }, T("ozet", "kısa"), T("etiket", "çocuk")] };
const a1 = olc("agac, yazilis sirasi", agac);
const a2 = olc("agac, kisa liste once", agac, true);
console.log("ayni kume mi:", JSON.stringify(a1) === JSON.stringify(a2));
for (const [a, t] of [["etiket", "çocuk"], ["ozet", "kısa"], ["ad", "öykü"], ["ad", "roman"]])
  console.log(`  gonderi listesi ${a}|${t}`.padEnd(34), String(yaprak(a, t).length).padStart(5));

// Sira: ayni kume iki bicimde siralanir. Kac kosulun karsilandigi sayilir.
const s2 = { taranan: 0 }, sayim = new Map();
for (const k of ["çocuk", "kısa", "öykü"].map(duz))
  for (const d of calistir(k, s2)) sayim.set(d, (sayim.get(d) ?? 0) + 1);
const dizinSira = [...sayim.keys()].sort((a, b) => a - b);
const kosulSira = [...dizinSira].sort((a, b) => sayim.get(b) - sayim.get(a) || a - b);
const ucu = (l) => l.slice(0, 10).filter((d) => sayim.get(d) === 3).length;
console.log(`ilk on sonucta uc kosulu da karsilayan: dizin sirasinda ${ucu(dizinSira)}, kosul sayisinda ${ucu(kosulSira)}`);
const tam = kosulSira.filter((d) => sayim.get(d) === 3);
const bas = tam.map((d) => dizinSira.indexOf(d) + 1);
console.log(`uc kosulu karsilayan ${tam.length} belge dizin sirasinda ortalama ` +
  `${Math.round(bas.reduce((x, y) => x + y, 0) / bas.length)}. basamakta, en gerisi ${Math.max(...bas)}.`);
```

```
derlem 6000 belge, tohum 271828
duz metin, herhangi biri               4211 belge   taranan giris  19102
duz metin, tumu zorunlu                 132 belge   taranan giris  21303
yapilandirilmis                          71 belge   taranan giris   7576
agac, yazilis sirasi                    138 belge   taranan giris   9501
agac, kisa liste once                   138 belge   taranan giris   7876
ayni kume mi: true
  gonderi listesi etiket|çocuk      2678
  gonderi listesi ozet|kısa          895
  gonderi listesi ad|öykü           1271
  gonderi listesi ad|roman          1007
ilk on sonucta uc kosulu da karsilayan: dizin sirasinda 1, kosul sayisinda 10
uc kosulu karsilayan 132 belge dizin sirasinda ortalama 2023. basamakta, en gerisi 4185.
```

## Küme: Aynı Sorunun Üç Okunuşu

Üç satır aynı üç sözcükten gelir ve üç ayrı küme döndürür. "Herhangi biri" okuması 4.211
belge verir — derlemin %70,2'si. Bu okuma katalogda pratikte kullanışsızdır: sonuç
listesi kütüphanenin kendisi kadar büyüktür. "Tümü zorunlu" okuması 132 belgeye iner;
sözcüklerin hangi alanda geçtiği hâlâ serbesttir, "öykü" ister kitabın adında ister
özetinde bulunabilir. Yapılandırılmış okuma üç koşulu üç ayrı alana bağlar — çocuk
etiketli, özetinde "kısa" geçen, adında "öykü" geçen kitap — ve 71 belge döndürür.

Aradaki 61 belge kaybolmuş değildir; onlar "öykü" sözcüğünü yalnız özetinde taşıyan
kitaplardır, yani bir öykü kitabı değil, öyküden söz eden bir kitaptır. Hangi kümenin
doğru olduğu sorunun kendisine bağlıdır ve düz metin sorgu bunu söylemez. Düz metin bir
sorgu değildir; **bir sorgunun okunuş adayıdır** ve okumayı seçen taraf motordur. Bir
katalog arayüzü bu seçimi yapar, kullanıcı yapmaz — bu yüzden aynı üç sözcüğü yazan iki
kullanıcı iki farklı arayüzde 4.211 ile 71 arasında herhangi bir sayı görebilir.

## Bedel: Ağacın Değerlendirme Sırası

Alt üç satır kümeyi sabit tutup bedeli ölçer. Bileşik ağaç şudur: adında "öykü" ya da
"roman" geçen, özetinde "kısa" geçen, çocuk etiketli kitaplar. Yazılış sırasında
değerlendirildiğinde önce iki uzun listenin birleşimi alınır (1.271 ve 1.007 giriş),
sonuç 2.678 girişlik etiket listesine kadar taşınır ve toplam 9.501 giriş okunur. Aynı
ağaçta çocuklar uzunluğa göre sıralandığında kesişim 895 girişlik en kısa listeyle
başlar ve toplam 7.876 girişe düşer — %17,1 daha az okuma. Dönen küme her iki koşumda da
138 belgedir; karşılaştırma bunu doğrular.

Aynı etki düz metin okumalarında daha büyüktür: "tümü zorunlu" 21.303 giriş okur, çünkü
her terim için önce üç alanın birleşimi kurulur ve bu birleşimler uzundur.
Yapılandırılmış biçim 7.576 girişle sonuçlanır — %64,4 daha az. Burada küme de bedel de
birlikte düşer, çünkü alan ayrımı hem gereksiz belgeleri hem gereksiz gönderi
listelerini eler. Kesişimin sıçramalı olması bu farkı doğuran şeydir: liste yürüyüşü
doğrusal olsaydı, iki sıra da uzun listenin tamamını okumak zorunda kalırdı.

## Sıra: Küme Sırayı Vermez

Değerlendiricinin döndürdüğü dizi belge numarasına göre artan sıradadır ve bu bir
ilgililik sırası değildir — dizine giriş sırasıdır. Son iki satır bunu ölçer. "Herhangi
biri" okumasının 4.211 belgesi içinde üç koşulu da karşılayan 132 belge vardır; dizin
sırasında ilk on sonucun yalnız 1 tanesi bu gruptandır. Karşılanan koşul sayısına göre
sıralandığında ilk onun 10 tanesi bu gruptan gelir. Küme değişmemiştir, yalnız sıra
değişmiştir.

Basamak oynaması bu farkı büyütür: 132 belge dizin sırasında ortalama 2.023. basamakta
durur, en gerideki 4.185. basamaktadır. Bir kullanıcı ilk sayfayı okuyup vazgeçtiğinde,
küme doğru olsa bile aradığı kitabı görmemiş olur. Karşılanan koşul sayısı en kaba
sıralama ölçütüdür; terimlerin ne kadar seyrek olduğunu, hangi alanda geçtiğini ve alanın
uzunluğunu hesaba katmaz. Bunların hepsi bir ilgililik puanı hesabına girer ve o hesap
bu konunun beşinci dersinde kurulur. Bu dersin bıraktığı sonuç daha önceliklidir: küme
kararıyla sıra kararı ayrı kararlardır ve bir sorgu biçimi ikisini birden belirlemez.

## Özet

- Yaprak sorgu tek alanda tek terimi arar; bileşik sorgu yaprakları bir ağaçta birleştirir
  ve ağacın kökü bağlacı belirler.
- 6.000 belgelik derlemde (tohum 271828) aynı üç sözcük üç okumada 4.211, 132 ve 71 belge
  döndürür; düz metin sorgu okumayı söylemez, okumayı motor seçer.
- Alan ayrımı hem kümeyi daraltır hem bedeli düşürür: yapılandırılmış biçim 7.576 giriş
  okur, düz metin "tümü zorunlu" 21.303 giriş.
- Ağacın çocukları uzunluğa göre sıralandığında küme değişmez (138 belge), taranan giriş
  9.501'den 7.876'ya iner.
- Eşleşen küme kendi içinde sırasızdır: üç koşulu karşılayan 132 belge dizin sırasında
  ortalama 2.023. basamaktadır, koşul sayısına göre sıralandığında ilk ondadır.

## Sonraki Adım

Yapılandırılmış sorgu terimleri alanlara bağladı, ama terimler arasındaki ilişkiyi hâlâ
"aynı belgede geçsin" düzeyinde bıraktı. Katalogda sorulan soruların bir bölümü bundan
fazlasını ister: tam adı bilinmeyen bir kitabın adının içindeki iki sözcük yan yana
geçiyordur ve arada başka sözcük yoktur. "Eleştirel deneme" ile "eleştirel bir deneme"
aynı iki terimi taşır; ilkinde terimler bitişiktir, ikincisinde araya bir sözcük
girmiştir. Ters dizinin gönderi listelerinde tutulan konum bilgisi tam olarak bu ayrımı
yapmak içindir ve şimdiye kadar hiç kullanılmadı. Sonraki ders ifade sorgusunu kurar:
terimlerin sırasını ve aralarındaki uzaklığı koşula çevirir, kayma payının kümeyi ne
kadar genişlettiğini sayar ve konum bilgisinin dizine ne kadara mal olduğunu ölçer.
