← Kursa Dön
📄 Text · 40 min

Tüm Field Tipleri Derinlemesine

Giriş — Doğru Kutuya Doğru Eşya

Bir depo düşünün. Kitaplar için raf, sıvılar için bidon, kıyafetler için askı kullanırsınız. Her eşyayı aynı kutuya atarsanız sonra ne bulabilirsiniz ne de düzenleyebilirsiniz. Elasticsearch'te de her veri parçasını doğru field type ile saklamak bu kadar kritiktir.

Yanlış field tipi seçimi genellikle hemen fark edilmez — çünkü Elasticsearch dynamic mapping ile bir şekilde çalışır. Ama sonuçları ağırdır: gereksiz disk kullanımı, yavaş aramalar, aggregation'ların çalışmaması veya hassasiyet kaybı. Bu ders, Elasticsearch'teki tüm field tiplerini birer birer, ne zaman kullanılacağı, kısıtlamaları ve pratik örnekleriyle inceleyecek.


1. String Tipler: text ve keyword

1.1 text

text tipi, full-text search için optimize edilmiş string tipidir. Değer bir analyzer'dan geçirilir ve tokenize edilir.

PUT /articles
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "standard",
        "index_options": "positions",
        "norms": true
      }
    }
  }
}

text parametreleri:

ParametreVarsayılanAçıklama
analyzerstandardIndex-time analyzer
search_analyzeranalyzer değeriSearch-time analyzer (farklı olabilir)
indextrueIndex'lensin mi?
index_optionspositionsdocs, freqs, positions, offsets
normstrueLength normalization skoru. Sadece scoring gerekliyse açık tutun
storefalseAyrı sakla (normalde _source'tan okunur)
term_vectornoyes, with_positions, with_offsets, with_positions_offsets
fielddatafalseAggregation/sort için memory-based yapı (⚠️ dikkat!)
eager_global_ordinalsfalseGlobal ordinals'ı hemen oluştur
similarityBM25Scoring algoritması
// text field'ında arama
GET /articles/_search
{
  "query": {
    "match": {
      "title": "elasticsearch performans optimizasyonu"
    }
  }
}

⚠️ Dikkat: text field'ları üzerinde aggregation veya sorting yapılamaz (fielddata açılmadıkça). Hem arama hem aggregation istiyorsanız multi-field kullanın.

1.2 keyword

keyword tipi, tokenize edilmeyen string tipidir. Tam değer olarak saklanır ve exact match, aggregation, sorting için kullanılır.

PUT /products
{
  "mappings": {
    "properties": {
      "status": {
        "type": "keyword",
        "ignore_above": 256,
        "normalizer": "lowercase_normalizer"
      }
    }
  },
  "settings": {
    "analysis": {
      "normalizer": {
        "lowercase_normalizer": {
          "type": "custom",
          "filter": ["lowercase"]
        }
      }
    }
  }
}

keyword parametreleri:

ParametreVarsayılanAçıklama
ignore_above2147483647Bu uzunluktan büyük değerler index'lenmez
normalizernullTokenize etmeden karakter dönüşümü (lowercase gibi)
doc_valuestrueAggregation/sort için columnar yapı
indextrueIndex'lensin mi?
null_valuenullnull değer yerine kullanılacak değer
eager_global_ordinalsfalseTerms aggregation performansı için
// keyword field'ında exact match
GET /products/_search
{
  "query": { "term": { "status": "active" } }
}

// keyword field'ında aggregation
GET /products/_search
{
  "size": 0,
  "aggs": {
    "statuses": { "terms": { "field": "status" } }
  }
}

1.3 text + keyword Multi-Field Pattern

En yaygın kullanım — hem arama hem aggregation:

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "standard",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          },
          "autocomplete": {
            "type": "text",
            "analyzer": "edge_ngram_analyzer"
          }
        }
      }
    }
  }
}
name         → text (full-text search)
name.keyword → keyword (aggregation, sorting, exact match)
name.autocomplete → text + edge_ngram (autocomplete)

💡 İpucu: ignore_above: 256 dynamic mapping'in varsayılanıdır. Eğer keyword alanınız uzun URL'ler veya açıklamalar içerecekse bu değeri artırın. Index'lenmeyen değerler aggregation'larda görünmez.


2. Sayısal Tipler

2.1 integer, long, short, byte

PUT /metrics
{
  "mappings": {
    "properties": {
      "count": { "type": "integer" },
      "total_bytes": { "type": "long" },
      "port": { "type": "short" },
      "priority": { "type": "byte" }
    }
  }
}

Sayısal tipler ve aralıkları:

┌──────────┬────────────────────────────┬──────────────┐
│ Tip      │ Aralık                     │ Boyut        │
├──────────┼────────────────────────────┼──────────────┤
│ byte     │ -128 ~ 127                 │ 8-bit        │
│ short    │ -32,768 ~ 32,767           │ 16-bit       │
│ integer  │ -2^31 ~ 2^31 - 1           │ 32-bit       │
│ long     │ -2^63 ~ 2^63 - 1           │ 64-bit       │
└──────────┴────────────────────────────┴──────────────┘

Ne zaman hangisi?

  • byte: 0-255 arası değerler (HTTP status category, priority level)

  • short: Port numaraları, küçük sayaçlar

  • integer: Çoğu sayısal alan için varsayılan seçim

  • long: Büyük sayılar (Unix timestamp millis, büyük ID'ler, file size byte)

💡 İpucu: Elasticsearch dahili olarak tüm sayısal tipleri BKD tree (Block KD-Tree) yapısında saklar. Daha küçük tip seçmek disk'te az yer kaplar ama arama hızını doğrudan etkilemez. Yine de doğru tipi seçmek iyi bir pratiktir.

2.2 float, double, half_float

PUT /products
{
  "mappings": {
    "properties": {
      "price": { "type": "float" },
      "precise_measurement": { "type": "double" },
      "score": { "type": "half_float" }
    }
  }
}
┌─────────────┬────────────────────────┬──────────┐
│ Tip         │ Hassasiyet             │ Boyut    │
├─────────────┼────────────────────────┼──────────┤
│ half_float  │ ~3 ondalık basamak     │ 16-bit   │
│ float       │ ~7 ondalık basamak     │ 32-bit   │
│ double      │ ~15 ondalık basamak    │ 64-bit   │
└─────────────┴────────────────────────┴──────────┘

⚠️ Dikkat: Floating point hassasiyet kaybı! float tipi 0.1 + 0.2'yi tam 0.3 olarak saklayamaz. Finansal veriler için scaled_float kullanın.

2.3 scaled_float — Finansal Veriler İçin

scaled_float, ondalıklı sayıları dahili olarak long olarak saklar. scaling_factor ile çarpılır ve tam sayıya dönüştürülür:

PUT /orders
{
  "mappings": {
    "properties": {
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      },
      "tax_rate": {
        "type": "scaled_float",
        "scaling_factor": 10000
      }
    }
  }
}
price: 29.99 → dahili olarak 2999 (long) saklanır
tax_rate: 0.1875 → dahili olarak 1875 (long) saklanır

Avantajları:

  • Hassasiyet kaybı yok (long arithmetic)

  • float'tan daha az disk kullanır (long sıkıştırması daha verimli)

  • Aggregation'lar daha hızlı

Ne zaman kullanılmalı: Fiyat, vergi oranı, oran, yüzde gibi sabit ondalık hassasiyetli değerler.


3. boolean

PUT /users
{
  "mappings": {
    "properties": {
      "is_active": { "type": "boolean" }
    }
  }
}

Kabul edilen değerler:

true değerleri:  true, "true", "yes", "on", "1", 1
false değerleri: false, "false", "no", "off", "0", 0, "" (boş string)
// Bunların hepsi aynı
POST /users/_doc/1 { "is_active": true }
POST /users/_doc/2 { "is_active": "true" }
POST /users/_doc/3 { "is_active": "yes" }

// Term query ile arama
GET /users/_search
{
  "query": { "term": { "is_active": true } }
}

⚠️ Dikkat: Boolean field'lar dahili olarak "T" ve "F" keyword olarak saklanır. Aggregation'larda true/false yerine "T"/"F" görebilirsiniz (client'a bağlı).


4. Date Tipleri: date ve date_nanos

4.1 date

PUT /events
{
  "mappings": {
    "properties": {
      "created_at": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

Dahili saklama: Tüm date değerleri dahili olarak epoch milliseconds (long) olarak saklanır. Format sadece parsing ve görüntüleme içindir.

Desteklenen formatlar:

// Hepsi geçerli date değerleri:
"2024-02-27T10:30:00.000Z"     // ISO 8601
"2024-02-27T10:30:00+03:00"    // Timezone offset ile
"2024-02-27"                    // Sadece tarih
1709029800000                   // Epoch millis
"1709029800000"                 // Epoch millis (string)

Yaygın format pattern'ları:

yyyy-MM-dd                          → 2024-02-27
yyyy-MM-dd'T'HH:mm:ss              → 2024-02-27T10:30:00
yyyy-MM-dd'T'HH:mm:ss.SSSZ         → 2024-02-27T10:30:00.000+0300
strict_date_optional_time           → ISO 8601 (varsayılan)
epoch_millis                        → 1709029800000
epoch_second                        → 1709029800
date_hour_minute_second             → 2024-02-27T10:30:00
basic_date_time_no_millis           → 20240227T103000+0300
// Birden fazla format (|| ile ayırma)
"format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd||epoch_millis"

4.2 date_nanos

Nanosaniye hassasiyeti gerektiren durumlar için:

PUT /high-precision-events
{
  "mappings": {
    "properties": {
      "timestamp": {
        "type": "date_nanos"
      }
    }
  }
}

// Nanosaniye hassasiyetinde değer
POST /high-precision-events/_doc/1
{
  "timestamp": "2024-02-27T10:30:00.123456789Z"
}

date vs date_nanos:

date:       milisaniye hassasiyeti  → 2024-02-27T10:30:00.123Z
date_nanos: nanosaniye hassasiyeti  → 2024-02-27T10:30:00.123456789Z

date:       epoch_millis (long)    → 13 haneli sayı
date_nanos: epoch_nanos (long)     → 19 haneli sayı

⚠️ Dikkat: date_nanos kullandığınızda aggregation ve sorting'de milisaniye hassasiyeti kaybedilmez ama bazı aggregation'lar (date_histogram gibi) nanosaniye hassasiyetini göstermeyebilir. Ayrıca date_nanos'un aralığı date'ten dardır: 1970-2262 arası (long overflow nedeniyle).


5. ip — IP Adresi Tipi

PUT /access-logs
{
  "mappings": {
    "properties": {
      "client_ip": { "type": "ip" }
    }
  }
}

// IPv4 ve IPv6 desteği
POST /access-logs/_doc/1 { "client_ip": "192.168.1.100" }
POST /access-logs/_doc/2 { "client_ip": "::ffff:192.168.1.100" }
POST /access-logs/_doc/3 { "client_ip": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" }

CIDR notation ile arama:

GET /access-logs/_search
{
  "query": {
    "term": { "client_ip": "192.168.1.0/24" }
  }
}

// Range query
GET /access-logs/_search
{
  "query": {
    "range": {
      "client_ip": {
        "gte": "192.168.1.0",
        "lte": "192.168.1.255"
      }
    }
  }
}

💡 İpucu: IP field'ı dahili olarak 128-bit integer olarak saklanır. IPv4 adresleri IPv6-mapped adrese dönüştürülür (::ffff:192.168.1.100). Bu sayede IPv4 ve IPv6 aynı field'da karışık saklanabilir.


6. Geo Tipleri: geo_point ve geo_shape

6.1 geo_point

Yeryüzünde bir nokta (enlem/boylam):

PUT /restaurants
{
  "mappings": {
    "properties": {
      "location": { "type": "geo_point" }
    }
  }
}

// Farklı geo_point formatları
POST /restaurants/_doc/1 { "location": { "lat": 41.0082, "lon": 28.9784 } }
POST /restaurants/_doc/2 { "location": "41.0082,28.9784" }
POST /restaurants/_doc/3 { "location": [28.9784, 41.0082] }  // ⚠️ [lon, lat] sırası!
POST /restaurants/_doc/4 { "location": "u14dg9s0" }           // Geohash

⚠️ Dikkat: Array formatında sıra [longitude, latitude]'dir — GeoJSON standardına uygun. Ama object formatında {"lat": ..., "lon": ...}. Bu tutarsızlık yaygın bir hata kaynağıdır!

6.2 geo_shape

Nokta, çizgi, polygon gibi karmaşık coğrafi şekiller:

PUT /areas
{
  "mappings": {
    "properties": {
      "boundary": { "type": "geo_shape" }
    }
  }
}

// Polygon tanımlama
POST /areas/_doc/1
{
  "boundary": {
    "type": "polygon",
    "coordinates": [
      [[28.5, 40.8], [29.5, 40.8], [29.5, 41.3], [28.5, 41.3], [28.5, 40.8]]
    ]
  }
}

// Point (geo_shape olarak)
POST /areas/_doc/2
{
  "boundary": {
    "type": "point",
    "coordinates": [28.9784, 41.0082]
  }
}

// Circle
POST /areas/_doc/3
{
  "boundary": {
    "type": "circle",
    "coordinates": [28.9784, 41.0082],
    "radius": "5km"
  }
}

Desteklenen şekiller: point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection, envelope (bounding box), circle


7. completion — Autocomplete İçin

completion tipi, suggest özelliği (autocomplete) için özel olarak optimize edilmiş bir field tipidir. FST (Finite State Transducer) veri yapısını kullanır ve tamamen bellekte çalışır.

PUT /products
{
  "mappings": {
    "properties": {
      "suggest": {
        "type": "completion",
        "analyzer": "simple",
        "search_analyzer": "simple",
        "preserve_separators": true,
        "preserve_position_increments": true,
        "max_input_length": 50
      }
    }
  }
}

// Doküman indexleme — input ve weight ile
POST /products/_doc/1
{
  "suggest": {
    "input": ["iPhone 15 Pro", "Apple iPhone 15", "iPhone"],
    "weight": 100
  }
}

POST /products/_doc/2
{
  "suggest": {
    "input": ["Samsung Galaxy S24", "Galaxy S24", "Samsung"],
    "weight": 80
  }
}

// Suggest query
GET /products/_search
{
  "suggest": {
    "product-suggest": {
      "prefix": "iph",
      "completion": {
        "field": "suggest",
        "size": 5,
        "fuzzy": {
          "fuzziness": 1
        }
      }
    }
  }
}

Parametreler:

  • preserve_separators: true → "foo bar" ve "foobar" farklı; false → aynı

  • preserve_position_increments: Stop word'ler sonrası pozisyon korunması

  • max_input_length: Input string uzunluk limiti (varsayılan 50)

💡 İpucu: completion tipi in-memory FST kullandığı için çok hızlıdır (sub-millisecond). Ama çok fazla input varsa bellek tüketimi artar. Milyonlarca ürün için dikkatli capacity planning yapın.


8. dense_vector ve sparse_vector — ML ve Vektör Arama

8.1 dense_vector

Makine öğrenmesi embedding'leri ve kNN (k-Nearest Neighbors) arama için:

PUT /embeddings
{
  "mappings": {
    "properties": {
      "title_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

// Vektör ile doküman indexleme
POST /embeddings/_doc/1
{
  "title": "Elasticsearch tutorial",
  "title_vector": [0.12, -0.34, 0.56, ...]  // 384 boyutlu vektör
}

// kNN search
GET /embeddings/_search
{
  "knn": {
    "field": "title_vector",
    "query_vector": [0.11, -0.33, 0.55, ...],
    "k": 10,
    "num_candidates": 100
  }
}

dense_vector parametreleri:

  • dims: Vektör boyutu (1-4096 arası, 8.x'te artırıldı)

  • index: true olursa HNSW index oluşturulur (kNN arama için)

  • similarity: cosine, dot_product, l2_norm, max_inner_product

  • index_options.type: hnsw (varsayılan) veya flat

  • index_options.m: HNSW graph bağlantı sayısı (varsayılan 16)

  • index_options.ef_construction: Index oluşturma kalitesi (varsayılan 100)

8.2 sparse_vector

Seyrek vektörler (çoğu eleman 0 olan) için:

PUT /sparse-search
{
  "mappings": {
    "properties": {
      "ml_tokens": {
        "type": "sparse_vector"
      }
    }
  }
}

// Sparse vektör — sadece non-zero değerler
POST /sparse-search/_doc/1
{
  "ml_tokens": {
    "elasticsearch": 2.5,
    "search": 1.8,
    "engine": 1.2,
    "tutorial": 0.9
  }
}

Sparse vector genellikle ELSER (Elastic Learned Sparse EncodeR) veya benzeri modellerin çıktıları için kullanılır.


9. flattened — Yapısı Bilinmeyen JSON

flattened tipi, iç yapısı önceden bilinmeyen veya çok değişken JSON objeleri için idealdir. Tüm alt alanları tek bir field altında keyword olarak index'ler:

PUT /events
{
  "mappings": {
    "properties": {
      "metadata": {
        "type": "flattened",
        "depth_limit": 20,
        "ignore_above": 256,
        "doc_values": true
      }
    }
  }
}

// Rastgele yapıda metadata
POST /events/_doc/1
{
  "metadata": {
    "user": { "name": "Ali", "role": "admin" },
    "action": "login",
    "details": {
      "browser": "Chrome",
      "os": "Windows",
      "version": "120.0"
    }
  }
}

// Arama — tüm alt alanlar keyword olarak aranır
GET /events/_search
{
  "query": {
    "term": { "metadata": "Chrome" }
  }
}

// Veya alt alan belirterek
GET /events/_search
{
  "query": {
    "term": { "metadata.details.browser": "Chrome" }
  }
}

Ne zaman kullanılmalı:

  • Label'lar, tag'ler, metadata gibi yapısı dinamik olan alanlar

  • Her dokümanın farklı field'lara sahip olabileceği durumlar

  • Mapping explosion'ı önlemek istediğinizde

Kısıtlamalar:

  • Tüm değerler keyword olarak saklanır — full-text search yok

  • Sayısal range query çalışmaz (hepsi string)

  • Nested aggregation yok

⚠️ Dikkat: flattened tip bir kaçış yoludur ama trade-off'ları önemlidir. Eğer o alanlar üzerinde full-text search veya sayısal range query yapmanız gerekecekse, flattened yerine düzgün mapping tanımlayın.


10. join — Parent-Child İlişkileri

join tipi, aynı index içinde parent-child ilişkisi kurar:

PUT /qa-forum
{
  "mappings": {
    "properties": {
      "relation": {
        "type": "join",
        "relations": {
          "question": "answer"
        }
      },
      "title": { "type": "text" },
      "body": { "type": "text" },
      "author": { "type": "keyword" }
    }
  }
}

// Parent doküman (question)
POST /qa-forum/_doc/1?routing=1
{
  "title": "Elasticsearch'te geo search nasıl yapılır?",
  "body": "Geo point kullanmak istiyorum...",
  "author": "ali",
  "relation": { "name": "question" }
}

// Child doküman (answer)
POST /qa-forum/_doc/2?routing=1
{
  "body": "geo_point tipini kullanarak geo_distance query yapabilirsiniz.",
  "author": "ayse",
  "relation": {
    "name": "answer",
    "parent": "1"
  }
}

// Parent'a göre child'ları bul
GET /qa-forum/_search
{
  "query": {
    "has_parent": {
      "parent_type": "question",
      "query": {
        "match": { "title": "geo search" }
      }
    }
  }
}

⚠️ Dikkat: join tipi performans açısından pahalıdır. Parent ve child dokümanları aynı shard'da olmalıdır (routing zorunlu). Mümkünse nested tipi tercih edin — join sadece parent-child ilişkisinin gerçekten bağımsız dokümanlar olması gerektiğinde kullanılmalıdır.


11. alias — Field Alias

Mevcut bir field'a alternatif isim verir:

PUT /products
{
  "mappings": {
    "properties": {
      "product_name": { "type": "text" },
      "name": {
        "type": "alias",
        "path": "product_name"
      }
    }
  }
}

// Her iki isim de çalışır
GET /products/_search { "query": { "match": { "name": "laptop" } } }
GET /products/_search { "query": { "match": { "product_name": "laptop" } } }

Ne zaman kullanılmalı:

  • Field ismi değiştirilmek isteniyor ama reindex yapılamıyor

  • Farklı sistemlerin farklı field isimleri beklediği durumlar


12. binary — Base64 Encoded Veri

PUT /attachments
{
  "mappings": {
    "properties": {
      "content": {
        "type": "binary",
        "doc_values": false,
        "store": true
      }
    }
  }
}

// Base64 encoded veri
POST /attachments/_doc/1
{
  "content": "U29tZSBiaW5hcnkgZGF0YQ=="
}

Kısıtlamalar:

  • Aranamaz — index'lenmez

  • Aggregation yapılamaz — doc_values yok

  • Sadece saklama amaçlı

  • Base64 encode zorunlu


13. rank_feature ve rank_features

Scoring'de boost olarak kullanılacak sayısal değerler için:

PUT /websites
{
  "mappings": {
    "properties": {
      "pagerank": { "type": "rank_feature" },
      "url_length": {
        "type": "rank_feature",
        "positive_score_impact": false
      },
      "topics": { "type": "rank_features" }
    }
  }
}

POST /websites/_doc/1
{
  "pagerank": 8.5,
  "url_length": 120,
  "topics": {
    "technology": 50,
    "elasticsearch": 90,
    "java": 30
  }
}

// rank_feature query
GET /websites/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "content": "elasticsearch" } }
      ],
      "should": [
        { "rank_feature": { "field": "pagerank", "boost": 2 } },
        { "rank_feature": { "field": "topics.elasticsearch" } }
      ]
    }
  }
}
  • positive_score_impact: true (varsayılan): Değer arttıkça score artar (pagerank)

  • positive_score_impact: false: Değer arttıkça score azalır (url_length — kısa URL daha iyi)

  • rank_features: Birden fazla feature'ı map olarak saklar


14. Range Tipleri

Tek bir değer yerine aralık saklamak için:

PUT /events
{
  "mappings": {
    "properties": {
      "time_range": { "type": "date_range" },
      "age_range": { "type": "integer_range" },
      "price_range": { "type": "float_range" },
      "ip_range": { "type": "ip_range" }
    }
  }
}

POST /events/_doc/1
{
  "time_range": {
    "gte": "2024-02-27T09:00:00Z",
    "lte": "2024-02-27T18:00:00Z"
  },
  "age_range": { "gte": 18, "lte": 65 },
  "price_range": { "gte": 10.5, "lt": 99.99 },
  "ip_range": { "gte": "192.168.1.0", "lte": "192.168.1.255" }
}

// Range ile kesişim araması
GET /events/_search
{
  "query": {
    "range": {
      "time_range": {
        "gte": "2024-02-27T12:00:00Z",
        "lte": "2024-02-27T14:00:00Z",
        "relation": "intersects"
      }
    }
  }
}

relation parametreleri:

  • intersects (varsayılan): Aralıklar kesişiyor mu?

  • contains: Kayıtlı aralık, sorgu aralığını kapsıyor mu?

  • within: Kayıtlı aralık, sorgu aralığının içinde mi?


15. Java ile Field Tipleri

15.1 Mapping Oluşturma

public class MappingManager {
    private final ElasticsearchClient client;

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

    public void createComprehensiveMapping() throws IOException {
        CreateIndexRequest request = CreateIndexRequest.of(c -> c
            .index("comprehensive-index")
            .mappings(m -> m
                // String tipler
                .properties("title", p -> p.text(t -> t
                    .analyzer("standard")
                    .fields("keyword", f -> f.keyword(k -> k.ignoreAbove(256)))
                ))
                .properties("status", p -> p.keyword(k -> k))

                // Sayısal tipler
                .properties("count", p -> p.integer(i -> i))
                .properties("total_bytes", p -> p.long_(l -> l))
                .properties("price", p -> p.scaledFloat(sf -> sf.scalingFactor(100.0)))

                // Boolean
                .properties("is_active", p -> p.boolean_(b -> b))

                // Date
                .properties("created_at", p -> p.date(d -> d
                    .format("yyyy-MM-dd'T'HH:mm:ss.SSSZ||epoch_millis")
                ))

                // IP
                .properties("client_ip", p -> p.ip(ip -> ip))

                // Geo
                .properties("location", p -> p.geoPoint(g -> g))

                // Completion
                .properties("suggest", p -> p.completion(comp -> comp
                    .analyzer("simple")
                ))

                // Flattened
                .properties("metadata", p -> p.flattened(fl -> fl
                    .depthLimit(10)
                ))

                // Dense vector
                .properties("embedding", p -> p.denseVector(dv -> dv
                    .dims(384)
                    .index(true)
                    .similarity("cosine")
                ))
            )
        );

        client.indices().create(request);
        System.out.println("Comprehensive index created");
    }
}

15.2 Field Tipine Göre Doküman Oluşturma

public void indexDocument() throws IOException {
    Map<String, Object> doc = new HashMap<>();

    // String
    doc.put("title", "Elasticsearch Deep Dive");
    doc.put("status", "published");

    // Number
    doc.put("count", 42);
    doc.put("total_bytes", 1073741824L);
    doc.put("price", 29.99);

    // Boolean
    doc.put("is_active", true);

    // Date
    doc.put("created_at", "2024-02-27T10:30:00.000Z");

    // IP
    doc.put("client_ip", "192.168.1.100");

    // Geo point
    Map<String, Double> location = new HashMap<>();
    location.put("lat", 41.0082);
    location.put("lon", 28.9784);
    doc.put("location", location);

    // Completion suggest
    Map<String, Object> suggest = new HashMap<>();
    suggest.put("input", List.of("Elasticsearch Deep Dive", "ES Deep Dive"));
    suggest.put("weight", 100);
    doc.put("suggest", suggest);

    // Flattened metadata
    Map<String, Object> metadata = new HashMap<>();
    metadata.put("source", "web");
    metadata.put("campaign", "spring-2024");
    doc.put("metadata", metadata);

    IndexResponse response = client.index(i -> i
        .index("comprehensive-index")
        .document(doc)
    );

    System.out.println("Indexed: " + response.id());
}

16. Tip Seçimi Karar Tablosu

┌──────────────────────────┬─────────────────┬──────────────────────────────────┐
│ Veri Türü                │ Önerilen Tip    │ Not                              │
├──────────────────────────┼─────────────────┼──────────────────────────────────┤
│ Tam metin (arama)        │ text            │ Analyzer seçimi kritik           │
│ Sabit değer (enum, tag)  │ keyword         │ ignore_above ayarla              │
│ İkisi birden             │ text + keyword  │ Multi-field kullan               │
│ Tamsayı (küçük)          │ integer         │ Aralığa dikkat                   │
│ Tamsayı (büyük/ID)       │ long            │ Timestamp, büyük ID              │
│ Ondalıklı (genel)        │ float           │ Hassasiyet kaybı olabilir        │
│ Ondalıklı (finansal)     │ scaled_float    │ scaling_factor zorunlu           │
│ Ondalıklı (bilimsel)     │ double          │ Gerçekten gerekli olmalı         │
│ Doğru/Yanlış             │ boolean         │ Çeşitli string kabul eder        │
│ Tarih/Zaman              │ date            │ Format tanımla                   │
│ Tarih (nanosaniye)       │ date_nanos      │ 1970-2262 aralığı               │
│ IP adresi                │ ip              │ IPv4 + IPv6, CIDR desteği        │
│ Konum (nokta)            │ geo_point       │ Array: [lon,lat] sırası!         │
│ Konum (şekil)            │ geo_shape       │ Polygon, circle vb.              │
│ Autocomplete             │ completion      │ In-memory FST, weight desteği    │
│ ML embedding             │ dense_vector    │ kNN search için index:true       │
│ Sparse embedding         │ sparse_vector   │ ELSER çıktıları                  │
│ Dinamik JSON             │ flattened       │ Sadece keyword arama             │
│ Parent-child             │ join            │ Routing zorunlu, yavaş           │
│ Alan takma adı           │ alias           │ Reindex gerektirmez              │
│ İkili veri               │ binary          │ Base64, aranamaz                 │
│ Scoring boost            │ rank_feature    │ BM25 ile birleşir                │
│ Aralık değer             │ integer_range   │ date_range, ip_range vb.         │
└──────────────────────────┴─────────────────┴──────────────────────────────────┘

17. Yaygın Hatalar

Hata 1: text field'ında aggregation yapmaya çalışmak

// ❌ Hata verir
GET /products/_search
{
  "aggs": { "names": { "terms": { "field": "name" } } }
}
// "Text fields are not optimised for operations that require per-document field data"

// ✅ Doğru — .keyword sub-field kullan
GET /products/_search
{
  "aggs": { "names": { "terms": { "field": "name.keyword" } } }
}

Hata 2: geo_point array sırasını karıştırmak

// ❌ Lat/Lon ters! Object'te lat/lon, array'de lon/lat
{ "location": [41.0082, 28.9784] }  // YANLIŞ — lat,lon

// ✅ Array: [longitude, latitude]
{ "location": [28.9784, 41.0082] }  // DOĞRU — lon,lat

Hata 3: float ile finansal veri saklamak

// ❌ Hassasiyet kaybı
{ "price": { "type": "float" } }
// 19.99 → dahili olarak 19.989999... olabilir

// ✅ scaled_float kullan
{ "price": { "type": "scaled_float", "scaling_factor": 100 } }

18. Özet

  • text full-text search için, keyword exact match/aggregation/sort için kullanılır. Çoğu string alanında multi-field (text + keyword) pattern en iyisidir

  • Sayısal tiplerde scaled_float finansal veriler için altın standarttır — float/double'ın hassasiyet kaybı riskini ortadan kaldırır

  • date tipi dahili olarak epoch millis saklar, format sadece parsing içindir. Birden fazla format || ile desteklenir

  • geo_point array formatında [lon, lat] sırası kullanır (GeoJSON standardı) — en yaygın hatalardan biri bu sıralamanın karıştırılmasıdır

  • completion tipi autocomplete için in-memory FST kullanır ve sub-millisecond hızda çalışır

  • dense_vector makine öğrenmesi ve semantic search için kullanılır. similarity parametresi (cosine, dot_product, l2_norm) use-case'e göre seçilmelidir

  • flattened tipi yapısı değişken JSON verileri için mapping explosion'ı önler ama sadece keyword-level arama sunar

  • join tipi parent-child ilişkileri için vardır ama performans maliyeti yüksektir — mümkünse nested veya denormalizasyon tercih edin