簡體   English   中英

如何在 Spring 中編寫 mongo 聚合減少查詢?

[英]How do I write mongo aggregation reduce query in Spring?

蒙戈中的數據:

在此處輸入圖像描述

db.test2.aggregate([
    {
        "$project" : {
        "contents" : 1,
        "comments" : {
            "$filter" : {
                "input" : "$comments", 
                "as" : "item", 
                "cond" : {"$gt" : ['$$item.score', 2]}

                },
            },
         "comments2" : {
            "$filter" : {
                "input" : "$comments2", 
                "as" : "item", 
                "cond" : {"$gt" : ["$$item.score", 5]}
            }
            }
        }
     },
     {
     "$project" : {
            "content" : 1,
            "commentsTotal" : {
                "$reduce" : {
                    "input" : "$comments",
                    "initialValue" : 0,
                    "in" : {"$add" : ["$$value", "$$this.score"]}
                }
                },
            "comments2Total" : {
                "$reduce" : {
                "input" : "$comments2",
                "initialValue" : 0,
                    "in" : {"$add" : ["$$value", "$$this.score"]}
                }
            }
        }
      },
      {$skip : 0},
      {$limit: 3}

  ]);
  <!-- language: lang-json-->

所以你可以看到,這做了以下事情: 1、過濾評論和 comments2 評分是 gt 5。 2、統計評論數組中的 socre 總數。

我在 Spring 中編寫聚合查詢是這樣的:

AggregationExpression reduce = ArithmeticOperators.Add.valueOf("$$value").add("$$this.socre");
    Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.project().andExclude("_id")
                    .andInclude("content")
                    .and("comments").filter("item", ComparisonOperators.Gt.valueOf("item.score").greaterThanValue(3)).as("comments")
                    .and("comments2").filter("item", ComparisonOperators.Gt.valueOf("item.score").greaterThanValue(3)).as("comments2"),
            Aggregation.project("comments", "comments2")
                    .and(ArrayOperators.Reduce.arrayOf("comments").withInitialValue("0").reduce(reduce)).as("commentsTotal")
    );

當我跑起來時,它會拋出異常:

java.lang.IllegalArgumentException:無效引用“$$value”!

您可以通過將$filter包裝在$reduce操作中來嘗試下面的聚合。

像下面這樣的東西

AggregationExpression reduce1 = new AggregationExpression() {
        @Override
        public DBObject toDbObject(AggregationOperationContext aggregationOperationContext) {
         DBObject filter = new BasicDBObject("$filter", new BasicDBObject("input", "$comments").append("as", "item").append("cond",
                    new BasicDBObject("$gt", Arrays.<Object>asList("$$item.score", 2))));
         DBObject reduce = new BasicDBObject("input", filter).append("initialValue", 0).append("in", new BasicDBObject("$add", Arrays.asList("$$value", "$$this.socre")));
         return new BasicDBObject("$reduce", reduce);
     }
 };

Aggregation aggregation = newAggregation(
     Aggregation.project().andExclude("_id")
       .andInclude("content")
       .and(reduce1).as("commentsTotal")
);

這是一個老問題,但萬一有人像我一樣來到這里,這就是我能夠解決它的方法。

在 spring 中,您不能像這樣直接訪問“ $$this ”和“ $$value ”變量。

AggregationExpression reduce = ArithmeticOperators.Add.valueOf("$$value").add("$$this.socre");

為此,我們必須使用 reduce 變量枚舉,如下所示:

AggregationExpression reduce = ArithmeticOperators.Add.valueOf(ArrayOperators.Reduce.Variable.VALUE.getTarget()).add(ArrayOperators.Reduce.Variable.THIS.referringTo("score").getTarget());

希望這可以幫助!

我必須解決下一個任務,但沒有找到任何解決方案。 所以我希望我的回答能幫助別人。

有角色的用戶(用戶有權限列表+角色列表,每個角色都有自己的權限列表,需要找到完整的權限列表):

用戶結構

角色結構

首先,我將角色查找到 roleDto(例如),然后我將角色的權限收集到 1 個列表:

ArrayOperators.Reduce reduce = ArrayOperators.Reduce.arrayOf("$roleDto.rights")
        .withInitialValue(new ArrayList<>())
        .reduce(ArrayOperators.ConcatArrays.arrayOf("$$value").concat("$$this"));

作為減少的結果,我有從角色中收集的這 1 個權利列表。 之后我做:

SetOperators.SetUnion.arrayAsSet(reduce).union("$rights")

使用以前的結果。 結果類型是 AggregationExpression,因為 AbstractAggregationExpression 實現了 AggregationExpression。

所以,最后我得到了這樣的東西(抱歉代碼混亂):

private static AggregationExpression getAllRightsForUser() {
    // concat rights from list of roles (each role have list of rights) - list of list to list
    ArrayOperators.Reduce reduce = ArrayOperators.Reduce.arrayOf("$roleDto.rights")
        .withInitialValue(new ArrayList<>())
        .reduce(ArrayOperators.ConcatArrays.arrayOf("$$value").concat("$$this"));
    // union result with user.rights
    return SetOperators.SetUnion.arrayAsSet(reduce).union("$rights");
}

這個操作的結果最終可以在像這里這樣的地方使用;):

public static AggregationOperation addFieldOperation(AggregationExpression aggregationExpression, String fieldName) {
    return aoc -> new Document("$addFields", new Document(fieldName, aggregationExpression.toDocument(aoc)));
}

我有同樣的問題,解決方案之一是創建自定義 Reduce 函數,這里是 Union 示例:

public class SetUnionReduceExpression implements AggregationExpression {
    @Override
    public Document toDocument(AggregationOperationContext context) {
        return new Document("$setUnion", ImmutableList.of("$$value", "$$this"));
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM