使用条件混合模板聚合



我有一个集合,其中的文档如下所示:

{
  _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
}

我怎么能用springmongotemplate做这样的聚合?

要执行这类操作,您需要聚合框架中的$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
                ]
            }
        }
    }}
])

为了让这类东西在mongotmplate聚合下工作,您需要一个扩展聚合操作的类,该类可以从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表示基本相同的管道。

因此,就目前而言,在不支持某些操作和序列的情况下,最好的情况是在AggregationOperation接口上实现一个类,该类可以被提供DBObject,也可以从您自己的自定义方法内部构造DBObject。

您可以使用以下聚合:

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

它将为您提供message可用的status的计数,所有其他status的计数应视为0。

最新更新