2016-04-19 7 views
4

Ich habe eine Tabelle, und speichern Sie wie folgt vor:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : [ { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } ] } 
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : [ { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } ] } 
{ "_id" : ObjectId("5716617f4af77ca97a9614bf"), "count" : 3, "author" : "Mary", "music" : [ ] } 

Ich hoffe, dass die Anzahl der Datensatz zu finden, dass „$ count“> „$ music.count“. Aber wenn ich {$ abwickeln: "$ music"} mache, bekomme ich folgendes:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } } 
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } } 

Der dritte Datensatz verschwindet. Wie kann ich das Ergebnis zu erhalten wie:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } } 
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } } 
{ "_id" : ObjectId("5716617f4af77ca97a9614bf"), "count" : 3, "author" : "Mary", "music" : {"count": 0} } 

Die ersten Aufzeichnungen von $ loopup bekam sind, die Gesamt Code ist wie folgt:

db.bookAuthors.aggregate([{ 
$lookup:{from:"musicAuthors", localField:"author", foreignField:"author",as:"music"}}, 
{$unwind:"$music"}, 
{$project:{_id:"$author",count:1,music:1}}, 
{$match:{$gt:["$count","$music.count"]}}, 
{$group:{_id:null,count:{$sum:1}}} 
]) 

Wie kann ich die Anzahl der Datensatz zu finden tun " $ count ">" $ music.count "? In diesem Beispiel sollte das Ergebnis 2 sein. Aber jetzt, wegen des Problems beim Abwickeln, bekomme ich 1. Danke.

Antwort

12

In MongoDB 3.2 (die Sie verwenden, wenn Sie $lookup haben) die $unwind Operator hat die preserveNullAndEmptyArrays Option. Dadurch ändert sich das Verhalten „nicht“ das Dokument aus den Ergebnissen entfernen, wo das Array ist in der Tat „leer“:

db.bookAuthors.aggregate([ 
    { "$lookup":{ 
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author", 
    "as": "music" 
    }}, 
    { "$unwind": { "path": "$music", "preserveNullAndEmptyArrays": true }, 
    { "$project": { 
     "count": 1, 
     "author": 1, 
     "music": { 
     "$ifNull": [ "$music", { "$literal": { "count": 0 } }] }, 
     } 
    }} 
]) 

Und die $ifNull ersetzt den fehlenden Wert in diesem Fall.

Aber eigentlich da Ihre hier Vereinigung 1:1 dann könnte man verzichten nur die $unwind zusammen, und einfach das leere Array ersetzen:

db.bookAuthors.aggregate([ 
    { "$lookup":{ 
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author", 
    "as": "music" 
    }}, 
    { "$project": { 
    "count": 1, 
    "author": 1, 
    "music": { 
     "$ifNull": [ 
     { "$arrayElemAt": [ "$music", 0 ] }, 
     { "$literal": { "count": 0 } } 
     ] 
    } 
    }} 
]) 

Und wenn $arrayElemAt nichts an dem Index 0 gefunden (also „leer“) dann würde der $ifNull den alternativen Wert genauso wie vorher zurückgeben. Natürlich, wo es etwas gefunden hat, wird dieser Wert stattdessen zurückgegeben.

Aber wieder hat Ihr spezifisches Problem noch eine bessere Lösung, die wiederum $unwind nicht benötigt. Da Sie nur berechnen können die „count“ Bedingung „in-line“ mit dem Array:

db.bookAuthors.aggregate([ 
    { "$lookup":{ 
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author", 
    "as": "music" 
    }}, 
    { "$group": { 
    "_id": null, 
    "count": { 
     "$sum": { 
     "$cond": { 
      "if": { 
      "$gt": [ 
       "$count", 
       { "$sum": { 
       "$map": { 
        "input": "$music", 
        "as": "el", 
        "in": "$$el.count" 
       } 
       }} 
      ] 
      }, 
      "then": 1, 
      "else": 0 
     } 
     } 
    } 
    }} 
]) 

Hier ist der $sum Operator in beide es verwendet wird, sind Fälle Gebrauch, wie es in "traditioneller„Akkumulator“und neue Rolle ist Summieren von "Werten in einem Array. Der Operator $map betrachtet jedes Array-Element und gibt den Wert $sum zurück, um eine Summe zu erzeugen. Ein "leeres" Array würde als 0 zurückgeben.

Dann gibt es den $cond Vergleich, um festzustellen, ob die zurückgegebene Summe aus dem Array weniger als die "count" Eigenschaft auf dem Dokument war. Wobei true eine 1 für den Akkumulator zurückgegeben wird und false0.

Das Endergebnis ist natürlich 2, da sowohl die "ersten" als auch die "dritten" Dokumente tatsächlich dem Zustand innerhalb des Akkumulators entsprechen. Das ist also der effizienteste Weg, auch wenn es dabei etwas "langatmig" aussieht.