1. İndeks Kavramı ve Uygulaması

1.1 İndeks Nedir

Bir indeks, veri alma işlemlerini hızlandırmak için veritabanı yönetim sisteminde kullanılan bir veri yapısıdır. Bu, veritabanında "dizin" olarak görülebilir ve veri tablosundaki verilerin hızlı bir şekilde bulunmasını sağlar, tam tablo taramalarını önler ve sorgu verimliliğini önemli ölçüde arttırır. Pratik uygulamalarda, indekslerin doğru kullanımı veritabanının performansını büyük ölçüde artırabilir.

1.2 İndeks Türleri

Çeşitli indeks türleri vardır, her birinin farklı uygulama senaryoları için tasarımı ve optimize edilmesi mevcuttur:

  • Tek Alanlı İndeks: Yalnızca bir alan içeren bir indeks, hızlı sorgular için tek bir koşula dayalı senaryolarda uygun şekilde kullanılır.
  • Birleşik İndeks: Birden fazla alanı içeren bir indeks, bu alanları içeren sorgular için optimize edilmiş bir yapı sunar.
  • Benzersiz İndeks: İndeks alanlarının benzersizliğini sağlar, yinelenen değerlerin varlığını engeller. Benzersiz indeksler tek alanlı veya birleşik olabilir.

2. İndeks Tanımı

2.1 Alan İndeks Tanımı

Tek bir alanlı bir indeks oluşturmak, veri tablosunun belirli bir sütunu üzerinde bir indeks oluşturmayı içerir, bu Fields yöntemi kullanılarak gerçekleştirilebilir. Aşağıdaki örnek, User varlığının phone alanı üzerinde bir indeks oluşturmanın nasıl yapıldığını göstermektedir.

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("phone"),
    }
}
func (User) Indexes() []ent.Index {
    // Tek alanlı bir indeks oluşturun.
    return []ent.Index{
        index.Fields("phone"),
    }
}

Bu kod parçacığında, phone alanı endekslenmiştir. Bu, sistem için phone alanını sorgularken indeksi kullanmasına izin verir.

2.2 Benzersiz İndeks Tanımı

Benzersiz bir indeks, endekslenen sütunlardaki verilerin benzersizliğini sağlar. Alanına Unique yöntemi eklenerek oluşturulabilir. Aşağıdaki örnekler, tek ve çoklu alanlar için benzersiz indeks oluşturmayı göstermektedir.

Tek bir alana benzersiz indeks oluşturma örneği:

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("email").Unique(),
    }
}

Bu örnekte, email alanı benzersiz olarak belirtilmiş ve her kullanıcının veritabanında benzersiz bir email adresine sahip olmasını sağlar.

Birden fazla alan kombinasyonu için benzersiz indeks oluşturma örneği:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Birden fazla alana benzersiz indeks oluşturun.
        index.Fields("first_name", "last_name").Unique(),
    }
}

Bu kod, first_name ve last_name alanlarının birleşik benzersiz bir indeksini tanımlar ve her iki alandaki değerlerin aynı olmasını engeller.

2.3 Birleşik İndeks Tanımı

Sorgu koşulları birden fazla alanı içerdiğinde, birleşik bir indeks devreye girer. Birleşik indeks, verileri indekste tanımlanan sırayla depolar. İndeksin sırası, sorgu performansı üzerinde önemli bir etkiye sahiptir; bu nedenle birleşik bir indeks tanımlarken, alanların sırası sorgu kalıbına göre belirlenmelidir.

Aşağıdaki örnek, bir birleşik indeks oluşturmayı göstermektedir:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Birden fazla alana sahip bir birleşik indeks oluşturun.
        index.Fields("country", "city"),
    }
}

Bu örnekte, country ve city alanları üzerinde bir birleşik indeks oluşturulmuştur. Bu, bu iki alana ilişkin sorgu işlemleri gerçekleştirilirken veritabanının hızlı bir şekilde koşulları karşılayan verileri bulmasını sağlar.

Birleşik indeks, sadece sorgu performansını hızlandırmakla kalmaz, aynı zamanda indeks tabanlı sıralama işlemlerini de destekler, daha verimli veri alım performansı sağlar. Bir birleşik indeks tasarlarken, daha fazla seçiciliğe sahip alanların indeksin ön kısmına yerleştirilmesi genellikle yaygındır, böylece veritabanı optimizer'i indeksi daha iyi kullanabilir.

3. Kenar Dizinleri

ent çerçevesinde, kenar dizinleri (ilişkileri) aracılığıyla dizinleri tanımlamanın bir yolu vardır. Bu mekanizma genellikle belirli ilişkiler altında alanların benzersizliğini sağlamak için kullanılır. Örneğin, veritabanı modeliniz şehirler ve sokakları içeriyorsa ve her şehir altında her sokak adının benzersiz olması gerekiyorsa, kenar dizinleri bunu sağlamak için kullanılabilir.

// ent/schema/street.go dosyası, Street varlığının şemasını tanımlar.
type Street struct {
    ent.Schema
}

func (Street) Fields() []ent.Field {
    // Alanları tanımla
    return []ent.Field{
        field.String("name"),
    }
}

func (Street) Edges() []ent.Edge {
    // Şehire ait olan Sokak ile ilgili kenar ilişkisini tanımla.
    return []ent.Edge{
        edge.From("city", City.Type).
            Ref("streets").
            Unique(),
    }
}

func (Street) Indexes() []ent.Index {
    // Benzersizlik sağlamak için kenar üzerinden benzersiz bir dizin oluşturun.
    return []ent.Index{
        index.Fields("name").
            Edges("city").
            Unique(),
    }
}

Bu örnekte, Street varlığını oluşturuyoruz ve onu City varlığıyla ilişkilendiriyoruz. Street varlığının Indexes yönteminde bir kenar dizini tanımlayarak, her şehir altında her sokak adının benzersiz olduğunu sağlıyoruz.

5. Bölüm: Gelişmiş Dizin Seçenekleri

5.1 Tam Metin ve Hash Dizinleri

Tam metin dizini ve hash dizini, MySQL ve PostgreSQL'de kullanılan iki benzersiz dizin türüdür ve farklı sorgu optimizasyon senaryoları için kullanılır.

Tam metin dizini genellikle metin verilerini aramak için kullanılır, özellikle kelime eşleme aramaları gibi karmaşık aramalar yapmanız gerektiğinde. Hem MySQL hem de PostgreSQL veritabanları tam metin dizinini destekler. Örneğin, MySQL'de şöyle bir tam metin dizini tanımlayabilirsiniz:

// ent/schema/user.go dosyası, User varlığının şemasını tanımlar
func (User) Indexes() []ent.Index {
    // MySQL'de FULLTEXT kategorisini kullanarak tam metin dizini oluşturun.
    return []ent.Index{
        index.Fields("description").
            Annotations(entsql.IndexTypes(map[string]string{
                dialect.MySQL: "FULLTEXT",
            })),
    }
}

Hash dizini özellikle eşitlik sorguları için uygun olup sıralama ve aralık sorgularını desteklemez. PostgreSQL'de hash dizini şu şekilde kullanılabilir:

func (User) Indexes() []ent.Index {
    // HASH türünde bir dizin tanımla
    return []ent.Index{
        index.Fields("c4").
            Annotations(entsql.IndexType("HASH")),
    }
}

5.2 Kısmi Dizinler ve Dizin Önekleri

Kısmi dizin, yalnızca belirli koşulları sağlayan tablo satırlarını dizinleyen bir dizin türüdür. SQLite ve PostgreSQL'de WHERE ifadesini kullanarak kısmi dizinler oluşturabilirsiniz.

Örneğin, PostgreSQL'de kısmi bir dizin tanımlama:

func (User) Indexes() []ent.Index {
    // "nickname" alanı üzerinde, "active" true olan satırları içeren bir kısmi dizin oluşturun
    return []ent.Index{
        index.Fields("nickname").
            Annotations(
                entsql.IndexWhere("active"),
            ),
    }
}

Dizin öneki özellikle metin alanları için MySQL'de özellikle kullanışlıdır. Dizin oluşturma süresini kısaltabilir, dizinin kapladığı alanı azaltabilir ve iyi bir performans sağlayabilir. Aşağıdaki gibi MySQL için bir önek ile bir dizin tanımlayabilirsiniz:

func (User) Indexes() []ent.Index {
    // Önek kullanarak bir dizin oluştur
    return []ent.Index{
        index.Fields("description").
            Annotations(entsql.Prefix(128)),
    }
}

5.3 Dizin Anotasyonları ve Özelleştirme

ent'te, Anotasyonlar geliştiricilere dizinleri özelleştirmelerine olanak sağlayan bir özelliktir. Dizin türünü, sıralama kurallarını ve daha fazlasını tanımlayabilirsiniz.

Örneğin, aşağıdaki kod, bir dizinde sütun sıralama kurallarını belirtmenin nasıl yapıldığını göstermektedir:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Sütun sıralama kurallarını tanımlamak için anotasyonları kullanın
        index.Fields("c1", "c2", "c3").
            Annotations(entsql.DescColumns("c1", "c2")),
    }
}

Anotasyon özelliği sayesinde geliştiriciler, veritabanı performansını ve yapısını optimize etmek için dizinleri esnek bir şekilde özelleştirebilirler.