【问题标题】:How do I write this Mongo aggregation query in Spring?如何在 Spring 中编写这个 Mongo 聚合查询?
【发布时间】:2015-11-03 16:49:24
【问题描述】:

我在 MongoDB 中有一个聚合查询,当我直接在 shell 中运行它时它就可以工作。这是 shell 查询:

db.MyCollection.aggregate([
    {$match: {_id: {$in: ['A', 'B', 'C']}}},
    {$project: {"versions": "$nested.field.version"}},
    {$unwind: "$versions"},
    {$group: {_id: "$_id", "maxVersion": {$max: "$versions"}}}
])

如您所见,它执行以下操作:

  1. 仅匹配具有指定 ID 的某些文档
  2. 将嵌套字段向下投影到基础级别字段(并有效地从管道中过滤掉所有其他字段,但仍保留 ID)
  3. 展开我们投射到管道中各个文档中的 $versions 字段的数组元素
  4. 查找每个 ID 的 $versions 的最大值

就像我说的,上面的查询已经有效。我的问题是如何将其转换为 Spring MongoDB 语法。这是我的第一次尝试,它不起作用

Aggregation aggregation = newAggregation(
    match(Criteria.where("_id").in(listOfIds))
    ,project().and("versions").nested(bind("versions", "nested.field.version"))
    ,unwind("versions")
    ,group("_id").max("versions").as("maxVersion")
);

当我尝试在调试模式下运行代码时,我可以看到我实际上在 newAggregation 上得到了一个 IllegalArgumentException,说它无法评估。如果我用 $group 子句注释掉该行,那么我可以看到聚合变量的这个 toString() 表示,这揭示了 $project 子句的问题:

{
  "aggregate" : "__collection__" ,
  "pipeline" : [
    { "$match" : { "_id" : { "$in" : [ "A" , "B" , "C"]}}} ,
    { "$project" : { "versions" : { "versions" : "$nested.field.version"}}} ,
    { "$unwind" : "$versions"}
  ]
}

显然这与我的意图不符,所以我的语法不正确。但是 TBH 我觉得 Spring MongoOps 的语法不是很直观,他们的文档也不是很好。

如果不首先包括对and() 的调用,我看不到任何调用nested() 方法的方法。我认为这是主要问题,因为它在那里的嵌套增加了一倍。这里有没有Spring MongoOps大侠可以帮我正确写出等价的Java代码?

编辑:这是我正在使用的集合的快照:

【问题讨论】:

    标签: java spring mongodb spring-data spring-data-mongodb


    【解决方案1】:

    $project 管道不是必需的,因为您仍然可以在嵌套字段上执行 $unwind,因此此聚合管道可以产生与您相同的结果当前:

    db.MyCollection.aggregate([
        {
            "$match": {
                "_id": { "$in": ['A', 'B', 'C'] }
            }
        },
        { "$unwind": "$nested.field" },
        {
            "$group": {
                "_id": "$_id", 
                "maxVersion": { "$max": "$nested.field.version" }
            }
        }
    ])
    

    Spring Data MongoDB 聚合等效项:

    Aggregation agg = newAggregation(
            match(Criteria.where("_id").in(ids)),
            unwind("nested.field"),        
            group("_id").max("nested.field.version").as("maxVersion")
        );
    

    回到您当前的聚合,您需要在nested.field 数组上$unwind,而不是nested.field.version 字段,因为这是一个字符串,而不是数组:

    db.MyCollection.aggregate([
        {$match: {_id: {$in: ['A', 'B', 'C']}}},
        {$project: {"fields": "$nested.field"}},
        {$unwind: "$fields"},
        {$group: {_id: "$_id", "maxVersion": {$max: "$fields.version"}}}
    ])
    

    Sprint Data MongoDB 等效项如下所示:

    Aggregation agg = newAggregation(
            match(Criteria.where("_id").in(ids)),
            project().and("nested.field").as("fields")
            unwind("fields"),        
            group("_id").max("fields.version").as("maxVersion")
        );
    

    【讨论】:

    • 嗯,你的回答是正确的。不幸的是,由于 Spring Data 的错误,它在我的特定情况下不起作用。我总是在发布到 SO 之前清理我的字段名称,但实际上我在一些嵌套的字段名称中有下划线,并且看起来 Spring Data 在执行一些导致它失败的引用完整性检查时对下划线进行了某种拆分。所以感谢您的努力,但不幸的是,Spring Data 的错误太多,现在无法用于我的用例。
    • @SoaperGEM 不用担心,太糟糕了,这对你没用。
    【解决方案2】:

    在下划线错误修复之前使用 map reduce 方式。 喜欢:

    GroupBy groupBy = GroupBy.key("user_id")
            .initialDocument("{ total : 0, used : 0 }")
            .reduceFunction("function( curr, result ){ result.total++; if(curr.status == 1) {result.used++;} result.userID = curr.user_id;");
            GroupByResults<YourResult> yourResultInfo =
                    mongoTemplate.group(Criteria.where("user_id").in(user_ids),
                                    "your_collection_name", groupBy, YourResult.class);
    
    class YourResult{
    private String userID;
        private Long total = 0l;
        private Long used = 0l;
    // getter and setter`enter code here
    }
    

    【讨论】:

      【解决方案3】:

      在聚合操作中执行字段引用验证时,Spring 使用 _ 作为数组的通配符,并拆分蛇案例字段。

      为避免验证,您可以使用以下 MongoTemplate 方法,该方法执行聚合,无需字段转换和验证。

      public <O> AggregationResults<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-02
        • 2020-04-29
        • 1970-01-01
        • 2018-08-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多