如何在Spring中编写这个Mongo聚合查询?

时间:2015-11-03 16:49:24

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

我在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的$ version版本的最大值
  5. 就像我说的那样,上面的查询已经有效了。我的问题是如何将其转换为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语法非常直观,而且他们的文档也不是很好。

    如果没有先调用nested(),我就无法调用and()方法。我认为这是主要的问题,因为它将那里的筑巢加倍。有没有Spring MongoOps英雄可以帮我正确编写等效的Java代码?

    编辑:以下是我使用的集合的快照:robomongo display

3 个答案:

答案 0 :(得分:6)

$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"}}}
])

Sprind 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")
    );

答案 1 :(得分:0)

在修正下划线错误之前使用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
}

答案 2 :(得分:0)

在聚合操作中执行字段引用验证时,Spring使用_作为数组的野生购物车,并拆分snake_case字段。

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

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