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} } } )
它将为您提供可用message
的status
计数,所有其他status
计数应考虑为0。