2012-03-28 11 views
1

Gegeben Ich habe folgende Produkte:CouchDB: Abfragen Many-To-Many "Beziehung"

{ 
    "_id": "2666df80782596200fca49557d757870", 
    "_rev": "3-99382057f6c484526835f1042753ccf2", 
    "type": "product", 
    "name": "Shirt", 
    "hersteller": "oska", 
    "price": 11.15 
} 

{ 
    "_id": "2666df80782596200fca49557d758e8c", 
    "_rev": "1-01cc88e69e5ff30f0d011fdf61fbedbc", 
    "type": "product", 
    "name": "Pullover", 
    "hersteller": "acme", 
    "price": 7.58 
} 

Und einen Auftrag

{ 
    "_id": "2666df80782596200fca49557d758228", 
    "_rev": "2-0b4b3a8605893b60c962b8ae78f0b775", 
    "type": "order", 
    "orderDate": "01.01.2012", 
    "positions": [ 
     "2666df80782596200fca49557d757870", 
     "2666df80782596200fca49557d758e8c" 
    ] 
} 

ich alle Aufträge mit _id, bestelldatum und Summe abfragen möchten aller Positionen Preise. Wie gehe ich das an?

Beispiel:

[ 
{ "_id": "2666df80782596200fca49557d758228", "orderDate": "01.01.2012", "total": 18.73 } 
] 

Edit:
Dieses Beispiel aufgebaut ist, und ich weiß, dass eine Gesamtbestellung sollte nicht die Preise für historische Aufträge ändern Produkt ändern. Es ist ein schlechtes Beispiel für etwas, wo ein echter "Join" notwendig wäre. Stellen Sie sich vor, ich muss die Summen ändern, wenn sich der Preis eines Produkts ändert. Daher möchte ich das Summenfeld nicht einfach denormalisieren. Es ist möglich, die Summen in den Bestellungen zu "reparieren", wenn sich ein Produkt ändert. Wenn ich jedoch viele verschiedene Dokumententypen habe, die auf das Produkt verweisen, muss ich sie mit einer bestimmten Funktion nach Typ ändern. Seans Antwort brachte mich auf den Gedanken, dass ich denormalisierte Felder mit einem Changes-Listener reparieren könnte, aber ich brauche einen Listener für jedes Mal, wenn ein Doc-Typ mit einem anderen verwandt ist - klingt nach viel Arbeit.

+0

Siehe http: // stackoverflow.com/questions/3033443/best-weg-zu-eins-zu-vielen-beitreten-in-couchdb –

+0

Hey Ryan, danke für den Kommentar. Ich wusste das schon vorher, ich glaube, ich habe irgendeinen SO-Thread zum Beitritt gelesen :-) Der eine, den du verlinktest, handelt von "One-to-Many-Joins", und ich möchte "viele-zu-viele" beitreten. Ich weiß, dass ich? Include_docs = true in aktuellen Couchdb-Versionen, die effektiv die verwandten Produkte für eine Bestellung abrufen, aber ich kann nicht auf die Ergebnisse reduzieren, um die Summe zu berechnen. Ich sehe keinen Grund, warum reduzieren würde hier nicht funktionieren. –

+0

OK, lesen http://stackoverflow.com/questions/3033443/best-way-to-do-one-to-many-join-in-couchdb wieder impliziert, es ist nicht eine "one-to-many beitreten" das ist besprochen. Ich denke, was ich vermisst habe, war die Show-Funktion hier. Ich verstehe nicht, warum ich noch nicht reduzieren kann :-) –

Antwort

1

Ich glaube, der beste Weg wäre, einen _changes-Listener zu haben, der die Bestelldokumente mit dem Preiswert jedes Produkts in Positionen aktualisiert, wenn der Datensatz historisch wird (dh nur lesen). Es scheint nur falsch, dass ein Preis für eine vergangene Bestellung ändern kann, und Sie können nicht einen korrekten Bericht/Rechnung ausdrucken, ohne durch die Kaninchenloch Preis Datumsbereiche gehen ....

Um das Problem zu lösen, wie Sie Wenn Sie einen clientseitigen Code haben, der mit JSON arbeiten kann, könnte der Trick, auf den Sie unter Linked Documents zeigen, hilfreich sein. Bei einer Kartenfunktion wie:

function(doc) { 
if(doc.type == 'order') { 
    for(var position in doc.positions) emit(doc._id,{_id: doc.positions[position]}); 
} 
} 

Sie verwenden können include_docs = true & key = "2666df80782596200fca49557d758228" zurückzukehren Ihr Beispiel, um mit Positionen einschließlich Preis?.

Leider können Sie include_docs nicht mit einem Reduzieren verwenden, daher könnte dies ein guter Ausgangspunkt für einen _changes Daemon sein, wie ich bereits erwähnt habe, um ein Auftragszusammenfassungsdokument zu erstellen, das wie eine endgültige Antwort aussieht ...


[bearbeiten hinzufügen Lösung] Nun, ich habe einen Weg gefunden, aber es ist nicht schön.

ein Design-Dokument mit dieser Karte ansehen Funktion Gegeben:

function(doc) { 
if(doc.type == 'order') { 
    for(var p in doc.positions) emit(doc.orderDate, {_id: doc.positions[p]}); 
} 
} 

und diese Liste Funktion:

function(head, req) { 
var count = 0; 
var dates = {}; 
var totals = {}; 

while(r = getRow()) { 
    if(!(r.id in dates)) { 
    count += 1; 
    dates[r.id] = r.key; 
    totals[r.id] = 0; 
    } 
    totals[r.id] += r.doc.price; 
} 
start({'headers': {'Content-Type': 'application/json'}}); 
send('[\n'); 
for(var order in dates) { 
    count -= 1; 
    send(JSON.stringify({'_id': order, 'orderDate': dates[order], 'total': totals[order]})); 
    send((count > 0)?',\n':'\n'); 
} 
send(']\n'); 
} 

ist Ihr gewünschtes Ergebnis bei

/db/_design/[Design Doc Name]/_list/[List Function Name]/[Map Function Name]?include_docs=true 

BTW, in diesem Fall , die Schlüsselwerte sind das orderDate ...

+0

Bist du dir sicher, dass deine emit diese Produkt-IDs bekommen? Weil Sie eine if -Anweisung haben, die Ihren Bereich auf Aufträge eingrenzt, und dann versuchen Sie, die IDs aus diesem Kreis zu emittieren. – anvarik

+0

Danke für Ihre ausführliche Antwort! Ich wusste, dass jemand sagen würde, dass die Preise der Bestellung historisch und read-only werden :-) Dieses Beispiel wurde erfunden und war ein schlechtes Beispiel für etwas, wo ein echter "Join" notwendig sein würde. Ein _changes-Listener ist jedoch möglicherweise eine gute Lösung für die Denormalisierung - wenn sich das ursprüngliche Dokument ändert, kann es alle Dokumente durchlaufen, in denen ich einen Wert denormalisiert und auch geändert habe. Die Lösung mit den verknüpften Dokumenten sieht gut aus - ich habe letzte Nacht nach dem Lesen von http://goo.gl/838Zj herumgespielt und etwas Ähnliches bekommen, aber hier nicht gepostet. werde deinen Code später versuchen –

+0

OK, ich kann bestätigen, dass es eine Lösung ist. Es ist nicht schön, dass die Listenfunktion für jede Anfrage aufgerufen wird. Wenn wir eine Lösung gefunden haben, die mit reduce arbeitet, würde das Ergebnis ("totals") im b-tree gespeichert werden. –

Verwandte Themen