Spring data and MongoDB aggregation (GroupBy)
Rédigé par gorki Aucun commentaireProblem :
I try to create an aggregation (count and groupBy) on my documents.
Thanks to these links :
I nearly resolve my problem... nearly :)
Solution :
Documents to analyze :
- the _id of the application is defined by an id attribute...
- instance is not present is all documents
{
phase : "Production",
application {
_id : 1234
},
instance: "myInstance"
}
The first try is to use springdata easy way : there is not groupBy ! but count is working if you need :
# If instance is set to null, returns the document which do not have the field
countByApplicationIdAndPhaseAndInstanceId(Long applicationId, Phase phase, String instance)
# Another way to test instance presence
countByApplicationIdAndPhaseAndInstanceIdExists(Long applicationId, Phase phase, Boolean exists)
So, thanks to baeldung (many thanks) ! Here is a working solution, take care about :
- filtering order ! the Match operation works either on the document, either on the result of the group by ! (see Baeldung example for testing result of the group by)
#Filter on document
Aggregation aggregation = newAggregation(filterStates, agg);
#Filter on result of the aggregation
Aggregation aggregation = newAggregation(agg, filterStates);
- my applicationId as used in Springdata easy way must be written with Mongo ID : application._id
- and application_id could not be present in the group clause as it is filtered (but as it takes me 2 long hours to make it works....)
- Don't forget @Id on the result bean. I see somewhere that we can execute the generic command in Mongo and do not take care about the result bean.
- The @Id must be on the first item of the group
- Do not miss the collection name in the aggregation command (here MONGO_EVALUATION_COLLECTION_NAME)
@Service
public class EvaluationAdditionalRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(EvaluationAdditionalRepository.class);
@Autowired
private MongoTemplate mongoTemplate;
public List<InstanceCount> getInstanceByApplicationAndPhase(Long applicationId) {
GroupOperation agg = group("application._id", "instanceId", "phase").count().as("countInstance");
MatchOperation filterStates = match(new Criteria("application._id").is(applicationId));
Aggregation aggregation = newAggregation(filterStates, agg);
AggregationResults<InstanceCount> result = mongoTemplate.aggregate(aggregation, MONGO_EVALUATION_COLLECTION_NAME, InstanceCount.class);
return result.getMappedResults();
}
}
And the result bean :
import org.springframework.data.annotation.Id;
public class InstanceCount {
@Id
private Long applicationId;
// Enum are authorized
private Phase phase;
private long countInstance;
private String instanceId;
public Phase getPhase() {
return phase;
}
public void setPhase(Phase phase) {
this.phase = phase;
}
public long getCountInstance() {
return countInstance;
}
public void setCountInstance(long countInstance) {
this.countInstance = countInstance;
}
public Long getApplicationId() {
return applicationId;
}
public void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
@Override
public String toString() {
return "InstanceCount{" +
", applicationId='" + applicationId + '\'' +
", phase='" + phase + '\'' +
", instanceId='" + instanceId + '\'' +
", countInstance=" + countInstance +
'}';
}
}