I´ve been trying to get multiple averages grouping by an identifier using the Spring boot Aggregation. The fields whose average I want are defined by an Array given by parameter, but I can´t retrieve more than one average. The data is stored like :
{
"_id": {
"$oid": "XX"
},
"space_id": "AA",
"temperature": 50,
"temperatureUoMCode": 0,
"pm25": 50,
"pm25UoMCode": 0,
"co2": 0.0005,
"co2UoMCode": 0,
"co": 1,
"coUoMCode": 0,
"gas": 50,
"gasUoMCode": 0,
"humidity": 50,
"humidityUoMCode": 0,
"occupants": 6,
"maxCapacity": 15,
"_class": "Record"
}
The code that I am using:
List<AggregationOperation> aggregationOperations = new ArrayList<>();
aggregationOperations.add(Aggregation.match(
Criteria.where("space_id").is(space_id)
));
for(Variable var : concerns){
System.out.println(var.getField());
aggregationOperations.add(Aggregation.group(
space_id).
avg(var.getField()).as(var.getAvg()));
}
Aggregation aggregation = Aggregation.newAggregation(
aggregationOperations
);
AggregationResults<HashMap> results = mongoTemplate.aggregate(
aggregation, "records",HashMap.class);
Also, Variable class is an enum with two fields, the name of the field in the db (field) and the average alias (avg). When I run it I get the following output:
co
pm25
temperature
humidity
gas
2021-09-22 11:34:17.046 ERROR 42971 --- [a1-0b2af6e69097] TConfig$$EnhancerBySpringCGLIB$$673c396b : Invalid reference 'pm25'!
java.lang.IllegalArgumentException: Invalid reference 'pm25'!
at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:114)
at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:86)
at org.springframework.data.mongodb.core.aggregation.GroupOperation$Operation.getValue(GroupOperation.java:529)
at org.springframework.data.mongodb.core.aggregation.GroupOperation$Operation.toDocument(GroupOperation.java:507)
at org.springframework.data.mongodb.core.aggregation.GroupOperation.toDocument(GroupOperation.java:441)
at org.springframework.data.mongodb.core.aggregation.AggregationOperation.toPipelineStages(AggregationOperation.java:55)
at org.springframework.data.mongodb.core.aggregation.AggregationOperationRenderer.toDocument(AggregationOperationRenderer.java:56)
at org.springframework.data.mongodb.core.aggregation.AggregationPipeline.toDocuments(AggregationPipeline.java:81)
at org.springframework.data.mongodb.core.aggregation.Aggregation.toPipeline(Aggregation.java:716)
at org.springframework.data.mongodb.core.AggregationUtil.createPipeline(AggregationUtil.java:112)
at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2144)
at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2119)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2113)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2014)
at co.edu.javeriana.smartuj.EnvironmentalAlerts.services.AlertService.makeQuery(AlertService.java:131)
If someone is having a similar problem I managed to solve it by following this post's answer. I realized that instead of creating various GroupOperations, I could just create a single one and use it to store all the operations by saving the return of the "as" function in the same object:
MatchOperation matchOperation = Aggregation.match(Criteria.where("space_id").is(space_id));
GroupOperation groupOperation = null;
for(Variable var : concerns){
if (groupOperation == null){
groupOperation = Aggregation.group("space_id")
.avg(var.getField())
.as(var.getAvg());
}else
groupOperation = groupOperation
.avg(var.getField())
.as(var.getAvg());
}
Aggregation aggregation = Aggregation.newAggregation(
matchOperation,
groupOperation
);
AggregationResults<HashMap> results = mongoTemplate.aggregate(
aggregation, "records",HashMap.class);
return results.getUniqueMappedResult();