简体   繁体   English

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

[英]How do I write this Mongo aggregation query in Spring?

I've got an aggregation query in MongoDB that works when I run it directly in the shell. 我在MongoDB中有一个聚合查询,当我直接在外壳中运行它时,它就可以工作。 Here's the shell query: 这是shell查询:

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

So as you can see, this does the following: 如您所见,这将执行以下操作:

  1. Matches only certain documents with specified IDs 仅匹配具有指定ID的某些文档
  2. Projects a nested field down to a base-level field (and effectively filters out all other fields from the pipeline, but still retaining the IDs) 将嵌套字段投影到基础级别字段(并有效地从管道中过滤掉所有其他字段,但仍保留ID)
  3. Unwinds the array elements of the $versions field that we projected into individual documents in the pipeline 展开我们投影到管道中各个文档中的$ versions字段的数组元素
  4. Finds the max value of those $versions per ID 查找每个ID的$ versions的最大值

Like I said, that query above already works. 就像我说的那样,上面的查询已经可以了。 My question is how to translate that into Spring MongoDB syntax. 我的问题是如何将其转换为Spring MongoDB语法。 Here's my first attempt, which does not work: 这是我第一次尝试, 工作:

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

When I try to run the code in debug mode, I can see that I actually get an IllegalArgumentException on newAggregation saying it cannot evaluate. 当我尝试在调试模式下运行代码时,我可以看到我实际上在newAggregation上收到一个IllegalArgumentException,说它无法求值。 If I comment out the line with the $group clause, then I can see this toString() representation of aggregation variable, which reveals a problem with the $project clause: 如果用$ group子句注释掉这一行,那么我可以看到聚合变量的toString()表示形式,这表明$ project子句存在问题:

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

Clearly this doesn't match up with what I was intending so I'm not getting the syntax correct. 显然,这与我的预期不符,因此语法不正确。 But TBH I don't find the Spring MongoOps syntax very intuitive, and their documentation isn't great either. 但是TBH我觉得Spring MongoOps语法不是很直观,并且它们的文档也不是很好。

I don't see any way to call that nested() method without first including the call to and() . 在没有首先包含对and()调用之前,我看不到任何调用该nested()方法的方法。 I think that's the main problem since it's doubling up the nesting there. 我认为这是主要问题,因为它使此处的嵌套加倍。 Are there any Spring MongoOps heroes here who can help me write the equivalent Java code properly? 这里有任何Spring MongoOps英雄可以帮助我正确地编写等效的Java代码吗?

EDIT: Here's a snapshot of the collection I'm using: 编辑:这是我正在使用的集合的快照: 机器人显示器

The $project pipeline is not necessary since you can still do an $unwind on the nested field, thus this aggregation pipeline can yield the same results as your current: $project管道不是必需的,因为您仍然$unwind在嵌套字段上执行$unwind ,因此此聚合管道可以产生与当前结果相同的结果:

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

The Spring Data MongoDB aggregation equivalent: Spring Data MongoDB聚合等效项:

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

Back to your current aggregation, you need to $unwind on nested.field array, not nested.field.version field since that is a String, not array: 回到您当前的聚合,您需要$unwind on nested.field数组,而不是nested.field.version字段,因为那是一个String而不是数组:

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

the Sprind Data MongoDB equivalent would look like: 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")
    );

use map reduce way before the underscore bug is fixed. 在修复下划线错误之前,请使用map reduce方法。 like : 喜欢 :

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
}

Spring uses _ as a wild cart for arrays, and split snake_case fields, when performing fields reference validation in aggregate operations. 当在聚合操作中执行字段引用验证时,Spring将_用作数组的疯狂购物车,并分割snake_case字段。

To avoid validation, you can use the following MongoTemplate method, that performs aggregation, without fields transformation and validation. 为了避免验证,您可以使用以下MongoTemplate方法执行聚合,而无需进行字段转换和验证。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM