【问题标题】:mongotemplate aggregation with condition带有条件的mongotemplate聚合
【发布时间】:2015-01-08 13:28:45
【问题描述】:

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

{
  _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” 我应该得到这个结果:

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

如何使用 spring mongotemplate 进行这种聚合?

【问题讨论】:

    标签: java mongodb mongodb-query aggregation-framework spring-data-mongodb


    【解决方案1】:

    要执行此类操作,您需要聚合框架中的 $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 或以其他方式从您自己的自定义方法内部构造一个。

    【讨论】:

    • 您的解决方案效果很好(只需将 $status 更改为 $messages.status ),我希望 mongotemplate 会尽快添加更多聚合 api,因为这不是很优雅。
    • @jacob 对不起,我的错。打字太快了。我会为后代编辑。我将您的问题重新标记为我所知道的“spring-data”维护人员定期查看的内容。 $cond 问题已经存在一段时间了。复杂的分组和分组_id 值是“已知问题”。其中一些作为方法实际上并不难实现。你总是可以捐出时间。我也应该考虑到这一点。
    • @NeilLunn 如何匹配两个条件?例如我想匹配messages.status=="Done"messages.status2=="Ready"
    • @NeilLunn 请展示matchunwind 方法的实现。
    • @NeilLunn 我找不到在哪里指定集合名称。你能分享完整的代码吗?
    【解决方案2】:

    您可以使用以下聚合:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 2023-02-13
      • 2019-01-08
      • 1970-01-01
      • 2023-01-11
      • 2020-09-28
      • 2015-01-23
      相关资源
      最近更新 更多