MongoDB aggregate ile $group kullanarak verileri gruplamak için belgeleri bir aggregation pipeline içindeki aşamalardan sırayla geçirirsin. $group aşaması, _id alanında belirtilen değeri aynı olan belgeleri aynı grupta toplar ve her grup için hesaplanmış bir sonuç belgesi üretir.
MongoDB’de belge, koleksiyonda tutulan JSON benzeri veri birimidir. Aşama ise bu belgeler üzerinde filtreleme, gruplama veya hesaplama yapan pipeline adımlarından biridir. Bir aşamanın ürettiği çıktı, sıradaki aşamanın girdisi olur. Bu nedenle aşamaların sırası, elde edeceğin sonucu doğrudan etkiler.
Aggregation pipeline aşamaları nasıl çalışır?
Aggregation pipeline, belgeleri bir dizi işlemden geçirerek özet sonuçlar üretir. Örneğin önce $match ile belirli tutardaki siparişleri seçebilir, ardından $group ile müşteri bazında toplayabilir ve son olarak $sort ile toplam tutara göre sıralayabilirsin.
Örnek sipariş belgeleri
db.siparisler.insertMany([
{ musteri: "Ayşe", urun: "Klavye", tutar: 1200 },
{ musteri: "Ayşe", urun: "Mouse", tutar: 600 },
{ musteri: "Berk", urun: "Monitör", tutar: 7500 },
{ musteri: "Berk", urun: "Kablo", tutar: 300 },
{ musteri: "Ayşe", urun: "Webcam", tutar: 1800 }
])
Aşağıdaki sorguda $match, tutarı 500 ve üzerindeki belgeleri sonraki aşamaya gönderir. $group, bu belgeleri musteri alanına göre birleştirir. $sum, $avg, $max ve $min ise her müşteri grubu için farklı özetler hesaplar.
db.siparisler.aggregate([
{ $match: { tutar: { $gte: 500 } } },
{
$group: {
_id: "$musteri",
toplamTutar: { $sum: "$tutar" },
ortalamaTutar: { $avg: "$tutar" },
enYuksekTutar: { $max: "$tutar" },
enDusukTutar: { $min: "$tutar" }
}
},
{ $sort: { toplamTutar: -1 } }
])
Sorgunun ürettiği sonuçta Berk’in 300 tutarındaki siparişi $match aşamasından geçmediği için hesaba katılmaz:
[
{
_id: 'Berk',
toplamTutar: 7500,
ortalamaTutar: 7500,
enYuksekTutar: 7500,
enDusukTutar: 7500
},
{
_id: 'Ayşe',
toplamTutar: 3600,
ortalamaTutar: 1200,
enYuksekTutar: 1800,
enDusukTutar: 600
}
]
Buradaki akış, verinin aşamalar arasında nasıl ilerlediğini gösterir: önce belgeler filtrelenir, sonra gruplar ve hesaplamalar oluşturulur, en sonunda oluşan grup belgeleri sıralanır.
$group içinde _id alanı neyi belirler?

$group içindeki _id alanı, her grubun anahtarını belirler. { $group: { _id: "$musteri" } } ifadesinde aynı musteri değerine sahip siparişler tek bir grupta toplanır ve sonuç belgesinin _id alanında müşteri adı gösterilir.
db.siparisler.aggregate([
{ $group: { _id: "$musteri" } }
])
[
{ _id: 'Ayşe' },
{ _id: 'Berk' }
]
Alan adının başındaki $ işareti, ifadenin mevcut belgedeki alan değerini kullanacağını belirtir. Bu işaret unutulursa "musteri" ifadesi alan yolu yerine sabit metin olarak değerlendirilir ve belgeler beklediğin müşteri değerlerine göre ayrılmaz. _id alanı da $group aşamasında zorunludur.
Tüm belgeleri tek bir grupta toplamak için _id: null kullanılabilir:
db.siparisler.aggregate([
{
$group: {
_id: null,
siparisSayisi: { $sum: 1 }
}
}
])
[ { _id: null, siparisSayisi: 5 } ]
Birden fazla alana göre gruplama yapmak için _id içine belge biçiminde birden fazla alan yazabilirsin. { _id: { musteri: "$musteri", urun: "$urun" } } ifadesi, müşteri ve ürün birleşimini grup anahtarı olarak kullanır. $group çıktıları ayrıca kendiliğinden sıralanmadığı için sıralama gerekiyorsa pipeline’a ayrıca $sort eklemelisin.
$sum, $avg, $max ve $min nasıl kullanılır?
Aggregation pipeline, belgeleri aşama aşama işleyen sorgu dizisidir. $group aşamasında _id alanı aynı olan belgeler bir araya gelir; $sum, $avg, $max ve $min ise her grup için sayısal sonuç üretir.
Önce siparisler koleksiyonuna ortak bir örnek veri ekleyelim. Ayşe’nin toplamı 1200 + 600 + 1800 = 3600, Mehmet’in toplamı ise 1500 + 800 = 2300 olacaktır.
db.siparisler.insertMany([
{ musteri: "Ayşe", urun: "Klavye", tutar: 1200 },
{ musteri: "Ayşe", urun: "Mouse", tutar: 600 },
{ musteri: "Mehmet", urun: "Klavye", tutar: 1500 },
{ musteri: "Mehmet", urun: "Mouse", tutar: 800 },
{ musteri: "Ayşe", urun: "Klavye", tutar: 1800 }
])
Her müşterinin toplam harcamasını bulmak için _id alanında "$musteri", toplam alanında ise $sum kullanılır.
db.siparisler.aggregate([
{
$group: {
_id: "$musteri",
toplamHarcama: { $sum: "$tutar" }
}
},
{ $sort: { _id: 1 } }
]).toArray()
[
{ _id: 'Ayşe', toplamHarcama: 3600 },
{ _id: 'Mehmet', toplamHarcama: 2300 }
]
Ürün bazında ortalama, en yüksek ve en düşük satış tutarını birlikte hesaplayabilirsin. Klavye ortalaması 1500, Mouse ortalaması 700 olur. $max en yüksek, $min ise en düşük tutarı seçer.
db.siparisler.aggregate([
{
$group: {
_id: "$urun",
ortalamaSatis: { $avg: "$tutar" },
enYuksek: { $max: "$tutar" },
enDusuk: { $min: "$tutar" }
}
},
{ $sort: { _id: 1 } }
]).toArray()
[
{ _id: 'Klavye', ortalamaSatis: 1500, enYuksek: 1800, enDusuk: 1200 },
{ _id: 'Mouse', ortalamaSatis: 700, enYuksek: 800, enDusuk: 600 }
]
Bu biriktiricilerin resmî kullanım biçimlerini MongoDB $group belgelerinde inceleyebilirsin.
$match ve $sort pipeline içinde nereye yazılır?

$match gruplamadan önce yazılırsa yalnızca koşulu sağlayan belgeler $group aşamasına ulaşır. Aşağıdaki sorgu, tutarı 1000 veya daha yüksek siparişleri seçip müşteri toplamlarını büyükten küçüğe sıralar.
db.siparisler.aggregate([
{ $match: { tutar: { $gte: 1000 } } },
{
$group: {
_id: "$musteri",
toplamHarcama: { $sum: "$tutar" }
}
},
{ $sort: { toplamHarcama: -1 } }
]).toArray()
[
{ _id: 'Ayşe', toplamHarcama: 3000 },
{ _id: 'Mehmet', toplamHarcama: 1500 }
]
Filtre nedeniyle Ayşe’nin 600 tutarındaki Mouse siparişi, Mehmet’in 800 tutarındaki siparişi hesaplamaya alınmaz. $sort gruplamadan sonra yazıldığında müşteri toplamı veya ürün ortalaması gibi üretilmiş alanlara göre sıralama yapar. $match gruplamadan sonra yazılırsa artık tek tek siparişleri değil, oluşan grup belgelerini filtreler; bu nedenle sorgunun anlamı değişebilir.
Filtreyi erken uygulamak, sonraki aşamalara gönderilen belge sayısını azaltabilir. Gerçek performans etkisi veri hacmine, filtre koşuluna ve sorgu planına bağlıdır. MongoDB $match belgeleri, bu aşamanın pipeline içindeki kullanımını ve filtreleme davranışını açıklar.
MongoDB $group ile SQL GROUP BY nasıl karşılaştırılır?
MongoDB'deki $group aşaması, SQL'deki GROUP BY ile aynı temel ihtiyacı, belgeleri veya satırları belirli bir anahtara göre gruplayıp toplu hesaplamalar yapmayı karşılar. MongoDB bunu aggregation pipeline içindeki bir aşama olarak, SQL ise SELECT sorgusundaki GROUP BY ve SUM, AVG gibi toplama işlevleriyle ifade eder.
siparisler koleksiyonunda gruplama anahtarı musteri alanı olsun. MongoDB sorgusunda _id: "$musteri" ifadesi aynı müşteriye ait belgeleri bir araya getirir. toplamTutar ve ortalamaTutar alanları ise her grup için hangi hesaplamaların yapılacağını belirler.
db.siparisler.aggregate([
{
$group: {
_id: "$musteri",
toplamTutar: { $sum: "$tutar" },
ortalamaTutar: { $avg: "$tutar" }
}
},
{
$sort: { toplamTutar: -1 }
}
])
Çıktı:
[
{
_id: "Ayşe",
toplamTutar: 2600,
ortalamaTutar: 866.6666666666666
},
{
_id: "Mehmet",
toplamTutar: 2400,
ortalamaTutar: 1200
}
]
Aynı hesaplama SQL tarafında, gruplama sütunu GROUP BY sonrasında yazılarak ve toplama işlevleri SELECT bölümünde kullanılarak kurulur.
SELECT
musteri,
SUM(tutar) AS toplamTutar,
AVG(tutar) AS ortalamaTutar
FROM siparisler
GROUP BY musteri
ORDER BY toplamTutar DESC;
| MongoDB kavramı | SQL karşılığı | Açıklama |
|---|---|---|
$group |
GROUP BY |
Belgeleri veya satırları ortak bir değere göre gruplar. |
_id: "$musteri" |
GROUP BY musteri |
Grup anahtarını belirler. |
{ $sum: "$tutar" } |
SUM(tutar) |
Her gruptaki tutar değerlerini toplar. |
{ $avg: "$tutar" } |
AVG(tutar) |
Her grubun ortalama tutarını hesaplar. |
$sort |
ORDER BY |
Hesaplanan sonuçların sırasını belirler. |
Sık yapılan MongoDB $group hataları nasıl düzeltilir?
Sık yapılan $group hataları, grup anahtarının, alan referanslarının veya aşama sırasının yanlış kurulmasından kaynaklanır. Aşağıdaki kontroller, sorgunun hangi bölümünde sorun olduğunu daha hızlı bulmana yardımcı olur.
Alan adını $ işareti olmadan yazmak
MongoDB'de bir alanın değerini aggregation ifadesine aktarmak için alan adının başına $ konur. "tutar" yazıldığında alan yolu yerine sabit metin kullanılmış olur.
// Hatalı
{ $sum: "tutar" }
// Doğru
{ $sum: "$tutar" }
_id alanını eksik bırakmak veya accumulator alanını yanlış yere koymak
$group içinde _id grup anahtarıdır ve zorunludur. Accumulator alanları da doğrudan $group nesnesinin içinde, _id ile aynı seviyede bulunmalıdır.
// Hatalı
{
$group: {
_id: "$musteri",
sonuc: {
toplam: { $sum: "$tutar" }
}
}
}
// Doğru
{
$group: {
_id: "$musteri",
toplam: { $sum: "$tutar" }
}
}
Orijinal alanı filtrelemek için $match'i $group'tan sonra yazmak
tutar gibi orijinal belge alanlarına göre filtreleme yapılacaksa $match genellikle $group aşamasından önce yazılır. Gruplamadan sonra sonuç belgelerinde tutar yerine çoğunlukla _id ve hesaplanan alanlar bulunur.
// Doğru
db.siparisler.aggregate([
{ $match: { tutar: { $gte: 1000 } } },
{
$group: {
_id: "$musteri",
toplam: { $sum: "$tutar" }
}
}
])
Filtre, grup toplamına uygulanacaksa $match sonrasında kullanılabilir. Bu durumda koşul, toplam gibi hesaplanan sonuç alanına bağlanmalıdır.
$sort'u yanlış sonuç alanına bağlamak
$group sonrasında sıralama yapılırken artık hesaplanan alanın adı kullanılmalıdır. Bu örnekte doğru alan toplam olduğu için sıralama tutar üzerinden yapılmaz.
// Doğru
{ $sort: { toplam: -1 } }
- Koleksiyon ve alan adlarını kontrol et.
$groupiçindeki_iddeğerinin grup anahtarını gösterdiğinden emin ol.- Accumulator sözdiziminde alan referanslarının
$işaretiyle yazıldığını kontrol et. $matchkoşulunun orijinal alana mı, hesaplanan sonuç alanına mı uygulanacağını belirle.- Sonuç alanlarının adlarını
$groupile$sortarasında aynı tut. $sortyönünde artan sıralama için1, azalan sıralama için-1kullan.
Sık Sorulan Sorular
$group aşamasında _id alanı neden gereklidir?
_id alanı, belgelerin hangi değere göre aynı grup içinde toplanacağını belirler. Örneğin _id: "$musteri" kullanıldığında aynı müşteriye ait siparişler tek bir grup altında hesaplanır.
$sum ile $avg arasındaki temel fark nedir?
$sum değerlerin toplamını, $avg ise sayısal değerlerin ortalamasını hesaplar. Aynı tutar alanında kullanılsalar bile iki accumulator farklı sonuç alanları üretir.
$match neden çoğu örnekte $group aşamasından önce yazılır?
$match önce yazıldığında yalnızca koşulu karşılayan belgeler gruplamaya gönderilir. Grup toplamı gibi hesaplanmış bir değere göre filtreleme yapılacaksa $match, bu değeri üreten $group aşamasından sonra yazılır.
$group sonucunu toplam veya ortalama değere göre nasıl sıralayabilirim?
$group içinde oluşturduğun sonuç alanını $sort içinde kullanarak sıralayabilirsin. Örneğin toplamı azalan sırada göstermek için { $sort: { toplamTutar: -1 } }, ortalamayı artan sırada göstermek için { $sort: { ortalamaTutar: 1 } } yazılır.
$group sorgularında doğru sonuç için grup anahtarını, hesaplama alanlarını ve aşama sırasını birlikte kontrol etmen yeterli bir başlangıç sağlar.