Je kunt zoiets eigenlijk eerst doen met "project", maar voor mij is het een beetje contra-intuïtief om een $project
te vereisen stadium voor de hand:
Aggregation agg = newAggregation(
project("quantity")
.andExpression("dayOfMonth(date)").as("day")
.andExpression("month(date)").as("month")
.andExpression("year(date)").as("year")
.andExpression("price * quantity").as("totalAmount"),
group(fields().and("day").and("month").and("year"))
.avg("quantity").as("averavgeQuantity")
.sum("totalAmount").as("totalAmount")
.count().as("count")
);
Zoals ik al zei, contra-intuïtief omdat je dit allemaal zou moeten kunnen declareren onder $group
podium, maar de helpers lijken niet op deze manier te werken. De serialisatie komt een beetje grappig uit (omhult de argumenten van de datumoperator met arrays), maar het lijkt wel te werken. Maar toch, dit zijn twee pijplijnfasen in plaats van één.
Wat is het probleem hiermee? Welnu, door de fasen de fasen te scheiden, dwingt het "project"-gedeelte de verwerking van alle documenten in de pijplijn om de berekende velden te krijgen, wat betekent dat het alles doorloopt voordat het naar de groepsfase gaat.
Het verschil in verwerkingstijd is duidelijk te zien door de queries in beide vormen uit te voeren. Met een aparte projectfase duurt het op mijn hardware drie keer langer om uit te voeren dan de query waarbij alle velden worden berekend tijdens de "groeps" -bewerking.
Dus het lijkt erop dat de enige huidige manier om dit goed te construeren is door het pijplijnobject zelf te bouwen:
ApplicationContext ctx =
new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
BasicDBList pipeline = new BasicDBList();
String[] multiplier = { "$price", "$quantity" };
pipeline.add(
new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject("month", new BasicDBObject("$month", "$date"))
.append("day", new BasicDBObject("$dayOfMonth", "$date"))
.append("year", new BasicDBObject("$year", "$date"))
)
.append("totalPrice", new BasicDBObject(
"$sum", new BasicDBObject(
"$multiply", multiplier
)
))
.append("averageQuantity", new BasicDBObject("$avg", "$quantity"))
.append("count",new BasicDBObject("$sum",1))
)
);
BasicDBObject aggregation = new BasicDBObject("aggregate","collection")
.append("pipeline",pipeline);
System.out.println(aggregation);
CommandResult commandResult = mongoOperation.executeCommand(aggregation);
Of als je dat allemaal te kort lijkt, dan kun je altijd met de JSON-bron werken en dat ontleden. Maar het moet natuurlijk wel een geldige JSON zijn:
String json = "[" +
"{ \"$group\": { "+
"\"_id\": { " +
"\"month\": { \"$month\": \"$date\" }, " +
"\"day\": { \"$dayOfMonth\":\"$date\" }, " +
"\"year\": { \"$year\": \"$date\" } " +
"}, " +
"\"totalPrice\": { \"$sum\": { \"$multiply\": [ \"$price\", \"$quantity\" ] } }, " +
"\"averageQuantity\": { \"$avg\": \"$quantity\" }, " +
"\"count\": { \"$sum\": 1 } " +
"}}" +
"]";
BasicDBList pipeline = (BasicDBList)com.mongodb.util.JSON.parse(json);