15.02.2013 Aufrufe

b2aat6n

b2aat6n

b2aat6n

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

Blocks geschlossen wird, muss das tatsächliche<br />

Lesen der Daten innerhalb der Session<br />

passieren. Lässt man das ToArray()<br />

weg, findet das Lesen erst beim Iterieren<br />

durch das Ergebnis statt, dann allerdings<br />

zu einem Zeitpunkt, da die Session bereits<br />

geschlossen ist. RavenDB verwendet für<br />

die Indizierung übrigens Lucene, was<br />

ebenfalls ein Open-Source-Projekt ist [5].<br />

Listing 9 zeigt, wie Sie die so erstellte Methode<br />

zum Ermitteln aller Produkte einer<br />

Kategorie gegen eine Datenbank testen<br />

können.<br />

Auch hier gilt es, noch einen weiteren<br />

Stolperstein zu beachten. RavenDB führt<br />

das Aktualisieren der Indizes im Hintergrund<br />

aus. Es kann daher sein, dass der Index<br />

zum Zeitpunkt des Lesevorgangs noch<br />

nicht aktualisiert ist. Zu Testzwecken kann<br />

man es bei Anwendung der Query mit der<br />

Methode WaitForNonStaleResults() erzwingen,<br />

dass auf die Aktualisierung des<br />

Index gewartet wird, bevor Ergebnisse geliefert<br />

werden. In einer Produktivumgebung<br />

sollten Sie diese Option allerdings<br />

nicht verwenden, da sie mit Performance-<br />

Listing 10<br />

Map und Reduce verwenden.<br />

store.DatabaseCommands.PutIndex(<br />

"ProduktKategorien",<br />

new IndexDefinition {<br />

Map = produkte => from produkt in produkte<br />

select new { produkt.Kategorie, Anzahl = 1 },<br />

Reduce = results => from result in results<br />

group result by result.Kategorie<br />

into g<br />

select new { Kategorie = g.Key, Anzahl = g.Sum(x => x.Anzahl) }<br />

});<br />

Listing 11<br />

Den Index testen.<br />

einbußen verbunden ist. An dieser Stelle<br />

zeigt es sich, dass Dokumentdatenbanken<br />

einen anderen Schwerpunkt setzen als relationale<br />

Datenbanken. Bei einer relationalen<br />

Datenbank geht die Konsistenz der Daten<br />

immer vor. Bei Dokumentdatenbanken<br />

wie RavenDB dagegen steht die Konsistenz<br />

hinter Skalierbarkeit und Ausfallsicherheit<br />

zurück.<br />

Map/Reduce<br />

Als Nächstes habe ich mir ein Feature angeschaut,<br />

das bei Dokumentdatenbanken<br />

sehr häufig zum Einsatz kommt und großen<br />

Einfluss auf die Skalierbarkeit hat:<br />

Map/Reduce. Eine Map-Funktion wurde<br />

bereits für den Index benötigt, mit dem alle<br />

Produkte einer Kategorie ermittelt werden<br />

können. Fügt man einem solchen Index<br />

noch eine Reduce-Methode hinzu,<br />

können Daten aus den Dokumenten aggregiert<br />

werden. Damit ist es möglich, beispielsweise<br />

einen Index zu erstellen, der<br />

aus den Produkten alle Kategorien ermittelt.<br />

Zusätzlich ist es durch die Aggregation<br />

möglich zu zählen, wie viele Produkte in<br />

[Test]<br />

public void Alle_Kategorien_ermitteln() {<br />

sut.Save(new Produkt {Id = "#1", Name = "iPad", Kategorie = "Elektronik"});<br />

sut.Save(new Produkt {Id = "#2", Name = "iPod", Kategorie = "Elektronik"});<br />

sut.Save(new Produkt {Id = "#3", Name = "Apfel", Kategorie = "Obst"});<br />

sut.Save(new Produkt {Id = "#4", Name = "Kartoffel", Kategorie = "Gemüse"});<br />

sut.Save(new Produkt {Id = "#5", Name = "Banane", Kategorie = "Obst"});<br />

var result = sut.Kategorien();<br />

Assert.That(result.ToArray(), Is.EquivalentTo(new[] {<br />

new KategorieMitAnzahl { Kategorie = "Elektronik", Anzahl = 2},<br />

new KategorieMitAnzahl { Kategorie = "Obst", Anzahl = 2},<br />

new KategorieMitAnzahl { Kategorie = "Gemüse", Anzahl = 1},<br />

}));<br />

}<br />

LÖSUNG<br />

der jeweiligen Kategorie enthalten sind.<br />

Das Erstellen dieses Index gleicht dem vorhergehenden<br />

Beispiel. Der wesentliche<br />

Unterschied besteht in der zusätzlichen<br />

Reduce-LINQ-Query. Dabei liegt das<br />

Hauptproblem darin, dass man darauf<br />

achten muss, dass Map- und Reduce-Query<br />

auf gleich aufgebauten Objekten arbeiten.<br />

Da hier anonyme Typen verwendet<br />

werden, ist das Unterfangen fehleranfällig,<br />

siehe Listing 10.<br />

Die Map-Query liefert für jedes Produkt<br />

ein Objekt zurück. Dieses Objekt hat zwei<br />

Eigenschaften: Kategorie und Anzahl. Die<br />

Anzahl ist immer 1, dies ist der Startwert<br />

für die spätere Aufsummierung. In der Reduce-Query<br />

wird nun auf Objekten vom<br />

Typ KategorieMitAnzahl gearbeitet. Der<br />

Typ muss als zweiter generischer Typparameter<br />

im Konstruktor von IndexDefinition<br />

angegeben werden.<br />

Die Herausforderung liegt darin, dass<br />

man in den beiden LINQ-Queries anonyme<br />

Typen verwenden muss. Wenn diese<br />

Queries Ergebnisse vom Typ KategorieMit-<br />

Anzahl liefern, erhält man eine Fehlermeldung<br />

vom RavenDB-Server, die besagt,<br />

man müsse anonyme Typen verwenden.<br />

Dennoch müssen diese anonymen Typen<br />

den gleichen Aufbau haben wie der definierte<br />

Typ. Wenn man diese Hürde genommen<br />

hat, steht einem Test des Index nichts<br />

mehr im Weg, wie Listing 11 zeigt.<br />

Das Schöne an diesem Index ist, dass RavenDB<br />

ihn jeweils auf dem aktuellen Stand<br />

hält. Jede Änderung an Dokumenten, die<br />

im Index verwendet werden, führt zu einem<br />

entsprechenden Update des Index.<br />

www.dotnetpro.de dotnetpro.dojos.2011 37<br />

Fazit<br />

Ich habe mir in diesem Spike die Funktionalität<br />

von RavenDB nur ausschnittweise<br />

angesehen. RavenDB hat darüber hinaus<br />

noch mehr zu bieten wie etwa die Verteilung<br />

einer Datenbank auf mehrere Server.<br />

Die ersten Schritte gingen zügig voran.<br />

Aber beim Map/Reduce hat es doch etwas<br />

länger gedauert, bis ich die zu berücksichtigenden<br />

Konventionen alle zusammenhatte.<br />

Hier wäre ein Beispiel in der Dokumentation<br />

sicherlich hilfreich. Gelernt habe<br />

ich wieder einiges, und das war schließlich<br />

Zweck der Übung. [ml]<br />

[1] http://clean-code-developer.de<br />

[2] http://ravendb.net/<br />

[3] http://builds.hibernatingrhinos.com/builds/<br />

ravendb<br />

[4] http://de.wikipedia.org/wiki/JSON<br />

[5] http://lucene.apache.org/lucene.net/

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!