Sunday, February 8, 2015

MongoDB ile Çalışmak



Klasik web uygulamaları, çok katmanlı mimari olarak tasarlanırlar. Her bir katmanın bir sorumluluğu vardır. Çok katmanlı yapıdaki her bir katman, kendinden önceki katmana hizmet verirken, kendinden sonraki katmandan hizmet alır. Şekil-1'de bu katmanlı yapıda yer alan 5 katman tanıtılmıştır:
  • İ: İstemci katmanı. Çoğu zaman bir web tarayıcısıdır.
  • S: Sunum katmanı. Uygulamanın kullanıcıya dönük yüzünü oluşturmaktan sorumludur. Sunum mantığını içerir.
  • İM: İş Mantığı katmanı. İşin asıl yapıldığı yerdir.
  • T: Tümleştirme katmanı. Kurumsal uygulamalar geliştirilirken çoğu zaman kurum içindeki ya da dışındaki diğer yazılımlarla etkileşim kurması istenir. Tümleştirme katmanı bu iletişimin sorunsuz gerçekleşmesinden sorumludur.
  • K: Kaynak katmanı. Verilerin kalıcı olarak saklandığı katmandır. Veritabanı (Oracle, MySQL, DB2 gibi), Dizin sunucusu (Active Directory, OpenLDAP), CRM (Oracle Siebel, Microsoft Dynamics), ERP uygulaması gibi çok farklı türde olabilir.

Şekil-1: Klasik web mimarisi

İstemci ile sunucu arasındaki etkileşim istek-cevap örüntüsündedir. Uygulamanın arayüzünün değişmesi için mutlaka sunucuya bir isteğin gitmesi gerekir. Sunum mantığı da sunucuda yer alır. Bu durum mimarinin zayıf halkasını oluşturur. Sunumdaki her değişiklik mutlaka sunucuya uğramak zorundadır. Sunumdaki bu problemin, Ajax ile bir ölçüde gizlendiği söylenebilir. Ajax teknolojisi, istemcilerin isteklerini asenkron bir şekilde sunucuya iletmelerine olanak sağlamıştır. Ajax istek cevabı şimdi sadece arayüzdeki değişiklikleri içerebilmektedir. Bu sunucunun yükünü bir miktar azaltır.
Kaynak katmanında çoğu zaman ilişkisel veri tabanları kullanılır. İlişkisel veri tabanları, ilişki cebri üzerine kurulmuştur. 1970'li yıllardan beri geliştirilen, sınanmış olgun bir teknolojidir. Ama iş ölçeklenebilirliğe geldiğinde, doğası gereği, başarılı oldukları söylenemez. Her kurumsal uygulama, ilişkisel veri tabanına ihtiyaç duyar. Dolayısı ile veritabanları her zaman potansiyel bir dar boğaz noktasıdır. 
Web tarayıcıları, HTML 5 ile birlikte gelen API'ler yardımı ile üzerinde uygulama koşturabileceğimiz platformlara dönüştüler:
  • Canvas API, 
  • Data Storage API, 
  • Drag and Drop API, 
  • Messaging and Workers API, 
  • WebSocket API, 
  • Geolocation API
Artık WebGL yardımı ile doğrudan grafik kartı üzerinde işlem yapabiliyoruz, web tarayıcısında yerel bir veri tabanı üzerinde çalışabiliyoruz. Tüm bu API'ler için programlama dili ise JavaScript (JS). Web tarayıcıları, uzun bir süredir, JS'de yazılmış kodları çok hızlı çalıştırabilen JS motorlarına sahipler. Bu JS motorları, Java Sanal Makinasının kullandığı ihtiyaç anında derleme gibi kodun hızlı çalışmasını sağlayacak teknikleri uygulamaktalar. İstemcileri makinalarında bugün en az iki çekirdekli işlemciler bulunuyor, ekran kartları en kötü HD filmleri izleyebilecek, orta karmaşıklıktaki oyunları 30 fps'de oynatabilecek ana kart üzerine gömülü grafik modüllerine sahipler. Makinaların en kötü 4GB bellekleri ve çok miktarda depolama ortamları bulunuyor. İnternet ağı bant genişlikleri HD filmleri takılmadan izlememize imkan sağlıyor. Tüm bu gelişmeler, artık klasik web uygulamalarını terk etmemizi gerektiriyor. Yenil nesil uygulamaların ihtiyaçları farklı:
  • Daha çok kullanıcı etkileşimi, 
  • Daha zengin kullanıcı deneyimi
  • Mobil cihazlardan, hareket halindeyken erişim
  • Değişen bant genişliklerinde çalışabilme
  • Bağlantının kesildiği durumlarda da çalışabilmek
  • Süper ölçeklenebilirlik
Bu ihtiyaçları karşılamak üzere yeni web mimarilerinin geliştirildiğini görüyoruz. Klasik web uygulamalarının aksine bu yeni mimarilerde sunum ve sunum mantığının sunucudan istemciye taşındığını görüyoruz (Şekil-2):
Şekil-2: İnce sunucu-donatımlı istemci modeli

Bu yeni mimaride sunucunun inceldiğini görüyoruz. İstemcilerin artık daha seyrek olarak ve daha çok veriye erişmek için sunucuya gittiklerini görüyoruz. İstemci tarafta programlama dili JS olduğu için veriye erişirken, en verimli kodlama biçimi Java Script Object Notation (JSON) olacaktır. HTTP protokolü üzerinden eriştiğimiz sunucuda, veri servisinin, kabul görmüş RESTful servis olarak tasarlandığını görüyoruz. RESTful servisler HTTP protokolünü kullanır. HTTP protokolü belleksiz yalın bir mantığa sahiptir. Dolayısı ile son derece ölçeklenebilir bir çözüm sunar. Tüm bu mimari değişimde, veri tabanı olarak sadece ilişkisel veri tabanı kullanamayız. Bu süreçte, ilişkisel veritabanları ile birlikte kullanılacak, yeni doğan ihtiyaçlara yanıt verebilecek yeni veritabanlarının türediği görüyoruz. Bu veri tabanlarına toplu olarak "Not Only SQL" anlamında NoSQL adını veriyoruz.  
Şekil-3: Süper ölçeklenebilir model
Farklı türlerde NoSQL veri tabanları bulunuyor:
Listenin bununla sınırlı olduğunu düşünmeyin. Bu yazıda JSON dokümanlarını saklayan bir NoSQL çözümü inceleyeceğiz: MongoDB. Kurulumunu ve yapılandırılmasını ele alacağız. Ayrıca JS kullanarak nasıl sorgu çalıştırılacağını çalışacağız.

Kurulum


MongoDB kurulum dosyasını bu bağlantıdan elinizdeki işletim sistemi için indirmeniz gerekiyor. Windows, Linux, Solaris ve MacOS platformları için MongoDB kurulum dosyası bulabilirsiniz. Açık kaynak kodlu bir proje olan MongnDB, C++'da yazılmıştır. Kaynak kodlara bu bağlantıdan erişebilirsiniz.
MongoDB kurulumu indirdiğiniz zip dosyasını uygun bir dizine açarak tamamlanıyor. Bu yazı için kurulumu Windows 8.1 x64 platformunda gerçekleştirdim. mongodb-win32-x86_64-2008plus-ssl-v3.4-latest.zip dosyasını c:\opt64 dizinine açtım ve dizini mongodb-3.4 olarak isimlendirdim. Tüm çalıştırılabilir dosyalar bin dizini içinde bulunuyor:
  • mongo.exe: Komut satırı istemcisi
  • mongod.exe: Sunucu yazılım
  • mongodump.exe: Yedek almak için kullanılan yazılım. Yedekleme dosyaları bson formatındadır.
  • bsondump.exe: bson formatındaki dosyaları komut satırından okumak için kullanılır.
  • mongoexport.exe: MongoDB sunucuları arasında veri alış verişi için kullanılır.
  • mongofiles.exe
  • mongoimport.exe: MongoDB sunucuları arasında veri alış verişi için kullanılır. Bir örneğini yazının devamında bulabilirsiniz.
  • mongooplog.exe
  • mongoperf.exe
  • mongorestore.exe: mongodump ile alınan bütün bir yedekten geri dönmek için kullanılır
  • mongos.exe
  • mongostat.exe: Unix'deki vmstat komutuna benzer bir işleve sahiptir. mongod sunucunun ekleme/silme/güncelleme başarımını izlemenize olanak sağlar:
mongostat
connected to: 127.0.0.1
insert  query update delete getmore command flushes mapped  vsize    res faults  locked db idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:24
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:25
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:26
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:27
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:28
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:29
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 admin:0.0%          0       0|0     0|0    62b     3k     1   01:32:30
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:31
    *0     *0     *0     *0       0     1|0       0   320m  2.91g    80m      0 world:0.0%          0       0|0     0|0    62b     3k     1   01:32:32
  • mongotop.exe: Unix'deki top komutuna benzer bir işleve sahiptir. Bu kez mongod yönetimindeki torbaların kullanım istatistiklerini listeliyor:
mongotop
connected to: 127.0.0.1

                            ns       total        read       write
       world.system.namespaces         0ms         0ms         0ms
               world.system.js         0ms         0ms         0ms
          world.system.indexes         0ms         0ms         0ms
                 world.movies1         0ms         0ms         0ms
              world.countries2         0ms         0ms         0ms
              world.countries1         0ms         0ms         0ms
                 world.cities2         0ms         0ms         0ms
          local.system.replset         0ms         0ms         0ms

MongoDB istemci-sunucu mimarisine sahiptir. Sunucuyu başlatmak için mongod.exe programını kullanıyoruz. Sunucuya komut satırından bağlanmak için ise mongo.exe programını kullanıyoruz. MongoDB JSON dokümanlarını hızlı erişim için bellekte, kalıcı hale getirmek için ise BSON (Binary JSON) olarak diskte saklar. BSON, JSON formatındaki dokümanları bilgisayar ağı üzerinden etkin aktarımı için tasarlanmış bir formattır. Formatın detaylarına bu bağlantıdan erişebilirsiniz.
Sunucuyu komut satırından çalıştırmak için aşağıdaki komutları bir betik haline getirip çalıştırıyoruz:
SET MONGODB_HOME=c:\opt64\mongodb-3.4
SET PATH=%MONGODB_HOME%\bin;%PATH%
start mongod --dbpath=%MONGODB_HOME%\data\db

Sunucunun ekran çıktısı aşağıda verilmiştir:

Windows servise eklemek için Administrator yetkisi ile aşağıdaki komutları çalıştırmak gerekir:
mkdir c:\opt64\mongodb-3.4\log
echo logpath=c:\opt64\mongodb-3.4\log\mongolog > mongo.cfg
set PATH=c:\opt64\mongodb-3.4\bin;%PATH%
cd c:\opt64\mongodb-3.4
mongod --install --config c:\opt64\mongodb-3.4\mongo.cfg --dbpath c:\opt64\mongodb-3.4\data\db

Windows servise MongoDB adıyla eklenen sunucuyu başlatmak ve kapatmak için ne komutu yeterlidir:
net start MongoDB
net stop MongoDB

Bundan sonraki örneklerde kullanmak üzere iki tane veri tabanını yükleyeceğiz. Bunun için mongoimport yazılımını kullanacağız. Örnek veri tabanlarını bu bağlantıdan indirip, örneğin c:\tmp dizinine açalım. İçinden çok sayıda json uzantılı dosya çıkacaktır: 
  • countries_v1.json
  • countries_v2.json
  • cities_v2.json
  • directors_v2.json
  • genres_v2.json
  • movies_v1.json
  • movies_v2.json
Bu dosyaları mongoimport yazılımını kullanarak veri tabanına geri yükleyelim:
mongoimport  --db imdb --collection movies1 --quiet --file c:\tmp\movies_v1.json --jsonArray
mongoimport  --db imdb --collection movies2 --quiet --file c:\tmp\movies_v2.json --jsonArray
mongoimport  --db imdb --collection directors2 --quiet --file c:\tmp\directors_v2.json --jsonArray
mongoimport  --db imdb --collection genres2 --quiet --file c:\tmp\genres_v2.json --jsonArray

mongoimport  --db world --collection countries1 --quiet --file c:\tmp\countries_v1.json --jsonArray
mongoimport  --db world --collection countries2 --quiet --file c:\tmp\countries_v2.json --jsonArray
mongoimport  --db world --collection cities2 --quiet --file c:\tmp\cities_v2.json --jsonArray

İlişkisel veri tabanlarında verileri tablolarda kayıtlar olarak saklıyoruz. MongoDB'de ilişkisel veri tabanlarındaki tablolara karşılık olarak torbalar (=Collection) yer alır. Tablolardaki kayıtlara karşılık olarak torbalarda, json dokümanlar yer alır (Şekil-4). İlişkisel veri tabanlarında, normalizasyon kurallarına uygun olarak, veri tutarlığını sağlamak ve veri tekrarı önlemek amacıyla, kayıtları tablolara dağıtıyoruz. Böylelikle, bu tablolar arasında, yabancı anahtarlar üzerinden, bire-bir, bire-çoklu, çokludan-bire ve çokludan-çokluya türünde ilişkiler yaratıyoruz. Bu yaklaşım, veri tutarlılığı açısından gerekli olsa da veriye okumak ya da değiştirmek amacıyla eriştiğimizde başarımın düşmesine neden olmaktadır. İlişkisel veri tabanları bu durumu aşmak için çeşitli çözümler geliştirseler de yüksek TPS (Transactions Per Second) değerleri önündeki en büyük engel olarak duruyor. MongoDB'de verileri aynı ilişkisel veri tabanlarında olduğu gibi farklı torbalara dağıtabilir ve bu torbalar arasında yabancı anahtarlar üzerinden ilişkiler kurabiliriz. Ancak MongoDB'de bu çalışma şeklinde yüksek başarım alınamaz, yüksek ölçeklenebilirliğe sahip uygulamalar geliştirilemez. Bunun yerine, torbalarda ilişkileriyle beraber tüm verileri saklamak daha uygun olur. 

Şekil-4: MongoDB'de Veri tabanı, Torba ve Doküman İlişkisi

Örneğimizde, imdb veri tabanındaki movies1 torbasında, film bilgileri, json dokümanı olarak saklanmaktadır. movies1 torbasındaki bir filme ulaştığımızda, filmin adına, yılına, yönetmenlerinin bilgilerine ve tür bilgilerine ulaşıyoruz:
mongo
MongoDB shell version: 3.4
connecting to: test
> use imdb
switched to db imdb
> show collections
directors2
genres2
histogram
movies1
movies2
system.indexes
> db.movies1.find({"_id": 1})
{ "_id" : 1, "directors" : [ { "_id" : 1, "imdb" : "nm1989536", "name" : "Marc Webb" } ], "genres" : [ { "name"
: "Comedy", "_id" : 1 }, { "name" : "Drama", "_id" : 2 }, { "name" : "Romance", "_id" : 3 } ], "imdb" : "tt10226
03", "title" : "500 Days Of Summer", "year" : 2007 }
>

Buna karşılık olarak movies2 torbasında ise doğrudan yönetmen ve tür bilgisi bulunmuyor. Türlere ve yönetmenlere erişmek için ayrı torbalar var: directors2, genres2
mongo
MongoDB shell version: 3.4
connecting to: test
> use imdb
switched to db imdb
> db.movies2.find({"_id": 1})
{ "_id" : 1, "directors" : [ { "id" : 1 } ], "genres" : [ { "id" : 1 }, { "id" : 2 }, { "id" : 3 } ], "imdb" : "
tt1022603", "title" : "500 Days Of Summer", "year" : 2007 }
>

Birinci tasarımda, veri tutarlılığını sağlamak geliştiricinin üstlendiği bir göreve dönüşmüştür. Buna karşılık birinci yöntemde veriye erişim başarımı en üst noktadadır. İkinci yöntemde ise veri tutarlılığı sağlanırken ve veri tekrarı önlenirken başarım düşecektir. Üstelik MongoDB'de tek bir torba üzerindeki tek bir ekleme, silme, güncelleme işlemleri atomik olarak gerçeklenir, ancak aynı ya da farklı torbalar üzerindeki bir dizi işlem, atomik olarak gerçeklenmesi garanti edilmez. MongoDB "transactional" bir veri tabanı değildir. Bu nedenle verilerin hayati derecede kritik olduğu, veri kaybına ya da tutarsızlığına tahammülün olmadığı uygulamalarda kullanılmamalıdır.  Ancak ACID uyumluluğuna sahip "transactional" bir MongoDB sunucusu bulunuyor: TokuMX. Şimdilik sadece Linux dağıtımları mevcut. Ücretsiz açık kaynak kodlu topluluk sürümüne bu bağlantıdan erişebilirsiniz. TokuMX fraktal ağaç yaklaşımını kullanmaktadır. 
Yükleme işleminin başarılı olduğunu test etmek için sunucuya mongo istemcisi ile bağlanalım ve veri tabanlarını ve bu veri tabanlarındaki torbaların listesini alalım:
mongo
MongoDB shell version: 3.4
connecting to: test
> show dbs
admin  (empty)
imdb   0.078GB
local  0.078GB
world  0.078GB
> use imdb
switched to db imdb
> show collections
directors2
genres2
movies1
movies2
system.indexes
> use world
switched to db world
> show collections
cities2
countries1
countries2
system.indexes
>

Torbalar Üzerinde Çalışmak

Çalışmaya önce bir torbaya kayıt ekleyerek başlayalım:
mongo
MongoDB shell version: 3.4
connecting to: test
> use imdb
switched to db imdb
> var tt2179136= {};
> tt2179136.title="American sniper";
American sniper
> tt2179136.year=Number(2014);
2014
> tt2179136.imdb= "tt2179136";
tt2179136
> var nm0000142 = {};
> nm0000142.name= "Clint Eastwood";
Clint Eastwood
> nm0000142.imdb= "nm0000142";
nm0000142
> tt2179136.directors= [ nm0000142 ];
[ { "name" : "Clint Eastwood", "imdb" : "nm0000142" } ]
> var action= { "name": "Action"}
> var biography= { "name": "Biography"}
> var drama= { "name": "Drama"}
> tt2179136.genres= [ action, biography, drama ];
[
        {
                "name" : "Action"
        },
        {
                "name" : "Biography"
        },
        {
                "name" : "Drama"
        }
]
> db.movies1.insert(tt2179136);
WriteResult({ "nInserted" : 1 })
>

Eklediğimiz bu kayda ulaşmak için ise aşağıdaki çağrıyı oluşturuyoruz:
> db.movies1.findOne({ "imdb": "tt2179136"})
{
        "_id" : ObjectId("54d764fc492627f560e3fe41"),
        "title" : "American sniper",
        "year" : 2014,
        "imdb" : "tt2179136",
        "directors" : [
                {
                        "name" : "Clint Eastwood",
                        "imdb" : "nm0000142"
                }
        ],
        "genres" : [
                {
                        "name" : "Action"
                },
                {
                        "name" : "Biography"
                },
                {
                        "name" : "Drama"
                }
        ]
}

Dikkat edilirse, suncunun torbaya attığım json dokümana fazladan bir alan eklediği görülür: _id. Bu alan torbadaki her bir doküman için ister biz eklemiş olalım isterse de sunucu tarafından otomatik olarak verilmiş olsun, istisnasız vardır. _id alanı aslında ObjectId tipinde bir başka json nesnesidir. ObjectId 12 sekizlikten oluşan bir dizi alandan oluşur (Şekil-5). 4-Sekizlik boyutundaki ilk alan dokümanın eklenme tarihini saklar. Bu bilgiye getTimestamp() çağrısı ile ulaşabiliriz:
> var sniper= db.movies1.findOne({"title": /sniper/});
> sniper._id
ObjectId("54d78d5bb93815e7249f4e6b")
> sniper._id.getTimestamp()
ISODate("2015-02-08T16:22:51Z")
>

2-sekizlik boyundaki alan mongod prosesinin proses kimliğinden (PID), 3-sekizlik boyundaki alan makinanın IP değerinden ve 3-sekizlik alan ise sayaç değerinden oluşur. Bu şekilde _id değerinin tüm evrende tekil bir değer olması sağlanır. Bu kimlik değerini karakter katarı olarak okumak mümkündür:
> sniper._id.str
54d78d5bb93815e7249f4e6b
> sniper._id.toString()
ObjectId("54d78d5bb93815e7249f4e6b")

Şekil-5: ObjectID alanları

Sadece filmin adına ve yılına ulaşmak için ise ikinci bir parametre daha vermemiz gerekir:
> db.movies1.findOne({ "imdb": "tt2179136"},{"title": 1, "year":2, "_id": 0})
{ "title" : "American sniper", "year" : 2014 }

Tüm filmlerin listesine ulaşmak için ise find() çağrısını kullanıyoruz:
> var cur= db.movies1.find({})
> cur.next()
{
        "_id" : 1,
        "directors" : [
                {
                        "_id" : 1,
                        "imdb" : "nm1989536",
                        "name" : "Marc Webb"
                }
        ],
        "genres" : [
                {
                        "name" : "Comedy",
                        "_id" : 1
                },
                {
                        "name" : "Drama",
                        "_id" : 2
                },
                {
                        "name" : "Romance",
                        "_id" : 3
                }
        ],
        "imdb" : "tt1022603",
        "title" : "500 Days Of Summer",
        "year" : 2007
}
>

find() her zaman bir imleç (=cursor) döner. Bu kayıtlara ulaşmak için bir JS nesnesidir. next() çağrısını kullanarak bir sonraki dokümana ulaşabiliriz. İmleci kullanarak skip() ve limit() çağrıları yardımı ile sayfalama yapmak mümkündür: 
> var cur= db.movies1.find({},{"title": 1, "_id":0})
> cur.skip(10).limit(10)
{ "title" : "Armored" }
{ "title" : "Bornova Bornova" }
{ "title" : "Coco avant Chanel" }
{ "title" : "Nefes: Vatan sa?olsun" }
{ "title" : "Up" }
{ "title" : "Whiteout" }
{ "title" : "The Time Travelers Wife" }
{ "title" : "Whatever Works" }
{ "title" : "Anonyma - Eine Frau in Berlin" }
{ "title" : "Weather Girl" }
>

Az önce eklediğimiz filmi silelim:
> db.movies1.remove( {"imdb": "tt2179136"})
WriteResult({ "nRemoved" : 1 })
> db.movies1.find( {"imdb": "tt2179136"})

world veri tabanındaki countries1 torbasındaki tüm Asya kıtasında yer alan ülkelerin nüfuslarını bir arttıralım:
> use world
switched to db world
> db.countries1.find({"continent": "Asia"}).forEach(
... function(country){
... db.countries1.findAndModify({
...    query:{"_id" : country._id },
...    update:{
...   $inc:{population: 1}
...    }
... });
... });
>

Torbalar Üzerinde Sorgu Çalıştırmak
MongoDB'yi yine JS kullanarak sorgulayacağız. Bunun için hem imdb hem de world veri tabanları üzerinde bir dizi sorgu problemini sırayla çalışacağız.

1. Tüm kıtaların listesi:
> use world
switched to db world
> db.countries1.distinct('continent')
[
        "North America",
        "Asia",
        "Africa",
        "Europe",
        "Oceania",
        "South America"
]

2. Toplam kıta sayısı:
> use world
switched to db world
> db.countries1.distinct('continent').length
6

3. Her bir kıtadaki ülke sayısı:
> use world
switched to db world
> db.countries1.group(
...   {
...    "key": { "continent": 1}, initial: {count :0},
...    "reduce": function(c,h){ h.count = h.count +1;}
...   }
... );
[
        {
                "continent" : "North America",
                "count" : 37
        },
        {
                "continent" : "Asia",
                "count" : 51
        },
        {
                "continent" : "Africa",
                "count" : 58
        },
        {
                "continent" : "Europe",
                "count" : 46
        },
        {
                "continent" : "South America",
                "count" : 14
        },
        {
                "continent" : "Oceania",
                "count" : 28
        },
        {
                "continent" : "Antarctica",
                "count" : 5
        }
]

4. Her bir kıtadaki ülkelerin listesi:
> use world
switched to db world
> db.countries1.group(
... {
... "key": { "continent": 1},
... "initial": {
...                "countries" : []
...   },
... "reduce": function(country,doc){ doc.countries.push(country.name);}
... }
... );
[
        {
                "continent" : "North America",
                "countries" : [
                        "Aruba",
                        "Anguilla",
                        "Netherlands Antilles",
                        "Antigua and Barbuda",
                        "Bahamas",
                        "Belize",
                        "Bermuda",
                        "Barbados",
                        "Canada",
                        "Costa Rica",
                        "Cuba",
                        "Cayman Islands",
                        "Dominica",
                        "Dominican Republic",
                        "Guadeloupe",
                        "Grenada",
                        "Greenland",
                        "Guatemala",
                        "Honduras",
                        "Haiti",
                        "Jamaica",
                        "Saint Kitts and Nevis",
                        "Saint Lucia",
                        "Mexico",
                        "Montserrat",
                        "Martinique",
                        "Nicaragua",
                        "Panama",
                        "Puerto Rico",
                        "El Salvador",
                        "Saint Pierre and Miquelon",
                        "Turks and Caicos Islands",
                        "Trinidad and Tobago",
                        "United States",
                        "Saint Vincent and the Grenadines",
                        "Virgin Islands, British",
                        "Virgin Islands, U.S."
                ]
        },
        {
                "continent" : "Asia",
                "countries" : [
                        "Afghanistan",
                        "United Arab Emirates",
                        "Armenia",
                        "Azerbaijan",
                        "Bangladesh",
                        "Bahrain",
                        "Brunei",
                        "Bhutan",
                        "China",
                        "Cyprus",
                        "Georgia",
                        "Hong Kong",
                        "Indonesia",
                        "India",
                        "Iran",
                        "Iraq",
                        "Israel",
                        "Jordan",
                        "Japan",
                        "Kazakstan",
                        "Kyrgyzstan",
                        "Cambodia",
                        "South Korea",
                        "Kuwait",
                        "Laos",
                        "Lebanon",
                        "Sri Lanka",
                        "Macao",
                        "Maldives",
                        "Myanmar",
                        "Mongolia",
                        "Malaysia",
                        "Nepal",
                        "Oman",
                        "Pakistan",
                        "Philippines",
                        "North Korea",
                        "Palestine",
                        "Qatar",
                        "Saudi Arabia",
                        "Singapore",
                        "Syria",
                        "Thailand",
                        "Tajikistan",
                        "Turkmenistan",
                        "East Timor",
                        "Turkey",
                        "Taiwan",
                        "Uzbekistan",
                        "Vietnam",
                        "Yemen"
                ]
        },
        {
                "continent" : "Africa",
                "countries" : [
                        "Angola",
                        "Burundi",
                        "Benin",
                        "Burkina Faso",
                        "Botswana",
                        "Central African Republic",
                        "Côte d’Ivoire",
                        "Cameroon",
                        "Congo, The Democratic Republic of the",
                        "Congo",
                        "Comoros",
                        "Cape Verde",
                        "Djibouti",
                        "Algeria",
                        "Egypt",
                        "Eritrea",
                        "Western Sahara",
                        "Ethiopia",
                        "Gabon",
                        "Ghana",
                        "Guinea",
                        "Gambia",
                        "Guinea-Bissau",
                        "Equatorial Guinea",
                        "British Indian Ocean Territory",
                        "Kenya",
                        "Liberia",
                        "Libyan Arab Jamahiriya",
                        "Lesotho",
                        "Morocco",
                        "Madagascar",
                        "Mali",
                        "Mozambique",
                        "Mauritania",
                        "Mauritius",
                        "Malawi",
                        "Mayotte",
                        "Namibia",
                        "Niger",
                        "Nigeria",
                        "Réunion",
                        "Rwanda",
                        "Sudan",
                        "Senegal",
                        "Saint Helena",
                        "Sierra Leone",
                        "Somalia",
                        "Sao Tome and Principe",
                        "Swaziland",
                        "Seychelles",
                        "Chad",
                        "Togo",
                        "Tunisia",
                        "Tanzania",
                        "Uganda",
                        "South Africa",
                        "Zambia",
                        "Zimbabwe"
                ]
        },
        {
                "continent" : "Europe",
                "countries" : [
                        "Albania",
                        "Andorra",
                        "Austria",
                        "Belgium",
                        "Bulgaria",
                        "Bosnia and Herzegovina",
                        "Belarus",
                        "Switzerland",
                        "Czech Republic",
                        "Germany",
                        "Denmark",
                        "Spain",
                        "Estonia",
                        "Finland",
                        "France",
                        "Faroe Islands",
                        "United Kingdom",
                        "Gibraltar",
                        "Greece",
                        "Croatia",
                        "Hungary",
                        "Ireland",
                        "Iceland",
                        "Italy",
                        "Liechtenstein",
                        "Lithuania",
                        "Luxembourg",
                        "Latvia",
                        "Monaco",
                        "Moldova",
                        "Macedonia",
                        "Malta",
                        "Netherlands",
                        "Norway",
                        "Poland",
                        "Portugal",
                        "Romania",
                        "Russian Federation",
                        "Svalbard and Jan Mayen",
                        "San Marino",
                        "Slovakia",
                        "Slovenia",
                        "Sweden",
                        "Ukraine",
                        "Holy See (Vatican City State)",
                        "Yugoslavia"
                ]
        },
        {
                "continent" : "South America",
                "countries" : [
                        "Argentina",
                        "Bolivia",
                        "Brazil",
                        "Chile",
                        "Colombia",
                        "Ecuador",
                        "Falkland Islands",
                        "French Guiana",
                        "Guyana",
                        "Peru",
                        "Paraguay",
                        "Suriname",
                        "Uruguay",
                        "Venezuela"
                ]
        },
        {
                "continent" : "Oceania",
                "countries" : [
                        "American Samoa",
                        "Australia",
                        "Cocos (Keeling) Islands",
                        "Cook Islands",
                        "Christmas Island",
                        "Fiji Islands",
                        "Micronesia, Federated States of",
                        "Guam",
                        "Kiribati",
                        "Marshall Islands",
                        "Northern Mariana Islands",
                        "New Caledonia",
                        "Norfolk Island",
                        "Niue",
                        "Nauru",
                        "New Zealand",
                        "Pitcairn",
                        "Palau",
                        "Papua New Guinea",
                        "French Polynesia",
                        "Solomon Islands",
                        "Tokelau",
                        "Tonga",
                        "Tuvalu",
                        "United States Minor Outlying Islands",
                        "Vanuatu",
                        "Wallis and Futuna",
                        "Samoa"
                ]
        },
        {
                "continent" : "Antarctica",
                "countries" : [
                        "Antarctica",
                        "French Southern territories",
                        "Bouvet Island",
                        "Heard Island and McDonald Islands",
                        "South Georgia and the South Sandwich Islands"
                ]
        }
]
>

5. 70'li yılların filmlerine, yıllara göre sıralı olarak erişmek:
> use imdb
switched to db imdb
> db.movies1.find(
... {
... 'year' : {$gte : 1970, $lt: 1980 }
... },
... {
... "title": 1, "year":2 , "_id": 0
... }
... ).sort({"year": 1})
{ "title" : "Le magnifique", "year" : 1973 }
{ "title" : "Dog Day Afternoon", "year" : 1975 }
{ "title" : "Network", "year" : 1976 }
{ "title" : "The Little Girl Who Lives Down the Lane", "year" : 1976 }
{ "title" : "Der amerikanische Freund", "year" : 1977 }
{ "title" : "The Last Wave", "year" : 1977 }

6. Birden fazla yönetmeni olan filmlerin listesi:
> use imdb
switched to db imdb
> db.movies1.find( { $where: "this.directors.length > 1" } , {"title": 1, "_id":0 })
{ "title" : "Gamer" }
{ "title" : "A Serious Man" }
{ "title" : "Rembetiko" }
{ "title" : "New York, I Love You" }
{ "title" : "Daybreakers" }
{ "title" : "The Imaginarium of Doctor Parnassus" }
{ "title" : "Cloudy with a Chance of Meatballs" }
{ "title" : "The Princess and the Frog" }
{ "title" : "Karamazovi" }
{ "title" : "The Missing Person" }
{ "title" : "Sonbahar" }
{ "title" : "I Love You Phillip Morris" }
{ "title" : "The Book of Eli" }
>

7. Nufus yoğunluğu düşük olan ülkelerin listesi:
> use world
switched to db world
> db.countries1.find(
... {
...           $and : [
...                { "population": { $lt: 1000000 } } ,
...                { "surfaceArea": { $gt: 100000 } }
...           ]
... } ,
...         {
...            "name": true, "population": true, "surfaceArea": true, "_id":false
...         }
... )
{ "name" : "Western Sahara", "population" : 293000, "surfaceArea" : 266012 }
{ "name" : "Greenland", "population" : 56000, "surfaceArea" : 2166102 }
{ "name" : "Guyana", "population" : 861000, "surfaceArea" : 214981 }
{ "name" : "Iceland", "population" : 279000, "surfaceArea" : 103012 }
{ "name" : "Suriname", "population" : 417000, "surfaceArea" : 163277 }
>

8. 5 karakterli olan ülke adları:
> use world
switched to db world
> db.countries1.find(
...  { name : /^....$/},
...  { name: 1 , _id: 0}
... )
{ "name" : "Cuba" }
{ "name" : "Guam" }
{ "name" : "Iran" }
{ "name" : "Iraq" }
{ "name" : "Laos" }
{ "name" : "Mali" }
{ "name" : "Niue" }
{ "name" : "Oman" }
{ "name" : "Peru" }
{ "name" : "Chad" }
{ "name" : "Togo" }

9. 70'li yıllara ait Drama türündeki filmlere, film adına göre sıralı olarak erişmek:
> use imdb
switched to db imdb
> db.movies1.find(
... {
... $and : [
... { 'year' : {$gte : 1970, $lt: 1980 } },
... { 'genres.name': { $in: [ 'Drama' ] } }
... ]
... },
... {
... "title": 1, "year":2 , "_id": 0
... }
... ).sort({"title": 1})
{ "title" : "Der amerikanische Freund", "year" : 1977 }
{ "title" : "Dog Day Afternoon", "year" : 1975 }
{ "title" : "Network", "year" : 1976 }
{ "title" : "The Last Wave", "year" : 1977 }
{ "title" : "The Little Girl Who Lives Down the Lane", "year" : 1976 } 

10. Her bir film türünden kaç tane film olduğu bilgisine ulaşmak:
> db.movies1.aggregate(
...         { $project: { "genres": 1} },
...         { $unwind: "$genres" },
...         { $group: { _id: "$genres.name" , total: { $sum: 1 } } }
... );
{ "_id" : "Documentary", "total" : 1 }
{ "_id" : "Western", "total" : 7 }
{ "_id" : "Fantasy", "total" : 14 }
{ "_id" : "Sport", "total" : 7 }
{ "_id" : "Crime", "total" : 41 }
{ "_id" : "Family", "total" : 12 }
{ "_id" : "History", "total" : 12 }
{ "_id" : "Biography", "total" : 33 }
{ "_id" : "Adventure", "total" : 26 }
{ "_id" : "Action", "total" : 36 }
{ "_id" : "Animation", "total" : 9 }
{ "_id" : "Film-Noir", "total" : 2 }
{ "_id" : "Mystery", "total" : 33 }
{ "_id" : "Musical", "total" : 2 }
{ "_id" : "War", "total" : 18 }
{ "_id" : "Horror", "total" : 19 }
{ "_id" : "Romance", "total" : 56 }
{ "_id" : "Music", "total" : 7 }
{ "_id" : "Thriller", "total" : 70 }
{ "_id" : "Drama", "total" : 193 }
{ "_id" : "Sci-Fi", "total" : 15 }
{ "_id" : "Comedy", "total" : 65 }

Torbaları İndekslemek

Sistem tüm torbaları _id alanları üzerinden indeksler. İndekslemek erişim başarımı için önemlidir. Eğer sorgularda sıkça kullanılan alanlar var ise bu alanları da indekslemek uygun olur. Şimdi indekslenmemiş bir alan üzerinde bir sorgu çalıştırmanın maliyetini görelim:
> db.movies1.find({"year": 2010}).explain();
{
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 18,
        "nscannedObjects" : 251,
        "nscanned" : 251,
        "nscannedObjectsAllPlans" : 251,
        "nscannedAllPlans" : 251,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 1,
        "nChunkSkips" : 0,
        "millis" : 4,
        "server" : "omegacw:27017",
        "filterSet" : false
}

Sunucunun, bu sorguda tüm torbayı taraması gerekti. Şimdi year alanı üzerine bir indeks yaratalım:
> db.movies1.ensureIndex({ year: 1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

Yarattığımız indeksin etkisini, explain() çağrısı ile görmeye çalışalım:
> db.movies1.find({"year": 2010}).explain();
{
        "cursor" : "BtreeCursor year_1",
        "isMultiKey" : false,
        "n" : 18,
        "nscannedObjects" : 18,
        "nscanned" : 18,
        "nscannedObjectsAllPlans" : 18,
        "nscannedAllPlans" : 18,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "year" : [
                        [
                                2010,
                                2010
                        ]
                ]
        },
        "server" : "omegacw:27017",
        "filterSet" : false
}

İndekslemediğimizde toplam 251 dokümanı da taraması gerekmişti. İndeksledikten sonra ise olması gerektiği gibi sadece 18 tane dokümana erişti. Sorgu sonucunun 18 filmden oluştuğuna dikkat edin:
> db.movies1.find({"year": 2010}).length()
18

MongoDB ile ilgili daha detaylı bilgi edinmek için "Front-end Development and MongoDB" ve "Building Scalable Web Applications using Node.js, MondoDB, and Knockout.js" eğitimlerinin içeriklerini inceleyebilirsiniz.