Wednesday, April 8, 2015

Temel Tipler Üzerinde Karşılaştırma İşlemleri

Temel tiplerle çalışırken karşılaştırma işlemi yapmak genellikle kolaydır. Örneğin Java'da tam sayı tiplerin karşılaştırılmasında çok özel durumlar yaşanmaz. Sadece sınır değerlerin aşılmamasına dikkat edilmelidir:
byte max= 127;
max++;
System.err.println(max==128);
byte tipinde bir değişken [-128..127] aralığında değer alır. max değişkeni alabileceği en büyük değeri almışken değerini bir artırırsanız, yeni değeri -128 olacaktır! Şimdi [-128..127] aralığındaki tam sayıların toplamını elde etmek için aşağıdaki kodu yazmış olalım:
 int sum=0; 
 for (byte b = Byte.MIN_VALUE; b <= Byte.MAX_VALUE; b++) {
     sum += b;
 }
Aynı nedenden dolayı yukarıdaki kod sonsuz döngüye girecektir. Eğer elle hesaplamış olsaydık çoktan -128 değerini bulmuştuk! Şimdi ise aşağıdaki koda bir bakalım:
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
   if (b == 0x90)
      System.out.print("Yessss, I have found it!");
}
Döngüde byte tipinden bir değişkenin alabileceği tüm değerleri tarıyoruz. Her halde b'nin değerinin eninde sonunda -112 olacağını ve if koşulunun en az bir kez sağlanacağını düşünüyoruz. Ama öyle olmuyor. Bu kodu çalıştırdığınızda ekran boş kalıyor ve etrafı bir sessizlik kaplıyor. Sorun 0x90 sabitini bir sekizlik olarak yorumlayışımızdan kaynaklanıyor:
  • Tüm tam sayı tipleri (byte, short, int, long) işaretli tam sayıdır
  • En yüksek anlamlı bit işaret bitini gösterir
  • Negatif sayılar ikiye tümleyen aritmetiğine göre kodlanır. Buna göre bir sekizli olarak yorumlandığında 0x90 değeri iki tabanında 0b10010000 ve on tabanında - (0b01101111 + 0b00000001)=-112 değerine eşittir. Eğer yukarıdaki kodda gerçekten sekiz bitlik olarak tanımlamak istiyorsak karşılaştırma koşulunu ( b == (byte) 0x90 ) olarak değiştirmeliyiz. Okunurluğu arttırmak için ise  sabiti aşağıdaki gibi tanımlamak uygun olur:
private static final MASTER_ID = (byte) 0x90 ;
  • char tipi ise işaretsiz 16-bit tam sayı olarak ele alınır:
System.err.println((int)(char)(byte)(-1));
Yukarıdaki kod çalıştırılır ise ekranda 65535 değeri görülür. Nedeni char tipinin işaretsiz, int ve byte tam sayı tiplerinin ise işaretli olmasıdır.
Tam sayılar (byte, short, int) ile yapılan aritmetik işlemler (+,-,*,/) sonucu her zaman int tipindedir. Bu nedenle derleyeci aşağıda kod için hata verecektir:
short x= 42, 
      y= 108,
      z= 0;
z = x + y;
Hatanın açıklamasında, "Type mismatch: cannot convert from int to short" yazdığını göreceksiniz. x+y sonucu int tipinde ve bu tipte bir değeri short tipinde değişkene yazmak istiyoruz. Çözüm için ya tüm değişkenleri int tipinde tanımlayacağız ya da tip dönüşümü yapacağız:
short x= 42, 
      y= 108,
      z= 0;
z = (short) ( x + y ) ;
Bir yol daha var: += operatörünü kullanmak. Tüm kısayol operatörleri örtük bir tip dönüşümü barındırırlar. Bu nedenle aşağıdaki kod hatasız derlenir:
short x= 42, 
      y= 108,
      z= 0;
z += x + y; 
Tüm tam sayı sabitler int tipindedir ve 32 bittir. Sonuna l ya da L koyarsanız long sabit tanımlamış olursunuz. 1 ile karışmaması için ne yapın edin L kullanın. Bu durumda sabitin uzunluğu iki katına çıkar: 64-bit. En uzun tam sayı tipimiz long, yeterince uzun olsa da bir sınırı var: 9223372036854775807. Bu sınırı aşarsanız, hata yaparsınız ama hata yaptığınızı fark etmeyebilirsiniz. Daha uzun tam sayılar için java.math.BigInteger sınıfını kullanabilirsiniz. Bu iş için float ya da double kullanmayı düşünüyorsanız, yazının devamını okuyun, kararınızı gözden geçirmeniz gerekebilir! Bir de tam sayılarla çalışırken herhangi bir sayıyı sıfıra bölmeye çalışmayın, bölemezsiniz, böldürmezler! 
int inf= 1/0;
int one= 0/0; 
Yukarıdaki her iki bölme işlemi de RuntimeException fırlatır: java.lang.ArithmeticException: / by zero
Ancak kayan noktalı sayıları karşılaştırırken çok daha dikkatli olmak gerekir. Örneğin aşağıdaki kod parçasını inceleyelim:
double myMoney = 4.35;
System.err.println("I have worked hard!");
myMoney *= 100;
if (myMoney == 435.0){
   System.err.println("I have $ 435.00 now!");
}
Ne yazık ki if cümlesindeki karşılaştırma hiçbir zaman true olmaz. 4 ile 5 arasında, sayılamaz sonsuz sayıda sayı bulunur. Ama bizim bu sayılamaz sonsuz sayıda sayıyı kodlamak için sınırlı sayıda bitimiz var: float için 32-bit ve double için ise 64-bit. Kayan noktalı sayılar IEEE-754 standardına göre kodlanırlar. IEEE-754 gösterimi, küçük sayılar için küçük ve büyük sayılar için büyük hatalar yapılacak şekilde tasarlanmıştır. Bu kodlamada her kayan noktalı sayıyı gösteremiyoruz, 4.35'de bu tam olarak gösteremediğimiz sayılardan. Üstelik bu şekilde tam olarak gösteremediğimizden dolayı hata yaptığımız çok sayı var. Ne kadar? Sonsuz sayıda. Üstelik ne kadar hata yapılacağını da kontrol edemiyoruz. O halde kayan noktalı sayılarla hesaplama yapmak istiyorsanız ve noktadan sonra kaç basamak doğru hesaplamak istediğinizi kontrol etmek istiyorsanız float ya da double temel tiplerini kullanmamalısınız. Bu amaçla java.util.BigDecimal sınıfını kullanabilirsiniz:
BigDecimal myMoney = new BigDecimal("4.35");
myMoney.setScale(6);
System.err.println("I have worked hard!");
myMoney = myMoney.multiply(BigDecimal.valueOf(100.0));
if (myMoney.compareTo(BigDecimal.valueOf(435))==0){
 System.err.println("I have 435 TL now...");
}
Burada setScale metodunu kullanarak noktadan sonra kaçıncı basamağa kadar doğru hesaplamak istediğimizi belirtebiliyoruz. BigDecimal sınıfını kullanarak pi sayısını noktadan sonra örneğin 100.000 basamağa kadar hesaplayabiliriz:
import java.math.BigDecimal;
import static java.math.BigDecimal.*;

public class Pi {
 private static final BigDecimal TWO = new BigDecimal(2);
 private static final BigDecimal FOUR = new BigDecimal(4);
 private static final int LENGTH = 100_000;

 public static void main(String[] args) {

  BigDecimal p = compute(LENGTH);  
  for (int start = 0, stop = start + 1_000; 
stop <= LENGTH; 
start += 1_000, stop += 1_000) {
    System.err.println(p.toString().substring(start, stop));
  }
 }

 // Gauss-Legendre Algorithm
 public static BigDecimal compute(final int SCALE) {
  BigDecimal a = ONE;
  BigDecimal b = ONE.divide(sqrt(TWO, SCALE), SCALE, ROUND_HALF_EVEN);
  BigDecimal t = new BigDecimal("0.25");
  BigDecimal x = ONE;
  BigDecimal y;

  while (!a.equals(b)) {
    y = a;
    a = a.add(b).divide(TWO, SCALE, ROUND_HALF_UP);
    b = sqrt(b.multiply(y), SCALE);
    t = t.subtract(x.multiply(y.subtract(a).multiply(y.subtract(a))));
    x = x.multiply(TWO);
  }

  return a.add(b).multiply(a.add(b))
    .divide(t.multiply(FOUR), SCALE, ROUND_HALF_UP);
 }

 // square root method (Newton's method)
 public static BigDecimal sqrt(BigDecimal A, final int SCALE) {
  BigDecimal x0 = new BigDecimal("0");
  BigDecimal x1 = new BigDecimal(Math.sqrt(A.doubleValue()));

  while (!x0.equals(x1)) {
   x0 = x1;
   x1 = A.divide(x0, SCALE, ROUND_HALF_UP);
   x1 = x1.add(x0);
   x1 = x1.divide(TWO, SCALE, ROUND_HALF_UP);
  }

  return x1;
 }
}
Merak etmeyin, ekran çıktısını burada listelemeyeceğim. Çıktıyı buradan indirip inceleyebilirsiniz. Tekrar eden bir örüntü bulursanız haber verin! Kayan noktalı sayılarda büyük sayılarda daha büyük hatalar yapıyoruz. Bir milyar liranız var ve hesabınıza 50 TL yatırıyorsunuz:
float yourMoney = 1_000_000_000F;
yourMoney = yourMoney + 50;
NumberFormat nf = NumberFormat.getCurrencyInstance(
                                    new Locale("tr","TR"));
System.err.println("Your money is " + nf.format(yourMoney));
Bakalım bu işlem sonunda hesabınızda ne kadar para var? Sizi şaşıracağınız bir sonuç bekliyor:
Your money is 1.000.000.064,00 TL
Evet, banka için kötü bir haber, kasada açık var! Gerçekten, jUnit kullanarak birim testi yazıyorsanız, benzer şekilde hiçbir zaman kayan noktalı sayıların mutlak eşitliğini test etmemelisiniz. Bunun yerine sınayacağınız değişkenin beklenen değerinin, bir aralık içine düşmesi ile yetinmelisiniz:
public class VehicleTest {

 @Test
 public void addBox_overloaded() {
  Vehicle vehicle = new Vehicle(1_000, 1_500);
  assertFalse(vehicle.addBox(501));
  assertEquals(1_000, vehicle.getWeight(), 0.001);
 }

 @Test
 public void addBox_ok() throws Exception {
  Vehicle vehicle = new Vehicle(1_000, 1_500);
  assertTrue(vehicle.addBox(100));
  assertEquals(1_100, vehicle.getWeight(), 0.001);
 }
}
Kayan noktalı sayılarda bölme işlemi RuntimeException fırlatmaz:
double inf= 1./0;
double negative_inf= -1./0;
double nan= 0./0;
System.err.println(inf);
System.err.println(negative_inf);
System.err.println(nan);
Yukarıdaki kodu çalıştırırsanız aşağıdaki ekran görüntüsü oluşur:
Infinity
-Infinity
NaN
IEEE-754'e göre sonsuz 0x7FF0000000000000, eksi sonsuz 0xFFF0000000000000 ve belirsizlik 0x7FF8000000000000 olarak kodlanır. 
Tam sayılarla çalışırken özel bir durum olmasa da bunların sarmal sınıfları ile çalışırken dikkatli olmamazı gerektiren bir durum var:
Integer lostIsland= 108;
Integer helixIsland= 108;
System.err.println(lostIsland==helixIsland);
Integer perfectNumber= 496;
Integer nothingSpecial= 496;
System.err.println(perfectNumber==nothingSpecial);
Başlangıçta her iki mantıksal ifadenin de true ya da false değer almasını bekliyorsunuz. Ancak uygulamayı çalıştırdığınızda ilk ifadenin true ikinci ifadenin ise false değer ürettiğini göreceksiniz. Bu durumun açıklaması için Integer sınıfının kaynak kodunu başvurmalısınız. Sarmal sınıftan bir referans değişkene bir sabiti atarken, derleyici gerçekte bu atamayı valueOf metod çağrısı üzerinden gerçekleştirir. valueOf metodu size bir ipucu verecektir. Burada Integer sınıfının bir sekizliğe sığan tam sayıları [-128..127] cepte tuttuğunu göreceksiniz:
public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
Bu nedenle karşılaştırmanın her durumda doğru sonuçlanmasını istiyorsanız, referansları değil içeriği karşılaştırın. İçeriği ise her durumda equals metodunu kullanarak karşılaştırabilirsiniz.
Integer lostIsland= 108;
Integer helixIsland= 108;
System.err.println(lostIsland.equals(helixIsland));
Integer perfectNumber= 496;
Integer nothingSpecial= 496;
System.err.println(perfectNumber.equals(nothingSpecial));
Şimdi çıktı aşağıdaki gibi olacaktır:
true
true
Benzer durumla String sınıfında da karşılaşıyoruz. String aslında temel bir tip değil. Ancak onu temel tipler ile birlikte incelememizi gerektirecek ve diğer sınıflarda olmayan bir kullanımı var. Java programlama dilinde, bir sınıftan nesne yaratmak istediğinizde, new operatörünü kullanmanız gerekir. Ancak String ile çalışırken iki farklı şekilde tanımlama yapabiliyoruz:
String jack1 = "Jack";
String jack2 = new String("Jack");
İlk kullanımda, String nesnesi bir "String havuzunda" yaratılır. Eğer daha önce aynı karakter katarı yaratılmışsa, jack1 referansı bu nesneyi gösterir. İkinci kullanımda ise String nesnesi her zaman Heap'de yaratılır. Bu nedenle String referansları (jack1 ve jack2) karşılaştırılır ise false değer üretir:
String jack1 = "Jack";
String jack2 = new String("Jack");
String jack3 = "Jack";
System.out.println(jack1==jack2);
System.out.println(jack1==jack3);
System.out.println(jack1.equals(jack2));
System.out.println(jack1.equals(jack3));
Karşılaştırma equals çağrısı üzerinden yapılırsa her zaman doğru sonucu verir:
false
true
true
true
String sınıfında yer alan intern() metodu ile Heap'deki bir String nesnesini String havuzuna getirmek mümkündür:
String jack1 = "Jack";
String jack2 = new String("Jack");
jack2= jack2.intern();
System.out.println(jack1==jack2);
Şimdi karşılaştırma true değer üretecektir. 
String sınıfı değiştirilemez bir sınıftır. Metodlarının hepsi yeni bir String nesnesi döndürür, mevcut String nesnesinin karakterleri üzerinde bir değişikliğe neden olmazlar:
String name= "Jack";
name.toUpperCase();
if (name.equals("JACK")){
   System.err.println("Jack the Ripper");
} else {
   System.err.println("Jack Bauer");
}
Yukarıdaki kod çalıştırılır ise ekranda "Jack Bauer" iletisi görülür. name.toUpperCase() ifadesi yeni bir String nesnesi döndü "JACK", ama bu sonucu referans değişkeninde saklamadığımız için bu nesne çöp oldu. String sınıfı içindeki toUpperCase, toLowerCase gibi bazı metotlar dil bilgisini kullanırlar:
String name="ışiğöçü";
System.err.println(name);
System.err.println(name.toUpperCase(Locale.US));
System.err.println(name.toUpperCase(new Locale("tr","TR")));
Yukarıdaki kodu çalıştırdığımızda İngilizce ve Türkçe için toUpperCase() davranışını inceleyiniz:
ışiğöçü
IŞIĞÖÇÜ
IŞİĞÖÇÜ
Son olarak String karşılaştırmasını Collator sınıfını kullanarak nasıl kontrol edebildiğimizi görelim:
String basicRules=  "< a < b < c < ç < d < e < f < g < ğ < h "+
        "< ı < i < j < k < l < m < n < o < ö < p "+
       "< r < s < ş < t < u < ü < v < y < z ";
String trExpension= "& şi ; she & ş ; sch & s ; ş & u ; ü & i ; ı " + 
                    "& c ; ç & o ; ö & ğ ; g" ;
final Collator collator= new RuleBasedCollator(basicRules + trExpension);
Collator.getInstance(new Locale("tr","TR"));
collator.setStrength(Collator.PRIMARY);
String name1="şima";
String name2="sima";
System.err.println("name1="+name1);
System.err.println("name2="+name2);
System.err.println("name1.equals(name2): "+collator.equals(name1, name2));
name1="şule";
name2="schule";
System.err.println("name1="+name1);
System.err.println("name2="+name2);
System.err.println("name1.equals(name2): "+collator.equals(name1, name2));
name1="ışçöüğ";
name2="iscoug";
System.err.println("name1="+name1);
System.err.println("name2="+name2);
System.err.println("name1.equals(name2): "+collator.equals(name1, name2));
Burada "şima" ile "shema" ve "şule" ile "schule" karşılaştırmalarının doğru sonuç vermesi için String karşılaştırmasını yeniden kendi ihtiyaçlarımıza göre Collator sınıfı yardımı ile tanımlıyoruz. Buna göre g ile ğ, s ile ş gibi karşılaştırmaların eşit olmasını sağlıyoruz. Yukarıdaki kodu çalıştırdığımızda aşağıdaki ekran çıktısı üretilecektir:
name1=şima
name2=shema
name1.equals(name2): true
name1=şule
name2=schule
name1.equals(name2): true
name1=ışçöüğ
name2=iscoug
name1.equals(name2): true
Ayrıca sözlük sıralamasını yeniden tanımlanabildiğini görüyoruz:
< a < b < c < ç < d < e < f < g < ğ < h
< ı < i < j < k < l < m < n < o < ö < p
< r < s < ş < t < u < ü < v < y < z
Buna göre sıralamayı nasıl değiştirebildiğimizi aşağıdaki örnek uygulamadan görelim:
List<String> names= new ArrayList<>();
names.add("şule");
names.add("sema");
names.add("şima");
names.add("zehra");
names.add("ayşegül");
Collections.sort(names);
System.out.println(names);
Comparator<String> stringComparator= new Comparator<String>() {
 @Override
 public int compare(String o1, String o2) {
  return collator.compare(o1, o2);
 }
};
Collections.sort(names,stringComparator);
System.out.println(names);
Yukarıdaki kodu çalıştırdığımızda oluşan ekran görüntüsünü inceleyelim:
[ayşegül, sema, zehra, şima, şule]
[ayşegül, sema, şima, şule, zehra]

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.