NoSQL – MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri

Merhaba,

Klasik olarak veritabanından elde edilen veriler üzerinde belli başlı işlemleri NoSQL sistemlerinde de uygulayabilmekteyiz. Bu içeriğimizde MongoDB veritabanında barındırılan veriler üzerinde sıralama, limit belirleme vs. gibi sıradan işlevlerle beraber $group, $lookup vs. gibi aggregate operatörleri ile elde edilen verilerin daha farklı modifiyeler sağlanarak nasıl anlamlandırılabileceğini inceliyor olacağız.

İçeriğimizde örneklendirme aşamasında model olarak;

const mongoose = require("mongoose");
 
module.exports = mongoose.model("Employees", new mongoose.Schem a({
    _id: Number,
    userName: String,
    name: String,
    surName: String,
    age: Number
}));

router yapısı olarak;

const express = require("express");
const router = express.Router();
router.get("/", (request, response, next) => {
    response.send("Merhaba dünya...");
});
module.exports = router;

ve ana modül olarak;

const express = require("express");
const app = express();
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/companydb")
    .then(() => console.log("Veritabanı bağlantısı başarıyla sağlanmıştır..."))
    .catch(error => console.log("Veritabanına bağlantı oluşturulurken beklenmeyen bir hatayla karşılaşıldı...", error.message));
const employeeRouter = require("./routers/employeeRouter");
app.use("/employee", employeeRouter);
app.listen(5000);

yapılarını kullanacak ve bunlardan route yapısında çalışacağımızı baştan bildirmekte fayda görüyorum.

Sıralama(sort)

Belirtilen kolona göre verileri sıralamaktadır. Klasik SQL’de ki “Order By” komutana karşılık gelmektedir. Sayısal tipte kolonlarda büyüklük-küçüklük kıyaslaması yaparken metinsel kolonlarda alfabetik sıralama gerçekleştirmektedir.

const express = require("express");
const router = express.Router();
const employeeModel = require("../models/employee");
router.get("/", (request, response, next) => {
    employeeModel.find({}, (error, data) => {
        if (!error)
            response.json(data);
        else
            response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
    }).sort({ "_id": -1 });
});
module.exports = router;

Dikkat ederseniz eğer belirtilen “_id” kolonuna “-1” değeri veirlmiştir. Bu değer sayısal olarak büyükten küçüğe, metinsel olarakta z’den a’ya anlamını ifade etmektedir. Eğer ki tam tersi bir sıralama isteniyorsa “1” değeri verilmesi yeterlidir.

const express = require("express");
const router = express.Router();
const employeeModel = require("../models/employee");
router.get("/", (request, response, next) => {
    employeeModel.find({}, (error, data) => {
        if (!error)
            response.json(data);
        else
            response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
    }).sort({ "name": 1, "_id": -1 });
});

module.exports = router;

Birden fazla kolonda sıralama yapmak istenirse yukarıdaki gibi çalışılabilmektedir. Burada önce “name” kolonuna göre a’dan z’ye ardından “_id” kolonuna görede büyükten küçüğe bir sıralama gerçekleştirilmiştir.

Limit Belirleme(limit)

Eldeki veri listesinin en baştaki verisinden belirtilen adet kadar veriyi getirecektir. Klasik SQL’de ki “Top n” komutuna karşı gelmektedir.

const express = require("express");
const router = express.Router();
const employeeModel = require("../models/employee");
router.get("/", (request, response, next) => {
    employeeModel.find({}, (error, data) => {
        if (!error)
            response.json(data);
        else
            response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
    }).limit(2);
});
module.exports = router;

Atlama(skip)

Veri listesinin başından belirtilen değer kadar veriyi görmemezlikten gelecek gerisini getirecektir.

const express = require("express");
const router = express.Router();
const employeeModel = require("../models/employee");
router.get("/", (request, response, next) => {
    employeeModel.find({}, (error, data) => {
        if (!error)
            response.json(data);
        else
            response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
    }).skip(2);
});
module.exports = router;

Aggregate Operatörleri

MongoDB’de aggregate operatörleri klasik SQL’de ki “aggregate” fonksiyonlarıyla aynı özellikleri barındırmakta ve orada hangi mantıkta hangi durumlarda kullanılıyorsa burada da aynı mantık ve durumlar için kullanılan, aradaki tek farkın oradakinin fonksiyon buradakinin ise operatör olması olan işlevlerdir.

Aggregate parametrelerini kullanabilmek için model üzerinden aggregate fonksiyonunu çağırmalısınız. Birazdan göreceğimiz tüm operatörler için aşağıdaki yapısal durumu genelleyerek zihninizde oturtmanız sizler için yapıların kullanımına dair oldukça kolaylık sağlayacaktır.

    model.aggregate([
        {
            [$parametre]: {
                [...işlemler...]
            }
        }
    ], (error, data) => {

    });

Yukarıdaki prototipi incelerseniz “aggregate” fonksiyonu içerisinde bir dizi oluşturulmakta ve kullanılacak operatör bu dizi içerisinde yaratılmış bir object içerisinde çağrılmaktadır. Aynı şekilde operatörde işlevselliği için bir başka object’e ihtiyaç duymakta ve tüm işlemler en alttaki objede gerçekleştirilmektedir. Tüm parametreler bu iskelette kullanıldığından dolayı bu yapıyı genelleyebilirsiniz.

  • $match Operatörü
    Herhangi bir kolona verilen değer ile eşleşen documentleri getiren operatördür.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $match: {
                    name: "Gençay"
                }
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    “name” kolonu “Gençay” olan tüm documentleri getirecektir.

    Eğer birden fazla kolon üzerinde eşleştirme yapmak istiyorsanız kolonları aşağıdaki gibi çoğaltabilirsiniz.

    .
    .
    .
                $match: {
                    name: "Gençay",
                    _id: 1
                }
    .
    .
    .
    
  • $group Operatörü
    Verileri gruplamamızı ve grup içi hesaplama yapmamızı sağlayan operatördür. Klasik SQL’de ki “Group By” komutuna karşılık gelmektedir.
    NoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri
    Yukarıdaki listede aynı isimde kaç adet personel mevcuttur? sorusuna karşılık her bir ismi gruplayıp karşılığında kaç adet kaydın olduğunu aşağıdaki şekilde hesaplayabiliriz.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $group: {
                    _id: "$name",
                    adet: { $sum: 1 }
                }
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    Yukarıdaki kod bloğunda $group operatörünü detaylandırmak için aşağı alalım;

    .
    .
    .
                $group: {
                    _id: "$name",
                    adet: { $sum: 1 }
                }
    .
    .
    .
    

    Gördüğünüz üzere “_id” alanına gruplama işlemi yapılan kolon belirtilirken, ismi tarafımızca opsiyonel olarak verilebilen alana(adet) gruplanmış değere karşılık toplam sayı belirtilmiştir. Burada “_id” alanı birebir isimde zorunludur lakin “adet” alanının ismine müdahalede özgürsünüz. Ayrıca gruplarda hesaplama yapılırken “$sum” toplamayı, “$avg” ortalamayı bildirmektedir. Tüm bunların dışında eğer ki bir kolon ismiyle çalışmanız gerekecekse yukarıda da görüldüğü üzere “$” karakterini kolon isminin önüne belirtmeniz gerekmektedir.
    NoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri

    .
    .
    .
                $group: {
                    _id: "$name",
                    adet: { $avg: "$age" }
                }
    .
    .
    .
    

    Eğer yukarıdaki gibi kodumuzu geliştirseydik her bir grup içerisinde yaş ortalaması alınmış olacaktı.

  • $project Operatörü
    Sorgulama neticesinde gelen verilerden sadece istenilen alanları getirmemizi sağlamaktadır.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $project: {
                    surName: true,
                    age: true,
                    name: 1
                }
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    Görüldüğü üzere belirtilen kolonlara değer olarak “true” ya da “1” değerleri verilmiştir. Her iki değerde aynı anlamı ifade etmekte ve ilgili kolonun gösterilmesi gerektiğini belirtmektedir. Yapıya dahil edilmeyen kolonlar ise dolayısıyla gösterilmeyecektir.

  • $sort, $limit ve $skip Operatörleri
    İlgili içeriğimizde fonksiyonel denklerine ilk konular olarak işlevsel açıdan değindiğimiz bu operatörleri aggregate olarakta kullanabilmekteyiz.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $sort: { _id: -1 }
            },
            {
                $skip: 1
            },
            {
                $limit: 2
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    Burada yapısal olarak sıralamaya uygun bir işlevsellik söz konusu olacak ve öncelikte $sort operatöründen dolayı “_id” kolonuna göre büyükten küçüğe sıralama işlemi yapılacak, ardından $skip operatörü bir adet eleman atlayacak ve son olarak $limit iki adet veriyi getirecektir.

  • $lookup Operatörü
    NoSQL yaklaşımından kaynaklı MongoDB’nin bir ilişkisel veritabanı olmadığını biliyoruz. Ancak bazı durumlar vardır ki tablolar/collectionlar arası ilişki kurulmadan devam edebilmek mümkün değildir. İşte böyle gereksinim olan durumlarda $lookup operatörü ile tabloları ilişkilendirebilmekteyiz.

    Employee CollectionOrder Collection
    NoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate OperatörleriNoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri

    Yukarıdaki collection ekran görüntülerini incelerseniz eğer employee collectionının “_id” kolonu ile order collectionının “employeeId” kolonu mantıksal ilişkiye sahiptir. Şimdi gelin bunu $lookup operatörü ile fiziksel bir hale getirelim.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $lookup: {
                    from: "order",
                    localField: "_id",
                    foreignField: "employeeId",
                    as: "siparişler"
                }
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    Yapıyı optimize etmek için şöyle aşağıya alalım;

    .
    .
    .
                $lookup: {
                    from: "order",
                    localField: "_id",
                    foreignField: "employeeId",
                    as: "siparişler"
                }
    .
    .
    .
    

    Gördüğünüz üzere $lookup operatörü içerisinde birden fazla değer alarak iki tablo arasında ilişkilendirmeyi gerçekleştirmiş bulunmaktadır. Peki bu değerler neledir?

    • from: İlişki kurulacak collection adını tutar.
    • localField: Modelde ilişki kuracak kolon bilgisini tutar.
    • foreignField: Diğer collectiondaki ilişki kurulacak kolon bilgisini tutar.
    • as: İlişkilendirme sonucu gelen verilerin atanacağı alanın adını tutar.

    NoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri

    Eğer ki bu ilişkilendirme sonucunda istenilen alanları göstermek istiyorsanız aşağıdakilerin yapılması gerekmektedir;

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.aggregate([
            {
                $lookup: {
                    from: "order",
                    localField: "_id",
                    foreignField: "employeeId",
                    as: "siparişler"
                }
            },
            {
                $unwind: "$siparişler"
            },
            {
                $project: {
    
                    orderNumber: "$siparişler.orderNumber",
                    name: 1
                }
            }
        ], (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    Yukarıdaki yapılan işlemlerde karşımıza “$unwind” operatörü çıkmaktadır. Şimdi gelin yukarıda ne yapıldığına değinirken bir yandan da “$unwind” operatörünün ne işe yaradığından bahsedelim.

    .
    .
    .
            {
                $lookup: {
                    from: "order",
                    localField: "_id",
                    foreignField: "employeeId",
                    as: "siparişler"
                }
            },
            {
                $unwind: "$siparişler"
            },
            {
                $project: {
    
                    orderNumber: "$siparişler.orderNumber",
                    name: 1
                }
            }
    .
    .
    .
    

    Göstermek istediğimiz alanlar içerisinde “siparişler” alanındaki verileride göstermek istersek bu alana $project içerisinden erişilebilmesi gerekmektedir. Dolayısıyla $unwind bir adım önceki operatörde yapılan çalışma neticesinde oluşturulan “siparişler” alanını bir sonraki operatöre taşımamızı sağlamakta ve işlevsel olarak iki operatör arasına girerek alan taşınımını gerçekleştirmektedir.
    NoSQL - MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri

  • $exists Operatörü
    Kolon var mı? yok mu? sorusuna karşılık kullanılan bir operatördür.

    const express = require("express");
    const router = express.Router();
    const employeeModel = require("../models/employee");
    router.get("/", (request, response, next) => {
        employeeModel.find({
            muiddin: {
                $exists: false
            }
        }, (error, data) => {
            if (!error)
                response.json(data);
            else
                response.send("Beklenmeyen bir hata ile karşılaşıldı.", error.message);
        });
    });
    module.exports = router;
    

    “muiddin” isminde kolonu olmayan listelenecektir. Eğer ki “true” değerini verseydik belirtilen isimde kolonu olanları listeletmiş olacaktık.

İlgilenenlerin faydalanması dileğiyle…

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. 04 Eylül 2018

    […] yazılarımdan NoSQL – MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri başlıklı makalemde “$lookup” operatörü ile MongoDB veritabanında iki farklı […]

  2. 05 Eylül 2018

    […] yazılarımdan NoSQL – MongoDB İşlevsel Fonksiyonlar ve Aggregate Operatörleri başlıklı makalemde “$group” operatörü ile nasıl gruplama işleminin […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

Copy Protected by Chetan's WP-Copyprotect.