Reputation: 12212
I defined a method to group data using the aggregation framework. The method accepts a list of fields on which you can group the data. I also created a class to map the result but the issue is that I don't know what type to give for the _id
attribute. The _id
can have different types depending on the number of fields used to group the data. I tried Object
but it only work when grouping with zero or one field but not when using several fields.
public List<Metric> getMetric(List<String> fields) {
MatchOperation match = match(where("status").in("WIN", "LOSS", "PUSH").and("year").gte(2015));
GroupOperation group = group(fields.toArray(new String[0]))
.count().as("count")
.avg("odd").as("averageOdd")
.sum("profit").as("profit")
.push("$$ROOT").as("picks");
Aggregation aggregation = Aggregation.newAggregation(
match,
group
);
// TODO: Handle _id
AggregationResults<Metric> aggregationResults = mongoTemplate.aggregate(aggregation, "pick", Metric.class);
return aggregationResults.getMappedResults();
}
public class Metric {
// private Map<String, Object> _id;
// private List<Object> _id;
private Object _id;
private Integer count;
private Double profit;
private Double averageOdd;
}
Using an empty list, the result (JSON representation of the List returned by the method) is:
[
{
"_id": null,
"count": 1997,
"profit": -0.07237707390649417,
"averageOdd": 2.4241016559624624,
"yield": -0.00003624290130520489
}
]
Using one field, the partial result is:
[
{
"_id": 2016,
"count": 751,
"profit": -34.484,
"averageOdd": 2.362993342210386,
"yield": -0.04591744340878828
}
]
Using several fields, the partial result is:
[
{
"_id": null,
"count": 211,
"profit": -18.961,
"averageOdd": 3.1104597156398106,
"yield": -0.08986255924170615
}
]
Upvotes: 1
Views: 490
Reputation: 12212
I found a way to map an _id
with several fields. In your mapped object you have to declare the fields that composed your _id
.
_id: {
sport: "MLB",
year: 2016
}
In the mapped object you have to declare the fields sport
and year
. The not so convenient part is you have to keep the _id
in your mapped object to handle the case for single field _id
even though it's logic.
The relevant code from MongoTemplate
:
class UnwrapAndReadDbObjectCallback<T> extends MongoTemplate.ReadDbObjectCallback<T> {
public UnwrapAndReadDbObjectCallback(EntityReader<? super T, DBObject> this$0, Class<T> reader, String type) {
super(reader, type, collectionName);
}
public T doWith(DBObject object) {
Object idField = object.get("_id");
if (!(idField instanceof DBObject)) {
return super.doWith(object);
} else {
DBObject toMap = new BasicDBObject();
DBObject nested = (DBObject)idField;
toMap.putAll(nested);
Iterator var5 = object.keySet().iterator();
while(var5.hasNext()) {
String key = (String)var5.next();
if (!"_id".equals(key)) {
toMap.put(key, object.get(key));
}
}
return super.doWith(toMap);
}
}
}
Upvotes: 1