Search code examples
spring-bootaggregation-frameworkspring-data-mongodb

Dynamically multiple averages in one Aggregation Spring Boot


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)

Solution

  • 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();