2014-10-16 9 views
8

ich eine Produktsammlung mit folgenden Unterlagen haben:MongoDB aggreagte Füllung fehlt Tage

{ "_id" : 1, "item" : "abc", created: ISODate("2014-10-01T08:12:00Z") } 
{ "_id" : 2, "item" : "jkl", created: ISODate("2014-10-02T09:13:00Z") } 
{ "_id" : 3, "item" : "hjk", created: ISODate("2014-10-02T09:18:00Z") } 
{ "_id" : 4, "item" : "sdf", created: ISODate("2014-10-07T09:14:00Z") } 
{ "_id" : 5, "item" : "xyz", created: ISODate("2014-10-15T09:15:00Z") } 
{ "_id" : 6, "item" : "iop", created: ISODate("2014-10-16T09:15:00Z") } 

Ich möchte für Tag ein Diagramm beschreibt die Anzahl der Produkte ziehen, so dass ich mongodb Aggregation Rahmen zur Produktgruppe von Tag zählen:

var proj1 = { 
     "$project": { 
      "created": 1, 
      "_id": 0, 
      "h": {"$hour": "$created"}, 
      "m": {"$minute": "$created"}, 
      "s": {"$second": "$created"}, 
      "ml": {"$millisecond": "$created"} 
     } 
    }; 

    var proj2 = { 
     "$project": { 
      "created": { 
       "$subtract": [ 
        "$created", { 
         "$add": [ 
          "$ml", 
          {"$multiply": ["$s", 1000]}, 
          {"$multiply": ["$m", 60, 1000]}, 
          {"$multiply": ["$h", 60, 60, 1000]} 
         ] 
        }] 
      } 
     } 
    }; 

    db.product.aggregate([ 
      proj1, 
      proj2, 
      {$group: { 
       _id: "$created", 
       count: {$sum: 1} 
      }}, 
      {$sort: {_id: 1}} 
     ]) 

Das Ergebnis in mongo Shell ist:

{ 
    "result" : [ 
     { 
      "_id" : ISODate("2014-10-01T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-10-02T00:00:00.000Z"), 
      "count" : 2 
     }, 
     { 
      "_id" : ISODate("2014-10-07T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-10-15T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-10-16T00:00:00.000Z"), 
      "count" : 1 
     } 
    ], 
    "ok" : 1 
} 

natürlich gibt es keine Produkt einige Tage und die Darstellung des Ergebnisses oben gesetzt verwenden sieht wie folgt aus:

enter image description here

Aber das gewünschte Diagramm sollte wie folgt aussehen:

desired output

Die Frage ist also: Wie kann Ich füge fehlende Ergebnisse (von den letzten 30 Tagen zum Beispiel) dem Ergebnis mit count = 0 hinzu? Das heißt, sieht das gewünschte Ergebnis Satz sollte wie folgt aus:

{ 
    "result" : [ 
     { 
      "_id" : ISODate("2014-09-16T00:00:00.000Z"), 
      "count" : 0 
     }, 
     { 
      "_id" : ISODate("2014-09-17T00:00:00.000Z"), 
      "count" : 0 
     }, 
     ...    
     { 
      "_id" : ISODate("2014-10-01T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-10-02T00:00:00.000Z"), 
      "count" : 2 
     }, 
     { 
      "_id" : ISODate("2014-10-03T00:00:00.000Z"), 
      "count" : 0 
     }, 
     ... 
     { 
      "_id" : ISODate("2014-10-07T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-09-08T00:00:00.000Z"), 
      "count" : 0 
     }, 
     ... 
     { 
      "_id" : ISODate("2014-10-15T00:00:00.000Z"), 
      "count" : 1 
     }, 
     { 
      "_id" : ISODate("2014-10-16T00:00:00.000Z"), 
      "count" : 1 
     }, 
     // also, add some extra days 
     { 
      "_id" : ISODate("2014-10-17T00:00:00.000Z"), 
      "count" : 0 
     }, 
     { 
      "_id" : ISODate("2014-10-10T00:00:00.000Z"), 
      "count" : 0 
     } 
    ], 
    "ok" : 1 
} 

Antwort

6

Verwenden Aggregat, um diese Frage vollständig zu behandeln, ist ein Schmerz.
Aber es kann erreicht werden.
(MongoDB V2.6 + erforderlich)

var proj1 = { 
    "$project" : { 
     "created" : 1, 
     "_id" : 0, 
     "h" : { 
      "$hour" : "$created" 
     }, 
     "m" : { 
      "$minute" : "$created" 
     }, 
     "s" : { 
      "$second" : "$created" 
     }, 
     "ml" : { 
      "$millisecond" : "$created" 
     } 
    } 
}; 

var proj2 = { 
    "$project" : { 
     "created" : { 
      "$subtract" : [ "$created", { 
       "$add" : [ "$ml", { 
        "$multiply" : [ "$s", 1000 ] 
       }, { 
        "$multiply" : [ "$m", 60, 1000 ] 
       }, { 
        "$multiply" : [ "$h", 60, 60, 1000 ] 
       } ] 
      } ] 
     } 
    } 
}; 

var group1 = { 
     $group : { 
      _id : "$created", 
      count : { 
       $sum : 1 
      } 
     } 
    }; 

var group2 = { 
     $group : { 
      _id : 0, 
      origin : { 
       $push : "$$ROOT" 
      }, 
      maxDate : { 
       $max : "$_id" 
      } 
     } 
}; 

var step = 24 * 60 * 60 * 1000; // milliseconds of one day 

var project3 = { 
    $project : { 
     origin : 1, 
     extents : { 
      $map : { 
       "input" : [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29], 
       "as" : "e", 
       "in" : { 
        _id : { 
         $subtract : [ "$maxDate", { 
          $multiply : [ step, "$$e"] 
         }] 
        }, 
        count : { 
         $add : [0] 
        } 
       } 
      } 
     } 
    } 
}; 

var project4 = { 
     $project : { 
      _id : 0, 
      values : { 
       $setUnion : [ "$origin", "$extents"] 
      } 
     } 
}; 

var unwind1 = { 
     $unwind : "$values" 
}; 

var group3 = { 
     $group : { 
      _id : "$values._id", 
      count : { 
       $max : "$values.count" 
      } 
     } 
}; 

db.product.aggregate([ proj1, proj2, group1, group2, project3, project4, 
     unwind1, group3, { 
      $sort : { 
       _id : 1 
      } 
     } ]); 

Ich möchte den fehlenden Teil bei Anwendung Ende so etwas für Ihre Referenz füllen:

function sortResult(x, y) { 
    var t1 = x._id.getTime(); 
    var t2 = y._id.getTime(); 
    if (t1 < t2) { 
     return -1; 
    } else if (t1 == t2) { 
     return 0; 
    } else { 
     return 1; 
    } 
} 

var result = db.product.aggregate(); 

var endDateMilliseconds = result[result.length - 1]._id.getTime(); 
var step = 24 * 60 * 60 * 1000; // milliseconds of one day 

var map = {}; 
for (var i in result) { 
    map[ result[i]._id.getTime() ] = result[i]; 
} 

for (var ms = endDateMilliseconds, x = 1; x < 30; x++) { 
    ms -= step; 
    if (! (ms in map)) { 
     map[ms] = {_id : new Date(ms), count : 0}; 
    } 
} 

var finalResult = []; 
for (var x in map) { 
    finalResult.push(map[x]); 
} 
finalResult.sort(sortResult); 
printjson(finalResult); 
+1

Diese Frage könnte besser erklärt werden. Während ich die Logik in jedem der ersten und zweiten Codeabschnitte sehe, wären viele Menschen verwirrt, wie diese zusammen funktionieren. –

2

Ok, in erster Linie: Nicht vorhandene Werte zu null ausgewertet werden (grob übersetzt „nada“, „nichts“, „nicht da“), die ungleich 0 ist, was ein wohldefinierter Wert ist.

MongoDB hat zum Beispiel kein semantisches Verständnis des Unterschieds zwischen 0 und 42. Wie sollte MongoDB entscheiden, welchen Wert man für einen Tag in der Zeit annehmen soll (von welchem ​​Mongo auch kein semantisches Verständnis hat)?

Grundsätzlich haben Sie zwei Möglichkeiten: Speichern Sie eine 0 für jeden Tag, wenn kein Wert aufgezeichnet werden soll oder Sie in Ihrer App über die Tage in der Zeit, für die Sie ein Diagramm erstellen möchten, ausgeben und 0 für jeden Tag keinen Wert ausgeben existiert als Ersatz. Id 'suggerieren, erstere zu tun, da dies die Verwendung des Aggregations-Frameworks ermöglichen würde.

+0

Die "Null" ist semantisch mehr wie ein [SQL] (https://en.wikipedia.org/wiki/Null_ (SQL)) oder [JSON] (http://json.org/) 'null' als ein Zeiger' NULL.' – Soren

+0

Which beide sind serialisierte Darstellungen von was? ;) –

+0

Von nichts - sie sind null und nicht serialisierte Werte von irgendetwas, aber Werte selbst außerhalb des normalen Wertebereichs - es gibt einen subtilen semantischen Unterschied. – Soren