MongoDB中的计算分组字段

对于MongoDB文档中的这个示例,如何使用MongoTemplate编写查询?

db.sales.aggregate( [ { $group : { _id : { month: { $month: "$date" }, day: { $dayOfMonth: "$date" }, year: { $year: "$date" } }, totalPrice: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } } ] ) 

或者一般来说,我如何按计算字段分组?

你可以先用“project”来做这样的事情,但对我来说,事先需要一个$project阶段有点反直觉:

  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") ); 

就像我说的那样,反直觉,因为你应该只能在$group阶段声明所有这些,但帮助者似乎不会这样工作。 序列化有点搞笑(用数组包装日期运算符参数),但它似乎确实有效。 但是,这仍然是两个管道阶段,而不是一个。

这有什么问题? 通过将阶段分开,“项目”部分强制处理管道中的所有文档以获得计算字段,这意味着它在进入组阶段之前通过所有部分。

通过以两种forms运行查询,可以清楚地看到处理时间的差异。 对于单独的项目阶段,在我的硬件上执行的时间比在“组”操作期间计算所有字段的查询要长三倍。

所以似乎正确构造它的唯一现有方法是自己构建管道对象:

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

或者,如果所有这些看起来都很简洁,那么您始终可以使用JSON源并解析它。 但当然,它必须是有效的JSON:

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

另一种方法是使用如下定义的自定义聚合操作类:

 public class CustomAggregationOperation implements AggregationOperation { private DBObject operation; public CustomAggregationOperation (DBObject operation) { this.operation = operation; } @Override public DBObject toDBObject(AggregationOperationContext context) { return context.getMappedObject(operation); } } 

然后在管道中实现它,如下所示:

 Aggregation aggregation = newAggregation( new CustomAggregationOperation( new BasicDBObject( "$group", new BasicDBObject("_id", new BasicDBObject("day", new BasicDBObject("$dayOfMonth", "$date" )) .append("month", new BasicDBObject("$month", "$date")) .append("year", new BasicDBObject("$year", "$date")) ) .append("totalPrice", new BasicDBObject( "$sum", new BasicDBObject( "$multiply", Arrays.asList("$price","$quantity") ) )) .append("averageQuantity", new BasicDBObject("$avg", "$quantity")) .append("count",new BasicDBObject("$sum",1)) ) ) ) 

所以它基本上只是一个与现有管道助手使用的接口一致的接口,但它不是直接使用其他辅助方法,而是直接在管道构造中返回DBObject定义。