mongotemplate聚合与条件

我有一个文件看起来像这样的集合:

{ _id: "545b9fa0dd5318a4285f7ce7", owner: "admin", messages: [ { id: "100", status: "sent", note: "" }, { id: "100", status: "pending", note: "" }, { id: "101", status: "sent", note: "" }, { id: "102", status: "sent", note: "" }, { id: "101", status: "done", note: "" } ] } 

(这只是一个简短的例子,在我的例子中,子数组非常大)

我需要查询集合并获取特定文档的一些统计信息。 所以在这个例子中,如果我查询具有id:“545b9fa0dd5318a4285f7ce7”的doucment,我应该得到这个结果:

 { sent: 3, pending: 1, done: 1 } 

如何使用spring mongotemplate进行此类聚合?

要做到这一点,你需要聚合框架中的$cond运算符。 Spring Data MongoDB还没有这样做,并且普通的$group操作缺少许多东西,甚至只在$project下实现。

跟踪任何$cond支持的实现的问题在这里:

https://jira.spring.io/browse/DATAMONGO-861

对于世界其他地方,它看起来像这样:

 db.collection.aggregate([ { "$match": { "_id": ObjectId("545b9fa0dd5318a4285f7ce7") } }, { "$unwind": "$messages" }, { "$group": { "_id": "$_id", "sent": { "$sum": { "$cond": [ { "$eq": [ "$mesages.status", "sent" ] }, 1, 0 ] } }, "pending": { "$sum": { "$cond": [ { "$eq": [ "$messages.status", "pending" ] }, 1, 0 ] } }, "done": { "$sum": { "$cond": [ { "$eq": [ "$messages.status", "done" ] }, 1, 0 ] } } }} ]) 

要在mongotemplate聚合下使用这种东西,你需要一个扩展聚合操作的类,它可以从DBObject构建:

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

然后,您可以将“$ group”定义为DBObject并在聚合管道中实现:

  DBObject myGroup = (DBObject)new BasicDBObject( "$group", new BasicDBObject( "_id","$_id" ).append( "sent", new BasicDBObject( "$sum", new BasicDBObject( "$cond", new Object[]{ new BasicDBObject( "$eq", new Object[]{ "$messages.status", "sent"} ), 1, 0 } ) ) ).append( "pending", new BasicDBObject( "$sum", new BasicDBObject( "$cond", new Object[]{ new BasicDBObject( "$eq", new Object[]{ "$messages.status", "pending"} ), 1, 0 } ) ) ).append( "done", new BasicDBObject( "$sum", new BasicDBObject( "$cond", new Object[]{ new BasicDBObject( "$eq", new Object[]{ "$messages.status", "done"} ), 1, 0 } ) ) ) ); ObjectId myId = new ObjectId("545b9fa0dd5318a4285f7ce7"); Aggregation aggregation = newAggregation( match(Criteria.where("_id").is(myId)), unwind("messges"), new CustomGroupOperation(myGroup) ); 

这允许您提出一个与上面的shell表示基本相同的管道。

所以现在看来​​,在不支持某些操作和序列的情况下,最好的情况是在AgggregationOperation接口上实现一个类, 该类可以提供DBObject,或者从您自己的自定义方法内部构造一个。

您可以使用以下聚合:

 db.collection.aggregate( { $match : { "_id" : ObjectId("545b9fa0dd5318a4285f7ce7") } }, { $unwind : "$messages" }, { $group : { "_id" : "$messages.status", "count" : { $sum : 1} } } ) 

它将为您提供可用messagestatus计数,所有其他status计数应考虑为0。