IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

Effiziente Bereichs-Queries mit CouchDB/GeoCouch

Im Vergleich zu klassischen SQL-Datenbank erfordern NoSQL-Datenbank vor allem bei der Datenabfrage ein Umdenken. Im Falle von CouchDB lässt sich zwar mit View Collation schon einiges erreichen, allerdings bei weitem nicht alles. Auf eine solche Grenze bin ich gestoßen, als ich Zeiträume speichern und abfragen wollte, also Einträge die ein Start- und Enddatum besitzen. Anfragen auf diese Daten könnten nun zu einem fixen Zeitpunkt alle darin ablaufenden Einträge erfragen, oder ausgeweitet auf einen Zeitraum auflisten, welche Einträge innerhalb eines Zeitfensters liegen. All dies ist mit CouchDB nicht wirklich lösbar.

Einen kleinen Workaround bietet die Idee, die Zeitleiste zu segmentieren und immer dann für einen Eintrag einen Key zu emitten, wenn der Zeitraum des Eintrages innerhalb dieses Bereichs liegt. Eine solche Map-Funktion könnte wie folgt aussehen. Hierbei wird für einen Eintrag jeweils ab dem Beginn für alle 5 Minuten ein Schlüssel in den Index emittiert.

Dokumentaufbau:

{
   "_id": "s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de",
   "begin": "2010-08-05T09:11:52.156Z",
   "end": "2010-08-05T09:23:13.457Z"
}

Map-Funktion:

//length of time segment (here 5 min)
var periodLength = (60*5);

function(doc)
{
        if(doc.begin && doc.end)
        {
                //start and end time as UNIX timestamps (seconds, not milliseconds)
                var begin =  Math.round(new Date(doc.begin).getTime()/1000);
                var end =  Math.round(new Date(doc.end).getTime()/1000);

                //calculate first matching segment of period
                var p = (begin - (begin%periodLength));

                //emit key for each matching period
                while(p<=end)
                {
                        emit([p, doc._id], null);
                        p = p + periodLength;
                }
        }
}

Der resultierende View sieht dann in etwa so aus:

Oder als Abfrage:
http://localhost:5984/entries/_design/entries/_view/docsByPeriodList?startkey=[1280998800]&endkey=[1281000193,{}]

{"total_rows":3,"update_seq":2,"offset":0,"rows":[
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1280999400,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"]15,"value":null},
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1280999700,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"],"value":null},
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","key":[1281000000,"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de"],"value":null}
]}

Eine viel bessere Lösung bietet jedoch die CouchDB-Erweiterung GeoCouch. Dank des R-Trees lassen sich Bereichtsabfragen effizient durchführen. Da GeoCouch eigentlich für zweidimensionale Geokoordinaten gedacht, muss sie und die GeoJSON-Syntax etwas missbraucht werden. Anstatt einer WGS84-Koordinate emittieren wir einfach einen UNIX-Zeitstempel und lassen den anderen Grad leer:

function(doc)
{
        if(doc.begin && doc.end)
        {
                //start and end time as UNIX timestamps (seconds, not milliseconds)
                var begin =  Math.round(new Date(doc.begin).getTime()/1000);
                var end =  Math.round(new Date(doc.end).getTime()/1000);

                emit(
                        {
                                type: "Point",
                                bbox : [0,start,0,end]
                        }, null
                );
        }
}

So lassen sich nun effiziente Bereichsabfragen durchführen:
http://localhost:5984/entries/_design/entries/_spatial/entriesByPeriod?bbox=0,1280998800,0,1281000600

{"update_seq":16,"rows":[
{"id":"s-ffc0b6b0-59d4-4a3b-ad36-7ec05e7db1de","bbox":[0,1280999512,0,1281000193],"value":null}
]}

Abfrage von auftretenden Jahrestagen (wie Geburtstagen) mit MySQL

Viele Datenbanken mit Personendaten enthalten Geburtsdaten. So ist eine typische Abfrage das Ermitteln von Personen, die in den nächsten Tagen Geburtstag haben oder in den vergangenen Tagen hatten. Eine solche Abfrage gestaltet sich aus verschiedenen Gründen komplizierter. Einerseits müssen alle Eigenheiten bei der Kalkulation mit Daten beachtet werden (z.B. Schaltjahre), andererseits möchte man ungern die kompletten Daten aus der Datenbank ziehen und dann erst dann mit einer weiteren Programmiersprache verarbeiten und berechnen, sondern direkt die Schnelligkeit und Funktionalität der Datenbank nutzen. Aus diesem Grund möchte ich hier meinen Versuch vorstellen, dieses Problem mit MySQL zu lösen. Ziel war es, eine Ergebnis zu bekommen, das auflistet, welche Personen innerhalb eines definierbaren Zeitraums Geburtstag hatten oder noch Geburtstag haben und wie alt die Person jeweils geworden ist oder wird. Das Feld ‘birthday’ soll hierbei ein Datenfeld vom Typ Date sein. Hier werden alle Geburtstage zwischen vergangener Woche und den kommenden zwei Wochen chronologisch aufgelistet:

SELECT
`name`,
`birthday`,
@nextBirthday := `birthday` + INTERVAL (YEAR(CURRENT_DATE) - YEAR(`birthday`) + IF(DATE_FORMAT(CURRENT_DATE, "%m%d") > DATE_FORMAT(`birthday`, "%m%d"), 1, 0)) YEAR,
@prevBirthday := @next - INTERVAL 1 YEAR,
@daysNext := DATEDIFF(@nextBirthday, CURRENT_DATE),
@daysPrev := DATEDIFF(@prevBirthday, CURRENT_DATE),
IF(ABS(@daysPrev) < @daysNext,@daysPrev +0 ,@daysNext+0) AS `days`,
IF(ABS(@daysPrev) < @daysNext,YEAR( @prevBirthday ) - YEAR( `birthday` ),YEAR( @nextBirthday ) - YEAR( `birthday` )) AS `age`
FROM
`table`
HAVING
`days` BETWEEN -7 AND 14
ORDER BY
`days` ASC

Die Grundidee ist relativ einfach. Es wird mithilfe von MySQL-Datumsfunktionen berechnet, wann die Person das nächste Mal Geburtstag hat und wann sie das letzte mal hatte. Nun wird geprüft, welches Ereignis aktueller ist und entsprechend die Werte ‘days’ und ‘age’ belegt. Mithilfe der HAVING-Klausel lässt sich außerdem die Ergebnismenge eingeschränken auf einen bestimmten Zeitraum.

Originalartikel: Abfrage von auftretenden Jahrestagen (wie Geburtstagen) mit MySQL

ioexception.de

Benjamin Erb studiert seit 2006 Medieninformatik und interessiert sich insbesondere für Java, Web-Technologien, Ubiquitous Computing, Cloud Computing, verteilte Systeme und Informationsdesign.


Raimar Wagner studiert seit 2005 Informatik mit Anwendungsfach Medizin und interessiert sich für C++ stl, boost & Qt Programmierung, Scientific Visualization, Computer Vision und parallele Rechenkonzepte.


David Langer studiert seit 2006 Medieninformatik und interessiert sich für Web-Entwicklung, jQuery, Business Process Management und Java.


Sebastian Schimmel studiert seit 2006 Informatik mit Anwendungsfach Medizin und interessiert sich für hardwarenahe Aspekte, Robotik, webOs, C/C++ und UNIX/Linux.


Timo Müller studiert seit 2006 Medieninformatik. Er interessiert sich allen voran für Mobile and Ubiquitous Computing, systemnahe Enwticklung und verteilte Systeme, sowie Computer Vision.


Achim Strauß studiert seit 2006 Medieninformatik. Seine Interessen liegen in Themen der Mensch-Computer Interaktion sowie Webentwicklung und UNIX/Linux.


Tobias Schlecht studiert seit 2006 Medieninformatik und interessiert sich vor allem für Software Engineering, Model Driven Development, Model Driven Architecture, Requirements Engineering, Web-Technologien, UML2 und Java.


Fabian Groh studiert seit 2006 Medieninformatik. Seine Interessengebiete sind Computer Graphics, Computer Vision, Computational Photography sowie Ubiquitos Computing.

Archiv

September 2010
M D M D F S S
« Aug    
 12345
6789101112
13141516171819
20212223242526
27282930