Loader

GORM Preload Obje Limitleme (Limit Preloaded Object Count)

· Golang ·
Golang, gorm, preload, lateral join, select distinct

Bildiğimiz üzere GORM, Golang üzerinde kullandığımız object relationship managerlardan birisi. Kendisi çoğu zaman zor işleri kolay şekilde çözse de bazı işlerin üstesinden gelmeyi daha da zorlaştırabiliyor :)

Karşılaştığım ve çözüm getireceğimiz sorun ise bir nesnenin içindeki ilişkisel verilerin kısıtlı sayıda getirilmesi problemidir. Daha detaylı olarak şu şekilde bahsedebiliriz.

Example diagram

Yukarıdaki yapıda bir tablomuz olduğunu ve ikisi arasında one to many ilişkisi olduğunu varsayalım. GORM üzerinde şu şekilde bir dönüş beklediğimizde preload işlemi yapıyoruz.

[
  {
    "id": 1,
    "content": "1. içerik",
    "comments": [
      {
        "id": 1,
        "content": "1. yorum"
      },
      {
        "id": 2,
        "content": "2. yorum"
      },
      {
        "id": 3,
        "content": "3. yorum"
      },
      {
        "id": 4,
        "content": "4. yorum"
      }
  // belki 1000 yorumdan fazla var?
    ]
  },
  {
    "id": 2,
    "content": "2. içerik",
    "comments": [
      {
        "id": 5,
        "content": "1. yorum"
      },
      {
        "id": 6,
        "content": "2. yorum"
      }
    ]
  }
]

Preload kullandığımızda gördüğünüz gibi iki içeriğin de yorumları başarıyla yüklendi. Ancak bu içeriklerin 500+ yorumu olduğunda preload yaptığımızda ne yapacağız?

İnternette önerilen kılavuz şu şekilde:

posts := []*models.Post{}
database.Connection().Model(&posts).Preload(“Comments”, func(tx *gorm.DB) *gorm.DB {
    return tx.Limit(2)
})

Görünüş olarak bir sıkıntı gözükmese de bu bizim beklediğimiz çalışma biçimine malesef sahip değil. Yukarıdaki kod bloğu tx.Limit(2) dediğinizde her Post nesnesi için 2 adet değil o anda çekilen tüm Post nesneleri için toplam 2 adet Comment döndürecektir. Çıktı olarak alacağınız sonuç bu şekildedir.

[
  {
    "id": 1,
    "content": "1. içerik",
    "comments": [
      {
        "id": 1,
        "content": "1. yorum"
      },
      {
        "id": 2,
        "content": "2. yorum"
      }
    ]
  },
  {
    "id": 2,
    "content": "2. içerik",
    "comments": [
        // aslında devamı var ancak çekmiyor :)
  // çünkü toplamda 2 adet nesne çekti ve query’i sonlandırdı
    ]
  }
]

Çözümü için ise yukarıdaki kod bloğuna daha komplike bir SQL sorgusu yazmamız gerekecek. Burada imdadımıza lateral join ve distinct selection yetişiyor. 

posts := []*models.Post{}
database.Connection().Model(&posts).Preload("Comments", func(tx *gorm.DB) *gorm.DB {
    return tx.Joins(`
  JOIN LATERAL (
      SELECT DISTINCT c.post_id FROM comments c WHERE c.post_id = comments.post_id LIMIT 2
  ) AS cm ON cm.post_id = comments.post_id
`)
})

Yukarıdaki şekilde join işlemi yaptığımızda beklediğimiz çıktıyı alacağız.

Örnek çıktı:

[
  {
    "id": 1,
    "content": "1. içerik",
    "comments": [
      {
        "id": 1,
        "content": "1. yorum"
      },
      {
        "id": 2,
        "content": "2. yorum"
      }
    ]
  },
  {
    "id": 2,
    "content": "2. içerik",
    "comments": [
      {
        "id": 5,
        "content": "1. yorum"
      },
      {
        "id": 6,
        "content": "2. yorum"
      }
    ]
  }
]

Okuduğunuz için teşekkür ederim, umarım faydalı olmuştur :) Sağlıcakla kalın.

Daha fazla gönderi...