Tengo la siguiente consulta que funciona bien cuando se ejecuta desde el shell de MongoDB:
db.feedback.aggregate([ { $group: { _id: { year: { $year: '$receivedDate' }, month: { $month: '$receivedDate' } }, count: { $sum: 1 } } } ])Devuelve fechas (año, mes) y su suma.
Sin embargo, cuando ejecuto la misma consulta desde MongoTemplate de Spring, obtengo la fecha y la suma siempre es nula:
private static final String QUERY_STATS_BY_MONTH = "{ aggregate: 'feedback', pipeline: ([ { $group:{ _id: { year: { $year: '$receivedDate' }, month: { $month: '$receivedDate' } }, count: { $sum: 1 } } } ])"; .... CommandResult commandResult = mongoTemplate.executeCommand(QUERY_STATS_BY_DAY); BasicDBList dbList = (BasicDBList) commandResult.get(QUERY_RESULT); Iterator<Object> it = dbList.iterator(); List<FeedbackDateAggregation> res = new ArrayList(); while (it.hasNext()) { DBObject dbObject = (DBObject) it.next(); res.add(mongoTemplate.getConverter().read(FeedbackDateAggregation.class, dbObject)); } return res;Mis clases de dominio son:
public class FeedbackDateAggregation { private StatsDate id; private Long count; public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } public StatsDate getId() { return id; } public void setId(StatsDate id) { this.id = id; } }y:
public class StatsDate { private int year; private int month; private int day; private int week; public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public int getWeek() { return week; } public void setWeek(int week) { this.week = week; } }Se recupera StatsDate, pero el recuento siempre es nulo.
Esta es la forma idiomática de hacer consultas de agregación usando una plantilla Spring. No necesita acceder a la API mongo CommandResult de bajo nivel, solo necesita un uso avanzado.
Hace uso de AggregationOperation ya que el operador $year y $month aún no se admiten de forma predeterminada en la fase de grupos de primavera.
AggregationOperation group = new AggregationOperation() { @Override public DBObject toDBObject(AggregationOperationContext context) { return new BasicDBObject( "$group", new BasicDBObject( "_id", new BasicDBObject( "year", new BasicDBObject("$year", "$receivedDate")).append( "month", new BasicDBObject("$month", "$receivedDate"))).append( "count", new BasicDBObject("$sum", 1))); } }; newAggregation es el método auxiliar estático que toma una lista de etapas de canalización.
Aggregation aggregation = newAggregation(group);Finalmente, el método agregado de la plantilla mongo para ejecutar la consulta.
List<FeedbackDateAggregation> res = mongoTemplate.aggregate(aggregation, "feedback", FeedbackDateAggregation.class).getMappedResults();Consulta generada:
{ "aggregate": "__collection__", "pipeline": [{ "$group": { "_id": { "year": { "$year": "$receivedDate" }, "month": { "$month": "$receivedDate" } }, "count": { "$sum": 1 } } }] }Puede intentar proyectar los campos que desea usar como el grupo por claves primero usando andExpression y Expresión en la operación de proyección y luego agrupar por los nuevos campos en la operación de grupo:
Aggregation agg = newAggregation( project("id") .andExpression("month(receivedDate)").as("month") .andExpression("year(receivedDate)").as("year"), group("year", "month").count().as("count") ); AggregationResults<FeedbackDateAggregation> results = mongoTemplate.aggregate( agg, "feedback", FeedbackDateAggregation.class ); List<FeedbackDateAggregation> res = results.getMappedResults(); En lo anterior, se crea una nueva agregación a través del método de fábrica estático newAggregation al que pasa una lista de operaciones de agregación. Estas operaciones agregadas definen la canalización de agregación de su Agregación.
En el primer paso, proyecta los campos de la colección de entrada utilizando andExpression y Expresión con la operación de proyecto.
En el segundo paso, utiliza la operación de grupo para definir un grupo para cada valor de "year" y "month" para el que agrega el recuento de ocurrencias a través del operador de agregación count() y recopila el resultado en un nuevo campo llamado "count" .
Finalmente, llame al método added aggregate() en MongoTemplate para permitir que MongoDB realice la operación de agregación real con la agregación creada como argumento.
El problema fue que usé una versión antigua de Fongo (mongo falso para pruebas). Una vez que lo probé en un MongoDB real, funcionó de maravilla. Cuando actualicé mi versión de Fongo, también funcionó. También tuve que cambiar mis objetos de dominio. Ahora tengo un objeto de dominio que incluye día, semana, mes, año, recuento.