CouchDB Multi-Key Query

06. Januar 2012
Festlegung
 
Als Erstes definiere ich hier - der besseren Übersicht halber - folgende Variable, die in den nachfolgenden Beispielen immer durch den zugewiesenen Wert ersetzt werden muss:
 
urlBase = http://localhost:5984/users/_design/userlist
 
Außerdem sollen alle Dokumente der Datenbank "users" (mindestens) die folgenden Eigenschaften besitzen:
 
{
    firstname: <VORNAME>,
    name: <NACHNAME>,
    city: <WOHNORT>
}
 
Die Eigenschaften _id und _rev verstehen sich von selbst (da diese immer angelegt werden müssen).
 
 
Problem
 
Das Problem, das mich heute beschäftigte war, wie man einer CouchDB-Anfrage mehrere Keys übergeben kann. Das ist von Haus aus gar nicht so einfach möglich. Es existiert zwar der Parameter 'key', den man auf Views anwenden kann z.B. derart:
 
urlBase/_view/meinView?key=["Matthias","Sonnenkalb", "Halle"]
 
Wie man sieht, ist so ein Key ganz schön flexibel. Hier ist er ein Array, er kann aber auch ein JSON-Objekt sein oder nur ein einzelner Wert. Nur eines kann man eben nicht, man kann keinen Key definieren, der "Lücken" hat. Etwa so (der Nachname soll hier nicht relevant sein):
 
urlBase/_view/meinView?key=["Matthias","", "Halle"]
 
Ich muss also immer den "vollständigen" Key an den View übergeben, wenn man - für die gerade genannten Beispiele - den folgenden View annimmt:
 
"views": {
    "meinView": {
        "map": "function( doc ){
            emit( [doc.firstname, doc.name, doc.city], doc._id );
         }"
    }
}
 
Aber was, wenn ich nicht immer alle Key-Werte benötige bzw. flexibel sein muss? So hat man z.B. bei vielen Suchmasken oft mehr als einen Parameter, nach denen man die Daten durchsuchen lassen kann (z.B. die erweiterte Buchsuch bei Amazon). Dann könnte man für die hier genannten drei Eigenschaften (Vorname,Nachname und Wohnort) noch die entsprechenden Views anlegen. Aber bei mehreren Eigenschaften artet das sehr schnell aus.
 
 
Lösung (?)
 
Eine mögliche Lösung könnte nun wie folgt aussehen.
 
"views": {
    "meinView": {
        "map": "function( doc ){
            emit( doc, doc );
         }"
    }
},
"lists": {
    "meineListe": "function( head, req ) {
        var row;
        var queryParams = JSON.stringify( req.query );
        start({'headers': {'content-type': 'text/html; charset=utf-8'}});
        while( row = getRow()){
        if( queryParams != '{}' && ( row.key.name == req.query.name ||
            row.key.firstname ==  req.query.firstname )){
            send(  row.key.firstname + ' ' + row.key.name +
                       ' , aus ' + row.key.city + '<br />' );
        }else if( queryParams == '{}' ){
           send( row.key.firstname + ' ' + row.key.name +
                     ' , aus ' + row.key.city + '<br />' );
        }
    }
}"
 
Ich habe hier einfach einen View definiert, der das gesamte Dokument als Key (und als Value) zurück gibt. Sowie eine Liste, die auf diesem View arbeitet (die Ergebnisse des Views darstellt). Und genau in dieser Listen-Funktion werte ich meine "eigenen Keys" aus. Der Aufruf sieht nun z.B. wie folgt aus:
 
1.)    urlBase/_list/meinListe/meinView?name=Sonnenkalb
2.)    urlBase/_list/meinListe/meinView?name=Sonnenkalb&firstname=Eric 
3.)    urlBase/_list/meinListe/meinView
 
Bei 1.) werden alle User geliefert, deren Nachname exakt Sonnenkalb lautet.
Bei 2.) werden sowohl die User geliefert, deren Nachname exakt Sonnenkalb oder deren Vorname exakt Eric lautet.
Bei 3.) wurde kein Key übergeben und daher wird eben die gesamte Liste zurückgegeben.
 
 
Hinweise
 
Man darf den Key-Wert im Querystring nicht in Anführungszeichen setzen, da der Stringvergleich dann nicht wie folgt aussieht:
 
Sonnenkalb == Sonnenkalb
 
sondern so:
 
Sonnenkalb == "Sonnenkalb"
 
Weiterhin werden im Beispiel nur exakte Namen berücksichtigt. Namen mit Leerzeichen z.B. Sonnenkalb von und zu Halle liefern keinen Treffer. Dies kann man aber leicht per RegEx abfangen:
 
...
if( query != '{}' && (row.key.name.match( req.query.name ) ||
    row.key.firstname.match( req.query.firstname ))){
...
 
Das liefert dann auch alle User, deren Nachname z.B. das Wort "Sonne" enhält.
 
Kommentare, Ideen oder Vorschläge willkommen!
HILFE
Code-Beispiele werden mit den BBCodes [code][/code] dargestellt. Jedes Tag muss eine Zeile für sich allein haben, d.h der Beispiel-Code muss wirklich ZWISCHEN den Tags stehen. Beispiel:
[code] ACHTUNG! HIER UMBRUCH WICHTIG. TAG [code] MUSS ALLEIN STEHEN
#include <stdio.h>

int main( void ){
    return 0;
} ACHTUNG! HIER UMBRUCH WICHTIG. NACHFOLGENDES TAG [/code] MUSS ALLEIN STEHEN
[/code]

Alles was zwischen den beiden Tags [register] und [/register] eingetragen wird, ist nur für registrierte (und eingeloggte) User sichtbar.
Dein Name *
Deine Email
Deine Website
Vorschau