← Kursa Dön
📄 Text · 40 min

Function Score Query — Skor Özelleştirme

Giriş — Google'ın Sırrı

Google sadece anahtar kelime eşleştirmesi yapsaydı, ilk sonuç genellikle en çok spam içeren site olurdu. Google'ı güçlü yapan şey, sayfa sıralamasında onlarca ek sinyal kullanmasıdır: popülerlik (PageRank), güncellik, kullanıcının konumu, tıklanma oranı ve daha yüzlercesi.

Elasticsearch'ün varsayılan BM25 scoring'i text relevance için mükemmeldir ama gerçek dünyada sadece metin benzerliği yetmez. Bir e-ticaret sitesinde popüler ürünler öne çıkmalı. Bir haber sitesinde güncel haberler öncelikli olmalı. Bir restoran uygulamasında yakındaki restoranlar daha üstte görünmeli.

İşte function_score query tam bu iş için var. BM25 skorunu ek fonksiyonlarla modifiye ederek, iş mantığınıza uygun özel sıralama oluşturmanızı sağlar. Bu ders, function_score'un her parametresini, her fonksiyon tipini ve gerçek dünya uygulamalarını derinlemesine ele alacak.


1. Function Score Temelleri

1.1 Yapı

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { ... },              // Temel query (BM25 skorunu üretir)
      "functions": [ ... ],           // Ek fonksiyonlar (skoru modifiye eder)
      "score_mode": "multiply",       // Fonksiyonlar arası birleştirme
      "boost_mode": "multiply",       // Fonksiyon skoru + query skoru birleştirme
      "max_boost": 42,                // Fonksiyonların üretebileceği max boost
      "min_score": 5                  // Bu skorun altındaki sonuçları at
    }
  }
}

1.2 score_mode — Fonksiyonlar Arası Birleştirme

Birden fazla fonksiyon tanımladığınızda, bunların skorlarını nasıl birleştireceğinizi score_mode belirler:

┌──────────────┬──────────────────────────────────────────────────┐
│ score_mode   │ Açıklama                                        │
├──────────────┼──────────────────────────────────────────────────┤
│ multiply     │ Tüm fonksiyon skorlarını çarp (varsayılan)       │
│ sum          │ Tüm fonksiyon skorlarını topla                   │
│ avg          │ Ortalama al                                      │
│ first        │ İlk eşleşen fonksiyonun skorunu kullan           │
│ max          │ En yüksek fonksiyon skorunu kullan                │
│ min          │ En düşük fonksiyon skorunu kullan                 │
└──────────────┴──────────────────────────────────────────────────┘

Örnek — İki fonksiyonun birleşimi:

Fonksiyon A skoru: 2.0
Fonksiyon B skoru: 3.0

score_mode: multiply → 2.0 × 3.0 = 6.0
score_mode: sum      → 2.0 + 3.0 = 5.0
score_mode: avg      → (2.0 + 3.0) / 2 = 2.5
score_mode: max      → 3.0
score_mode: min      → 2.0

1.3 boost_mode — Query Skoru ile Birleştirme

Fonksiyonların birleşik skoru (score_mode sonucu) ile orijinal query skorunun nasıl birleştirileceğini belirler:

┌──────────────┬──────────────────────────────────────────────────┐
│ boost_mode   │ Formül                                          │
├──────────────┼──────────────────────────────────────────────────┤
│ multiply     │ query_score × function_score (varsayılan)        │
│ replace      │ function_score (query_score'u yok say)           │
│ sum          │ query_score + function_score                     │
│ avg          │ (query_score + function_score) / 2               │
│ max          │ max(query_score, function_score)                 │
│ min          │ min(query_score, function_score)                 │
└──────────────┴──────────────────────────────────────────────────┘

Senaryo: Query skoru = 10, Fonksiyon skoru = 3

boost_mode: multiply → 10 × 3 = 30
boost_mode: replace  → 3
boost_mode: sum      → 10 + 3 = 13
boost_mode: avg      → (10 + 3) / 2 = 6.5

💡 İpucu: boost_mode: multiply çoğu durumda en doğal sonuçları verir çünkü orijinal relevance'ı korur ve üzerine boost ekler. replace kullanırsanız metin benzerliği tamamen göz ardı edilir.


2. weight — En Basit Fonksiyon

weight fonksiyonu, eşleşen dokümanlara sabit bir çarpan uygular. Genellikle filter ile birlikte kullanılır:

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "filter": { "term": { "is_promoted": true } },
          "weight": 5
        },
        {
          "filter": { "term": { "is_in_stock": true } },
          "weight": 2
        },
        {
          "filter": { "term": { "brand.keyword": "Apple" } },
          "weight": 1.5
        }
      ],
      "score_mode": "multiply",
      "boost_mode": "multiply"
    }
  }
}

Bu örnekte:

  • Tanıtılan ürünler 5x boost alır

  • Stokta olan ürünler 2x boost alır

  • Apple ürünleri 1.5x boost alır

  • Stokta olan, tanıtılan bir Apple laptop: 5 × 2 × 1.5 = 15x boost

2.1 Filter + Weight Kombinasyonu

Her fonksiyonun bir filter'ı olabilir. Filter eşleşmezse o fonksiyon skoru 1 olur (yani etkisiz):

"functions": [
  {
    "filter": { "range": { "rating": { "gte": 4.5 } } },
    "weight": 3
  }
]
// rating >= 4.5 olan dokümanlar 3x boost alır
// Diğerleri 1x (boost yok)

3. field_value_factor — Alan Değerine Göre Boost

3.1 Temel Kullanım

Dokümanın bir sayısal alanının değerini skor hesaplamasında kullanır:

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "field_value_factor": {
            "field": "sales_count",
            "factor": 1.2,
            "modifier": "log1p",
            "missing": 1
          }
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

3.2 modifier Seçenekleri

modifier, field değerine uygulanacak matematiksel dönüşümü belirler:

┌──────────────┬──────────────────┬──────────────────────────────────┐
│ modifier     │ Formül           │ Açıklama                        │
├──────────────┼──────────────────┼──────────────────────────────────┤
│ none         │ value × factor   │ Ham değer (varsayılan)           │
│ log          │ log(value×factor)│ Logaritmik — çok büyük değerleri │
│              │                  │ baskılar                          │
│ log1p        │ log(1+value×f)   │ log ama 0 değeri handle eder    │
│ log2p        │ log(2+value×f)   │ log1p gibi, 2 offset            │
│ ln           │ ln(value×factor) │ Doğal logaritma                  │
│ ln1p         │ ln(1+value×f)    │ ln ama 0 handle eder            │
│ ln2p         │ ln(2+value×f)    │ ln1p gibi, 2 offset             │
│ square       │ (value×f)²       │ Kare — yüksek değerleri aşırı   │
│              │                  │ ödüllendirir                      │
│ sqrt         │ √(value×factor)  │ Karekök — yüksek değerlerin      │
│              │                  │ etkisini azaltır                  │
│ reciprocal   │ 1/(value×factor) │ Ters orantı                      │
└──────────────┴──────────────────┴──────────────────────────────────┘

modifier seçimi pratikte:

sales_count = 0, 10, 100, 1000, 100000

none:      0,    12,   120,  1200,  120000     ← Aşırı fark!
log1p:     0,    2.5,  4.8,  7.1,   11.7       ← Dengeli ✅
sqrt:      0,    3.5,  11,   35,    355        ← Orta
square:    0,    144,  14400, 1.4M,  14.4B     ← Aşırı!

💡 İpucu: Çoğu durumda `log1p` en iyi seçimdir. Değer 0 olduğunda güvenlidir (log(1+0) = 0) ve çok büyük değerlerin (100K satış vs 1K satış) makul bir skor farkı oluşturmasını sağlar.

3.3 factor ve missing

  • `factor`: Field değerini bu sayıyla çarpar. Boost'un "şiddetini" ayarlar

  • `missing`: Field değeri yoksa (null/eksik) kullanılacak varsayılan değer

{
  "field_value_factor": {
    "field": "popularity",
    "factor": 0.5,      // Popularity'nin etkisini yarıya indir
    "modifier": "log1p",
    "missing": 0         // Popularity yoksa 0 varsay (log1p(1+0) = 0)
  }
}

⚠️ Dikkat: missing değeri belirtmezseniz ve dokümanın ilgili alanı yoksa, Elasticsearch hata verir! Her zaman missing belirtin.


4. random_score — Rastgele Sıralama

4.1 Temel Kullanım

Arama sonuçlarına kontrollü rastgelelik ekler. A/B testi, "keşfet" özelliği veya "her refresh farklı sonuç" senaryoları için kullanılır:

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match_all": {} },
      "functions": [
        {
          "random_score": {
            "seed": 12345,
            "field": "_seq_no"
          }
        }
      ]
    }
  }
}

4.2 seed ve Tekrarlanabilirlik

seed parametresi aynı değer verildiğinde aynı rastgele sıralama üretir (deterministic random):

// Aynı seed → aynı sıralama (aynı kullanıcı her zaman aynı sonuçları görür)
"random_score": { "seed": "user_123", "field": "_seq_no" }

// Farklı seed → farklı sıralama
"random_score": { "seed": "user_456", "field": "_seq_no" }

// Seed vermezsen → her seferde farklı sıralama
"random_score": {}

Yaygın seed stratejileri:

  • Kullanıcı ID → Her kullanıcı kendi tutarlı sıralamasını görür

  • Kullanıcı ID + Tarih → Günlük farklı sıralama ama gün içinde tutarlı

  • Timestamp → Her istekte farklı (gerçek rastgele)

4.3 field Parametresi

field, rastgelelik hesaplamasında kullanılan alan. _seq_no önerilir çünkü her dokümanda vardır ve unique'tir:

"random_score": {
  "seed": 42,
  "field": "_seq_no"
}

⚠️ Dikkat: field parametresi verilmezse, Elasticsearch dahili Lucene doc ID kullanır. Ancak merge sonrası doc ID'ler değişebileceğinden, aynı seed ile farklı sonuçlar alabilirsiniz. Her zaman "field": "_seq_no" kullanın.


5. script_score — Özel Skor Hesaplama

5.1 Painless Script ile Custom Scoring

En esnek fonksiyon. Painless script ile istediğiniz formülü yazabilirsiniz:

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "script_score": {
            "script": {
              "source": """
                double popularity = doc['popularity'].value;
                double recency = 1.0 / (1.0 + Math.exp(-0.1 * (doc['days_since_publish'].value - 30)));
                return Math.log(1 + popularity) * (1 - recency);
              """
            }
          }
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

5.2 Parametreli Script

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "script_score": {
            "script": {
              "source": """
                double base = doc[params.field].value;
                return Math.log(1 + base) * params.weight;
              """,
              "params": {
                "field": "sales_count",
                "weight": 2.0
              }
            }
          }
        }
      ]
    }
  }
}

5.3 Stored Script

Sık kullanılan script'leri saklayabilirsiniz:

// Script'i kaydet
PUT _scripts/popularity-boost
{
  "script": {
    "lang": "painless",
    "source": """
      double pop = doc[params.pop_field].value;
      double factor = params.containsKey('factor') ? params.factor : 1.0;
      return Math.log1p(pop) * factor;
    """
  }
}

// Kullanım
GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "script_score": {
            "script": {
              "id": "popularity-boost",
              "params": {
                "pop_field": "sales_count",
                "factor": 1.5
              }
            }
          }
        }
      ]
    }
  }
}

5.4 Script Performans İpuçları

// ❌ YAVAŞ — Her dokümanda string arama
"source": "doc['tags'].value.contains('premium') ? 2.0 : 1.0"

// ✅ HIZLI — Filter ile ayır, weight ile boost ver
"functions": [
  { "filter": { "term": { "tags": "premium" } }, "weight": 2 }
]

⚠️ Dikkat: script_score her eşleşen doküman için çalıştırılır. 1 milyon doküman eşleşiyorsa, script 1 milyon kez çalışır! Mümkünse field_value_factor veya filter + weight kullanın — bunlar daha optimize edilmiştir.


6. Decay Functions — Mesafe/Zaman/Sayısal Azalma

6.1 Decay Nedir?

Decay fonksiyonları, bir origin (merkez) noktasından uzaklaştıkça skorun azalmasını sağlar. Üç tip vardır:

Skor
  ▲
  │ ██████████
  │ █        █
  │ █         █           gauss (çan eğrisi)
  │ █          ██
  │ █            ████████████
  │
  │ ██████████
  │ █        █
  │ █         █           exp (üstel azalma)
  │ █          █
  │ █           ██
  │ █             ████████████
  │
  │ ██████████
  │ █        ████
  │ █            ████
  │ █                ████     linear (doğrusal azalma)
  │ █                    ████
  │ █                        ████
  └──────────────────────────────► Mesafe/Zaman/Değer
      origin   scale

6.2 Decay Parametreleri

Her decay fonksiyonunun dört parametresi vardır:

┌──────────┬──────────────────────────────────────────────────────┐
│ Parametre│ Açıklama                                            │
├──────────┼──────────────────────────────────────────────────────┤
│ origin   │ Merkez nokta — "en iyi" değer. Skor burada 1.0     │
│ scale    │ origin'den bu kadar uzaklıkta skor = decay          │
│ offset   │ origin ± offset aralığında skor 1.0 kalır           │
│          │ (tolerance zone)                                     │
│ decay    │ origin + offset + scale mesafesindeki skor değeri   │
│          │ Varsayılan: 0.5                                     │
└──────────┴──────────────────────────────────────────────────────┘

Görselleştirme:

Skor
1.0 │ ████████████████████
    │ ←    offset    →  ██
    │                     ██
0.5 │ ........................██............ ← decay = 0.5
    │                           ██
    │                             ████
0.0 │                                 ████████
    └──────────────────────────────────────────
         origin         scale
    |←    offset    →|← scale →|

6.3 Geo Distance Decay — Yakınlık Boost

GET /restaurants/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "cuisine": "türk mutfağı" } },
      "functions": [
        {
          "gauss": {
            "location": {
              "origin": { "lat": 41.0082, "lon": 28.9784 },
              "scale": "5km",
              "offset": "500m",
              "decay": 0.5
            }
          }
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

Bu sorgu:

  • Sultanahmet'ten (origin) 500m içindeki restoranlar → skor 1.0

  • 500m + 5km = 5.5km uzaklıkta → skor 0.5

  • 5.5km'den uzakta → skor hızla azalır (gauss eğrisi)

6.4 Date Decay — Güncellik Boost

GET /articles/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "content": "elasticsearch" } },
      "functions": [
        {
          "gauss": {
            "publish_date": {
              "origin": "now",
              "scale": "30d",
              "offset": "7d",
              "decay": 0.5
            }
          }
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

Bu sorgu:

  • Son 7 gün içinde yayınlanan makaleler → skor 1.0 (offset)

  • 7 + 30 = 37 gün önce yayınlanan → skor 0.5

  • Daha eski → skor hızla düşer

6.5 Numeric Decay — Sayısal Değer Yakınlığı

GET /products/_search
{
  "query": {
    "function_score": {
      "query": { "match": { "category": "laptop" } },
      "functions": [
        {
          "exp": {
            "price": {
              "origin": 15000,
              "scale": 5000,
              "offset": 1000,
              "decay": 0.3
            }
          }
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

Bu sorgu:

  • Fiyatı 14000-16000 (±1000 offset) arası → skor 1.0

  • Fiyatı 9000 veya 21000 (±6000) → skor 0.3

  • Fiyat origin'den uzaklaştıkça skor azalır

6.6 Gauss vs Exp vs Linear

                        gauss           exp             linear
─────────────────────────────────────────────────────────────────
Azalma şekli            Çan eğrisi      Üstel           Doğrusal
Başlangıçta (yakın)     Yavaş azalır    Hızlı azalır    Sabit hızda
Ortada                  Hızlı azalır    Yavaşlar        Sabit hızda
Uzakta                  Çok yavaş azalır Çok yavaş       Tam 0 olur

Ne zaman kullan?
gauss:  "Ortadaki değerler önemli değil" — restoran yakınlığı
exp:    "Hemen azalsın" — haber güncelliği
linear: "Eşit hızda azalsın" — fiyat aralığı

Sayısal örnek (scale=10, decay=0.5):

Mesafe:     0    2    5    8    10   15   20
gauss:      1.0  0.98 0.88 0.68 0.50 0.22 0.06
exp:        1.0  0.93 0.79 0.64 0.50 0.32 0.20
linear:     1.0  0.90 0.75 0.60 0.50 0.25 0.00 ← 0'a ulaşır!

⚠️ Dikkat: linear decay, belirli bir noktadan sonra skor 0 olur. gauss ve exp ise asimptotik olarak 0'a yaklaşır ama asla tam 0 olmaz. Sonuçlardan tamamen çıkmasını istemiyorsanız gauss veya exp kullanın.


7. Birden Fazla Fonksiyonun Birleşimi

7.1 E-Commerce Örneği — Tam Kapsamlı

GET /products/_search
{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            { "multi_match": {
                "query": "kablosuz kulaklık",
                "fields": ["title^3", "description", "brand^2"]
            }}
          ],
          "filter": [
            { "term": { "is_active": true } },
            { "range": { "price": { "gte": 50, "lte": 2000 } } }
          ]
        }
      },
      "functions": [
        {
          "filter": { "term": { "is_promoted": true } },
          "weight": 3
        },
        {
          "field_value_factor": {
            "field": "sales_count",
            "modifier": "log1p",
            "factor": 0.5,
            "missing": 0
          }
        },
        {
          "field_value_factor": {
            "field": "rating",
            "modifier": "none",
            "factor": 1,
            "missing": 3.0
          }
        },
        {
          "gauss": {
            "created_at": {
              "origin": "now",
              "scale": "90d",
              "offset": "7d",
              "decay": 0.5
            }
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply",
      "max_boost": 50
    }
  }
}

Bu sorgu ne yapıyor?

  1. query: "kablosuz kulaklık" için BM25 text relevance skoru üretir

  2. is_promoted: true → 3 puan ekle

  3. sales_count → log1p ile popülerlik puanı (log(1 + sales × 0.5))

  4. rating → Doğrudan puan değeri (1-5 arası)

  5. created_at → Yeni ürünlere bonus (7 gün içi tam puan, 97 gün → yarı puan)

  6. score_mode: sum → Fonksiyon puanları toplanır

  7. boost_mode: multiply → BM25 skoru × fonksiyon toplamı

  8. max_boost: 50 → Fonksiyonların toplam etkisi max 50 ile sınırlı

7.2 Haber Sitesi Örneği

GET /articles/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query": "yapay zeka",
          "fields": ["title^5", "summary^2", "content"]
        }
      },
      "functions": [
        {
          "exp": {
            "publish_date": {
              "origin": "now",
              "scale": "3d",
              "offset": "1h",
              "decay": 0.3
            }
          },
          "weight": 5
        },
        {
          "field_value_factor": {
            "field": "view_count",
            "modifier": "log2p",
            "factor": 0.1,
            "missing": 0
          }
        },
        {
          "filter": { "term": { "is_breaking": true } },
          "weight": 10
        },
        {
          "filter": { "term": { "category": "teknoloji" } },
          "weight": 1.5
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}

8. Java ile Function Score

8.1 Temel Function Score Query

public class FunctionScoreSearchService {
    private final ElasticsearchClient client;

    public FunctionScoreSearchService(ElasticsearchClient client) {
        this.client = client;
    }

    public SearchResponse<Product> searchWithBoost(String queryText) throws IOException {
        return client.search(s -> s
            .index("products")
            .query(q -> q
                .functionScore(fs -> fs
                    .query(fq -> fq
                        .multiMatch(mm -> mm
                            .query(queryText)
                            .fields("title^3", "description")
                        )
                    )
                    .functions(
                        // Popülerlik boost
                        fn -> fn
                            .fieldValueFactor(fvf -> fvf
                                .field("sales_count")
                                .modifier(FieldValueFactorModifier.Log1p)
                                .factor(0.5)
                                .missing(0.0)
                            ),
                        // Stokta olanlar 2x
                        fn -> fn
                            .filter(f -> f.term(t -> t
                                .field("in_stock")
                                .value(true)
                            ))
                            .weight(2.0),
                        // Güncellik decay
                        fn -> fn
                            .gauss(g -> g
                                .date(d -> d
                                    .field("created_at")
                                    .placement(p -> p
                                        .origin("now")
                                        .scale("30d")
                                        .offset("3d")
                                        .decay(0.5)
                                    )
                                )
                            )
                    )
                    .scoreMode(FunctionScoreMode.Sum)
                    .boostMode(FunctionBoostMode.Multiply)
                    .maxBoost(100.0)
                )
            )
            .size(20),
            Product.class
        );
    }

    public SearchResponse<Product> searchWithRandomization(
            String queryText, String userId) throws IOException {
        return client.search(s -> s
            .index("products")
            .query(q -> q
                .functionScore(fs -> fs
                    .query(fq -> fq
                        .match(m -> m.field("category").query(queryText))
                    )
                    .functions(fn -> fn
                        .randomScore(rs -> rs
                            .seed(userId)
                            .field("_seq_no")
                        )
                    )
                    .boostMode(FunctionBoostMode.Replace)
                )
            )
            .size(20),
            Product.class
        );
    }
}

8.2 Script Score ile Custom Ranking

public SearchResponse<Product> searchWithCustomScore(
        String queryText, double userLat, double userLon) throws IOException {
    return client.search(s -> s
        .index("products")
        .query(q -> q
            .functionScore(fs -> fs
                .query(fq -> fq
                    .match(m -> m.field("title").query(queryText))
                )
                .functions(fn -> fn
                    .scriptScore(ss -> ss
                        .script(sc -> sc
                            .inline(i -> i
                                .source("""
                                    double popularity = Math.log1p(doc['sales_count'].value);
                                    double rating = doc['rating'].value;
                                    double freshness = 1.0 / (1.0 + (System.currentTimeMillis() 
                                        - doc['created_at'].value.toInstant().toEpochMilli()) 
                                        / 86400000.0 / 30.0);
                                    return (popularity * 0.3 + rating * 0.5 + freshness * 0.2);
                                """)
                            )
                        )
                    )
                )
                .boostMode(FunctionBoostMode.Multiply)
            )
        )
        .size(20),
        Product.class
    );
}

9. Performans ve Best Practices

9.1 Performans Kuralları

┌──────────────────────────────────┬──────────┬──────────────────────────┐
│ Fonksiyon                        │ Hız      │ Not                       │
├──────────────────────────────────┼──────────┼──────────────────────────┤
│ weight (filter ile)              │ ⭐⭐⭐⭐⭐ │ En hızlı — sadece çarpma │
│ field_value_factor               │ ⭐⭐⭐⭐  │ Doc values'dan okur       │
│ random_score                     │ ⭐⭐⭐⭐  │ Hash hesaplama            │
│ decay (gauss/exp/linear)         │ ⭐⭐⭐   │ Matematik hesaplama       │
│ script_score (basit)             │ ⭐⭐⭐   │ Script compile + execute  │
│ script_score (karmaşık)          │ ⭐⭐     │ Her doküman için çalışır  │
└──────────────────────────────────┴──────────┴──────────────────────────┘

9.2 Best Practices

✅ YAP:
  • filter + weight kullan mümkünse (en hızlı)
  • field_value_factor'da log1p modifier kullan (büyük değerleri dengele)
  • random_score'da seed + _seq_no field kullan (tutarlılık)
  • max_boost ile üst sınır koy (outlier'ları engelle)
  • Stored script kullan (compile cache'lenir)
  • explain: true ile skoru debug et

❌ YAPMA:
  • Her isteğe script_score ekleme (performans maliyeti)
  • modifier: none ile büyük değerli alanları kullanma (patlama riski)
  • boost_mode: replace kullanma (text relevance kaybolur) — bilerek yapmıyorsan
  • Çok fazla fonksiyon ekleme (5-6'dan fazlası nadir gerekir)
  • missing parametresini unutma (hata kaynağı)

9.3 Debug — explain ile Skor Analizi

GET /products/_search
{
  "explain": true,
  "query": {
    "function_score": {
      "query": { "match": { "title": "laptop" } },
      "functions": [
        {
          "field_value_factor": {
            "field": "sales_count",
            "modifier": "log1p",
            "missing": 0
          }
        }
      ]
    }
  }
}

explain: true yanıtı, her dokümanın skor hesaplamasını adım adım gösterir — hangi fonksiyonun ne kadar etki ettiğini görürsünüz.


10. Özet

  • function_score, BM25 text relevance skorunu iş mantığına göre modifiye etmenizi sağlar — popülerlik, güncellik, konum gibi sinyaller ekler

  • score_mode fonksiyonlar arası birleştirmeyi (multiply, sum, avg, max, min, first), boost_mode fonksiyon + query skoru birleştirmesini belirler

  • weight en basit ve en hızlı fonksiyondur — filter ile birlikte koşullu boost verir

  • field_value_factor doküman alanını skora dahil eder. modifier: log1p büyük değerlerin etkisini dengeler, missing parametresi zorunludur

  • random_score kontrollü rastgelelik sağlar. seed ile aynı kullanıcıya tutarlı sıralama, seed değiştirerek farklı sonuçlar sunabilirsiniz

  • script_score en esnek ama en yavaş fonksiyondur. Mümkünse filter+weight veya field_value_factor tercih edin

  • Decay fonksiyonları (gauss, exp, linear) origin noktasından uzaklaştıkça skorun azalmasını sağlar: geo distance, tarih güncelliği, fiyat yakınlığı gibi senaryolar için idealdir. gauss çoğu durumda en doğal sonuçları verir