MongoDB’ye Giriş

MongoDB, RDBMS’ler gibi şemaya gerek duymaz. Yani bir tablo oluşturmak için önce şema yaratmanıza gerek yoktur. Dinamik bir yapıya sahip olduğundan yapıyı belirtmeden veri girebiliriz.
Mongo’da RDBMS sistemlerinde tablo adlandırılan yapılar kolleksiyon (collection), girilen her bir veri de doküman (document) olarak adlandırılır.
Dokümanlar yapı olarak liste (array) ve başka dokümanlar barındırabilir. MongoDB’yi RDBMS sistemlerinden ayıran en büyük unsur bence budur.
Normalde birkaç tablo ile yapabileceğiniz yapıyı MongoDB ile tek kolleksiyonda oluşturabilirsiniz.
Örnek vermek gerekirse; bir kişinin bilgilerini veritabanında saklamak istiyoruz. Kişinin bir adı ve bir soyadı olacağından kisiler isimli tabloda saklayabiliriz. Ancak birden fazla telefon numarası veya adresi varsa, ya her adres veya telefon için tabloda ayrı bir sütun açmamız gerekir. Peki kaç telefon numarası olabileceğini bilmiyorsak? O zaman ayrı bir tablo oluşturup telefon numaralarını bu tabloda saklayarak kişinin id’si ile bağlamamız gerekir. Ancak MongoDB’de buna gerek yoktur; bir kolleksiyonda bir kişinin tek bir telefonu da olabilir, onlarca telefonu da.
MongoDB, javascript yazımını kullanır. Bu sayede javascript ile döngüler yazıp çoklu veri girişi gibi şeyler yapabilirsiniz.

mongo shell’e girmek için konsolda “mongo” komutunu verelim. Mevcut olan veritabanlarını görmek için “show dbs” komutunu kullanabiliriz. Bir veritabanına komut vermek için “use” komutunu kullanıyoruz. Eğer olmayan bir veritabanına use derseniz yeni bir veritabanına geçmiş olursunuz. Seçtiğiniz veritabanını görmek için “db” komutunu verebilirsiniz. İlk veri girilene kadar veritabanı yaratılmış olmaz. O yüzden veri girmeden başka bir veritabanını seçerseniz önce seçtiğiniz veritabanı yaratılmamış olur.


{
"_id" : ObjectId("517297952d423a75b2b3c4b6"),
"isim" : "Bora",
"yas" : 33
}
{
"_id" : ObjectId("5172a996c45522b7d60bcb83"),
"isim" : "Yavuz",
"yas" : 32,
"telefon" : [
"5323232",
"5323233",
"5323234"
]
}
{
"_id" : ObjectId("5172aa8fc45522b7d60bcb84"),
"isim" : "Sedat",
"yas" : 40,
"telefon" : [
"5323232"
],
"adres" : [
{
"tip" : "ev",
"adres" : "adres1"
},
{
"tip" : "is",
"adres" : "adres2"
}
]
}

Yukarıdaki dokümanlar tek bir kolleksiyonda sorunsuzca oluşturulabilir. Bir dokümanın içinde liste, listenin içinde de başka alt dokümanlar olabilir.

Veritabanim adında yeni bir veritabanı yaratalım sonra da ilk verimizi girelim:

use veritabanim
db.kisiler.insert( { “isim”: “Bora”, “yas”: 33 })

Bu şekilde veri girebileceğimiz gibi verimizi bir değişkene atayarak da girebiliriz.

kisi = { “isim”: “Bora”, “yas”: 33}
db.kisiler.insert(kisi)

“Show collections” diyerek de veritabanı içindeki kolleksiyonları görebiliriz.

kisiler
system.indexes

Kisiler kolleksiyonuna girilmiş olan verileri de aşağıdaki komutla sorgulayalım.

db.kisiler.find()

Bu komut kisilerdeki bütün kayıtları getirecektir.

{ "_id" : ObjectId("517297952d423a75b2b3c4b6"), "isim" : "Bora", "yas" : 33 }

“_id” adında bir alan daha girilmiş olduğunu görüyoruz. Bu alan sistem tarafından otomatik olarak girilen benzersiz bir alandır. Primary key gibi düşünebiliriz.

Şimdi bir döngü yardımı ile birçok veri girelim.

for (var i=0; i<1000; i++) db.notlar.insert({“ogrenci”: i, “not”: Math.floor(Math.random()*100)})
db.notlar.find().pretty()
{ "_id" : ObjectId("51729a1a72560375f9566a36"), "ogrenci" : 135, "not" : 9 }
{
"_id" : ObjectId("51729a1a72560375f9566a37"),
"ogrenci" : 136,
"not" : 46
}
{
"_id" : ObjectId("51729a1a72560375f9566a38"),
"ogrenci" : 137,
"not" : 19
}
{
"_id" : ObjectId("51729a1a72560375f9566a39"),
"ogrenci" : 138,
"not" : 73
}
{
"_id" : ObjectId("51729a1a72560375f9566a3a"),
"ogrenci" : 139,
"not" : 80
}

Yukarıda kullandığımız pretty komutu da çıktının daha okunabilir olmasını sağlar.
Gelen bütün sonuçların hepsini aynı anda ekrana yazdırmak mümkün olmadığından mongo sonucun bir kısmını ekrana basar. Açılmış olan cursor’ı ilerletmek için “it” komutunu kullanabiliriz.
Bir sorgu girdiğimizde MongoDB cursor nesnesi döndürür. Bu nesneyi bir döngü ile ilerletebiliriz.

var c = db.notlar.find()
while (c.hasNext()) printjson( c.next())

Bu sayede bütün sonuçları ekrana yazdırmış oluruz.
Bu komuttan sonra herhangi bir cursor nesnesine ulaşmak istediğimizde aşağıdaki gibi bir hata alırız:

printjson(c[3])
undefined

Cursor nesnesi iterasyonunu tamamladığından hafızadan çıkmıştır. Cursor’a bu şekilde ulaşmak yerine array şeklinde ulaştığımızda tek bir sonuç döndürebiliriz.

var c = db.notlar.find()
printjson(c[3])
{ "_id" : ObjectId("51729a1a72560375f95669b2"), "ogrenci" : 3, "not" : 42 }

Bu şekilde kolleksiyona ulaşıldığında cursor tarafından döndürülen bütün nesneler hafızaya atılır ve cursor hafızadan çıkar. Ancak array hafızada kalacağından RAM’i tüketebilir.

Şimdi de kolleksiyonlardaki dokümanları nasıl filtreleyeceğimize bakalım.

db.notlar.find({db.notlar.find({“ogrenci”: 55})
{ "_id" : ObjectId("51729a1a72560375f95669e6"), "ogrenci" : 55, "not" : 63 }

Şimdi de notu 50 olan öğrencileri bulalım:

db.notlar.find({“not”: 50})
{ "_id" : ObjectId("51729a1a72560375f95669bc"), "ogrenci" : 13, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f95669ec"), "ogrenci" : 61, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566a0a"), "ogrenci" : 91, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566a42"), "ogrenci" : 147, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566a89"), "ogrenci" : 218, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566b3d"), "ogrenci" : 398, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566bcb"), "ogrenci" : 540, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566ca4"), "ogrenci" : 757, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566cec"), "ogrenci" : 829, "not" : 50 }
{ "_id" : ObjectId("51729a1a72560375f9566d08"), "ogrenci" : 857, "not" : 50 }

Not: Bütün not değerleri random yaratıldığından sizin alacağınız sonuçların tamamen farklı olabileceğini unutmamakta fayda var.

Eğer sorgudan sadece tek bir doküman dönmesini istiyorsak findOne metodunu kullanabiliriz.

db.notlar.findOne({“not”: 50})
{ "_id" : ObjectId("51729a1a72560375f95669bc"), "ogrenci" : 13, "not" : 50 }

Gelen sonuçları sınırlamak veya sıralamak isteyebiliriz. Örneğin en yüksek not alan ilk 100 öğrenciyi bulalım.

db.notlar.find().limit(100).sort({“not”: -1})

Sort metoduna sıralamak istediğimiz alanı -1 parametresini vererek çağırdığımızda azalan şeklinde bir sıralama yapacaktır.

{ "_id" : ObjectId("51729a1a72560375f95669e4"), "ogrenci" : 53, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566a99"), "ogrenci" : 234, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566aa6"), "ogrenci" : 247, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566ac0"), "ogrenci" : 273, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566ac8"), "ogrenci" : 281, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566ae6"), "ogrenci" : 311, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566b03"), "ogrenci" : 340, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566b1b"), "ogrenci" : 364, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566b96"), "ogrenci" : 487, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566c26"), "ogrenci" : 631, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566cdb"), "ogrenci" : 812, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566cf2"), "ogrenci" : 835, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566d04"), "ogrenci" : 853, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f9566d51"), "ogrenci" : 930, "not" : 99 }
{ "_id" : ObjectId("51729a1a72560375f95669c1"), "ogrenci" : 18, "not" : 98 }
{ "_id" : ObjectId("51729a1a72560375f95669e8"), "ogrenci" : 57, "not" : 98 }
{ "_id" : ObjectId("51729a1a72560375f9566a16"), "ogrenci" : 103, "not" : 98 }
{ "_id" : ObjectId("51729a1a72560375f9566a87"), "ogrenci" : 216, "not" : 98 }
{ "_id" : ObjectId("51729a1a72560375f9566a98"), "ogrenci" : 233, "not" : 98 }
{ "_id" : ObjectId("51729a1a72560375f9566ac7"), "ogrenci" : 280, "not" : 98 }

Varsayılan olarak böyle bir sorgu bütün alanları getirecektir. Sadece istediğimiz alanları getirmek için dönmesini istediğimiz alanları aşağıdaki gibi belirtebiliriz.

db.notlar.find({“not”: 50}, {“not”: true, “_id”: false})

Gelmesini istediğimiz alanları belirterek true, gelmesini istemediğimiz alanlara da false parametresini vermemiz gerekir. _id alanı false olarak belirtmediğimiz sürece dönecektir.
Bu kolleksiyondan sadece öğrencileri getirmek istersek ama filtre uygulamak istemezsek:

db.notlar.find({},{“ogrenci”: true, “_id”: false})

İlk olarak boş bir JSON nesnesi koyabiliriz. Ogrenci alanına true derken _id alanı her zaman döneceğinden ona da false dememiz gerekir.

Şimdi de notu 40’ın altında olan öğrencileri bulalım:

db.notlar.find({“not”: { $lt: 40 }})

Notu 60 ile 90 arasındaki öğrenciler:

db.notlar.find({“not”: {$gt: 60, $lt:90}})

Genelde yapılan bir hata da şöyledir:

db.notlar.find({ “not”: {$gt:60}, “not”: {$lt: 90}})

Bu yazım sadece notları 90’dan aşağı olan öğrencileri getirir. İlk girdiğimiz filtre dikkate alınmaz.

Şimdi de update metoduna bakalım. MongoDB’de update metodu aşağıdaki gibi kullanıldığında güncellenen dokümanı olduğu gibi değiştirir.
Önce bir doküman oluşturalım:

db.kisiler.insert({“isim”: “Bora”, “yas”: 33, “adres”: “İstanbul”})
db.kisiler.insert({“isim”: “Ahmet”, “yas”: 30, “adres”: “İzmir”})
db.kisiler.insert({“isim”: “Onur”, “yas”: 23: “adres”: “İstanbul”})

db.kisiler.update({“isim”: “Bora”}, {“adres”: “İzmir”})
db.kisiler.find()
{ "_id" : ObjectId("5172c6af3cfd9a893f0ce1ce"), "adres" : "İzmir" }
{ "_id" : ObjectId("5172c6d03cfd9a893f0ce1cf"), "isim" : "Ahmet", "yas" : 30, "adres" : "İzmir" }
{ "_id" : ObjectId("5172c6e93cfd9a893f0ce1d0"), "isim" : "Onur", "yas" : 23, "adres" : "İstanbul" }

Gördüğünüz gibi update işlemi ismi Bora olan dokümanda adres alanını güncellemek yerine sadece bizim girdiğimiz adres alanı olan yeni bir doküman yarattı (object id aynı kalmak koşulu ile). Aslında işlem bu hali ile dokümanı bizim yeni girdiğimiz verilerle güncelliyor. Yani sadece adres alanını belirttiğimiz için sadece adres alanı olan bir doküman olarak güncelliyor.
Eğer sadece istediğimiz alanı güncellemek istersek set komutunu kullanmamız gerekir.

db.kisiler.update({“isim”: “Onur”},{$set: {“adres”: “İzmir”}})
db.kisiler.find()
{ "_id" : ObjectId("5172c6af3cfd9a893f0ce1ce"), "adres" : "İzmir" }
{ "_id" : ObjectId("5172c6d03cfd9a893f0ce1cf"), "isim" : "Ahmet", "yas" : 30, "adres" : "İzmir" }
{ "_id" : ObjectId("5172c6e93cfd9a893f0ce1d0"), "adres" : "İzmir", "isim" : "Onur", "yas" : 23 }

Şimdi de çoklu bir güncelleme deneyelim. Notu 45 – 49 arasındaki bütün öğrencilerin notunu 50 yapalım.

db.notlar.update({“not”: {$gte: 45, $lte:49}}, {$set: {“not”: 50}}, {multi: true})

İstersek notu 10 ve altındaki öğrencilere de 20 puan fazladan verebiliriz.

db.notlar.update({“not”: {$lte:10}}, {$inc: {“not”: 20}}, {multi: true})

Kolleksiyonda olmayan bir dokümanıu güncellemek istediğimizde kolleksiyonda herhangi bir değişiklik olmaz. Ancak güncellenmesi istenen doküman olmadığında eklemek istersek upsert flag’ini kullanmamız gerekir.

db.notlar.update({“orgenci”: 1000}, {$set: {“not”:100}}, {upsert: true})

Oluşturduğumuz kolleksiyonda 1000 numaralı bir öğrenci yok. Yukarıdaki komut ile notu 100 olan 1000 numaralı bir öğrenci ekledik. Aynı komutu aşağıdaki şekilde çalıştırırsak bu sadece bu öğrencinin notunu güncelleyecektir.

db.notlar.update({“orgenci”: 1000}, {$set: {“not”:99}}, {upsert: true})

MondoDB’de or ve and deyimlerini de kullanabiliriz. Aslında yukarıdaki örneklerde and kullandık.

db.notlar.find({“not”: {$gt:20,$lt:30}})

deyimi bize 20 ve 30 arasındaki değerleri getirecektir. Bunu şu şekilde de yazabilirdik:

db.notlar.find({ $and: [ {“not”: {$gt:10}},{“not”: {$lt:20}}]})

Notu 1 veya 99 olan öğrencileri de şu şekilde getirebiliriz:

db.notlar.find({ $or: [ {“not”: 1},{“not”: 99}]})

Dokümandan kaldırmak istediğimiz bir alan olursa unset metodunu kullanmamız gerekir.
100 nolu öğrencinin not alanını kaldırmak için:

db.notlar.update({“ogrenci”:100},{$unset:{“not”:1}})
db.notlar.find({“ogrenci”:100})
{ "_id" : ObjectId("51729a1a72560375f9566a13"), "ogrenci" : 100 }

Tümden silmek istediğimiz dokümanlar için de remove metodunu kullanabiliriz.
Kolleksiyondan notu 1 olan öğrencileri silelim.

db.notlar.remove({“not”:1})
db.notlar.find(“not”:1})

Sonuç döndürmeyecektir.