5

Um direkt zu sein, wie kann ich das tun:Aggregation Projektgruppe extrahierte Tag, Monat und Jahr mit Spring Data

group._id = { 
    year: { $year : [{ $subtract: [ "$timestamp", 25200000 ]}] }, 
    month: { $month : [{ $subtract: [ "$timestamp", 25200000 ]}] }, 
    day: { $dayOfMonth : [{ $subtract: [ "$timestamp", 25200000 ]}] } 
}; 

mit Federdaten

ich dies bereits versucht, und einige andere Formen und waren erfolgreiche nicht

Aggregation aggregation = Aggregation.newAggregation(
       Aggregation.match(c), 
       Aggregation.project("programa", "custo", "duracao", "dataHora") 
        .andExpression("dataHora").minus(25200000).extractDayOfMonth().as("dia") 
        .andExpression("dataHora").minus(25200000).extractMonth().as("mes") 
        .andExpression("dataHora").minus(25200000).extractYear().as("ano"), 
       Aggregation.group("programa", "ano", "mes", "dia") 
        .count().as("count") 
        .sum("custo").as("valorTotal") 
        .sum("duracao").as("duracaoTotal") 
        .first("dataHora").as("dataHora"), 
       Aggregation.sort(Direction.ASC, "dataHora") 
     ); 

ich von Tag, Monat und Jahr in mongodb gruppieren müssen, sonst muss ich das alles gruppierten Daten in Code konvertieren.

Vielen Dank im Voraus

Antwort

5

Sie sind in eine Begrenzung der Feder Mongo läuft in dem, was Sie für Feldberechnungen in einem einzigen $project Bühne tun können, und in der Tat werden Sie wahrscheinlich bereits als separate $project schreiben, da Sie, dass Sie entdecken Namhafte Felder können jetzt auch nicht direkt in einem $group_id projiziert werden.

Sie wären also besser dran, alles in der $group zu halten, sowie eine andere Methode zum Runden Ihrer angepassten Daten auf Ortszeit verwenden.

Der bessere Weg Ihr schreiben $group daher wäre:

{ "$group": { 
    "_id": { 
    "programa": "$programa", 
    "dataHora": { 
     "$add": [ 
     { "$subtract": [ 
      { "$subtract": [{ "$subtract": ["$dataHora", new Date(0)] }, 25200000 ] }, 
      { "$mod": [ 
      { "$subtract": [{ "$subtract": ["$dataHora", new Date(0)] }, 25200000 ] }, 
      1000 * 60 * 60 * 24 
      ]} 
     ]}, 
     new Date(0) 
     ] 
    } 
    }, 
    "count": { "$sum": 1 }, 
    "valorTotal": { "$sum": "$custo" }, 
    "duracaoTotal": { "$sum": "$duracao" }, 
    "dataHora": { "$first": "$dataHora" } 
}} 

Natürlich, diese Art von Struktur zu verwenden, mit Feder mongo Sie Implementierung der Aggregationsstufe Operation eine benutzerdefinierte müssen die DBObject eine definierte nehmen :

public class CustomGroupOperation implements AggregationOperation { 
    private DBObject operation; 

    public CustomGroupOperation (DBObject operation) { 
     this.operation = operation; 
    } 

    @Override 
    public DBObject toDBObject(AggregationOperationContext context) { 
     return context.getMappedObject(operation); 
    } 
} 

, die Sie dann in Zusammenhang wie folgt verwenden:

Aggregation aggregation = Aggregation.newAggregation(
      Aggregation.match(c), 
      new CustomGroupOperation(
       new BasicDBObject("$group", 
        new BasicDBObject("_id", 
         new BasicDBObject("programa","$programa") 
          .append("dataHora", 
           new BasicDBObject("$add",Arrays.asList(
            new BasicDBObject("$subtract",Arrays.asList(
             new BasicDBObject("$subtract",Arrays.asList(
              new BasicDBObject("$subtract",Arrays.asList(
               "$dataHora", new Date(0) 
              )), 
              25200000 
             )), 
             new BasicDBObject("$mod",Arrays.asList(
              new BasicDBObject("$subtract",Arrays.asList(
               new BasicDBObject("$subtract",Arrays.asList(
                "$dataHora", new Date(0) 
               )), 
               25200000 
              )), 
              1000 * 60 * 60 * 24 
             )) 
            )), 
            new Date(0) 
           )) 
          ) 
        ) 
        .append("count",new BasicDBObject("$sum",1)) 
        .append("valorTotal",new BasicDBObject("$sum","$custo")) 
        .append("duracaoTotal",new BasicDBObject("$sum","$duracao")) 
        .append("dataHora",new BasicDBObject("$first","$dataHora")) 
       ) 
      ), 
      Aggregation.sort(Direction.ASC,"_id.dataHora") 
    ); 

Da die benutzerdefinierte Klasse von derselben Basisklasse abstrahiert wird, die von den integrierten Hilfsmethoden verwendet wird, kann sie wie gezeigt neben ihnen verwendet werden.

Wie der grundlegende Prozess mit dem Datum Mathe funktioniert hier ist, dass wenn Sie $subtract ein BSON Date-Objekt aus einem anderen dann das Ergebnis ist die Millisekunden der Differenz, und in diesem Fall aus dem Epochendatum (Datum (0)) Extrahiert den Millisekundenwert. Dies ermöglicht Ihnen, die Berechnung auf den aktuellen Datumswert um den Modulo ($mod) von der Anzahl der Millisekunden an einem Tag zu runden.

Viel wie Sie ursprünglich versucht haben, wenn Sie dann $add dieser Millisekunde Wert zu einem BSON Date-Objekt der zurückgegebene Wert ist wieder ein BSON-Datum. Wenn Sie also ein Objekt, das epoch darstellt, hinzufügen, wird ein neues Date-Objekt zurückgegeben, das auf das aktuelle Datum gerundet wird.

Dies ist normalerweise viel nützlicher als das Extrahieren von Teilen über date aggregation operators und funktioniert auch etwas kürzer, besonders wenn man die Zeit von UTC anpasst, wie hier.

Obwohl der Bau des $group hier ist etwas kurz und bündig als die Helferfunktionen der Feder Mongo versuchen zu vermeiden, ist es viel effizienter am Ende als eine separate $project Stufe laufen die Feldwerte zu transformieren, dass Sie wirklich nur in der $group Bühne sowieso wollen.

+0

Vielen Dank für die Antwort, und vielen Dank für die Zeit zu erklären, die Mongo-Einschränkung und Workaround! Ich werde diesen Ansatz versuchen – Thiesen

+0

Diese Antwort rockt! – nurgasemetey

Verwandte Themen