b2aat6n
b2aat6n
b2aat6n
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/