1. 程式人生 > >spring boot mongodb 統計表中某一欄位的所有值,其中aggregate的用法之一

spring boot mongodb 統計表中某一欄位的所有值,其中aggregate的用法之一

pom.xml 依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

yml檔案配置:

data:
  mongodb:
    port: 27017
    host: xx.xx.xx.xx//(IP地址)
    database: test

這裡因為用nosql,非關係型資料庫,所以只說明非實體類統計的用法,即,資料庫中的表沒有對應的實體類;

需求是:統計表中某列的總和

程式碼:

public Long getSumByScoreField(String name, String subject, String time) {
        DBCollection collection = mongoTemplate.getCollection("user");//user為表
        String matchStr = "{\"$match\":{\"name\":\"" + name+ "\",\"subject\":\"" + subject+ "\",\"login_time\":\"" + time + "\"}}";
        String groupStr = "{\"$group\":{\"_id\": null,\"total\":{\"$sum\": \"$score\"}}}";
        System.out.println(JSON.parse(matchStr));
        DBObject match = (DBObject) JSON.parse(matchStr);
        DBObject group = (DBObject) JSON.parse(groupStr);
        LinkedList<DBObject> list= new LinkedList<>();
        objects.add(match);
        objects.add(group);
        Cursor cursor = collection.aggregate(list, AggregationOptions.builder().outputMode(AggregationOptions.OutputMode.CURSOR).build());
        while (cursor.hasNext()){
            System.out.println(cursor.next());
        }
}

以上程式碼中如果是第一次接觸的同行就有一些陌生的關鍵字,如:$match,$group,$sum,這三個皆為對應mongodb的關鍵字,

match是依據的條件查詢,group是分組查詢,在該需求就會用到,sum聚合函式求總和,還有一個$score,這個是表中的欄位score,因為要求和的正是該欄位的和,固定寫法而已,至於其他欄位都是表的欄位,注意這裡沒有對應的POJO類。

上述的程式碼中有這麼一段程式碼:

Cursor cursor = collection.aggregate(list, AggregationOptions.builder().outputMode(AggregationOptions.OutputMode.CURSOR).build());

需要額外說明一下,這段程式碼之前我是這麼寫的:

AggregationOutput aggregate2 = collection.aggregate(match, group);

或者是:

AggregationOutput aggregate2 = collection.aggregate(list);

這麼寫的,很多時候在執行到這段程式碼的時候會出錯,所以就改成上述程式碼段裡的寫法了因為當寫成上面紅色程式碼的兩種方式時會報錯:

com.mongodb.MongoCommandException: Command failed with error 9: 'The 'cursor' option is required, except for aggregate with the explain argument' on server 192.168.31.14:27017. The full response is { "ok" : 0.0, "errmsg" : "The 'cursor' option is required, except for aggregate with the explain argument", "code" : 9, "codeName" : "FailedToParse" }
    at com.mongodb.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:115) ~[mongodb-driver-core-3.4.3.jar:na]

這種報錯要麼是依賴的mongodb版本和spring-bootstart版本不同,要麼就是:這裡細心的同行會發現,錯誤的(標紅字)和正確的(程式碼段裡面的)返回值型別不同,Cursor為輸入模型的,AggregationOutput 為輸出模型的,預設是輸出模型,所以,當出現上述異常時可以改下程式碼為輸入模型即可;

上述程式碼段還可以有一下寫法:

  public Long getSumByScoreField(String name, String subject, String time) {
    BasicDBObject basicDBObject = new BasicDBObject();
//        basicDBObject.put("name", name);
//        basicDBObject.put("subject", subject);
//        basicDBObject.put("update_time", time);
        BasicDBObject[] basicDBObjects = {new BasicDBObject("name", name), new BasicDBObject("subject", subject), new BasicDBObject("update_time", time)};
//        BasicDBObject basicDBObject = new BasicDBObject();
        basicDBObject.put("$and", basicDBObjects);
        BasicDBObject smatch = new BasicDBObject();
        smatch.put("$match", basicDBObject);
        BasicDBObject dbObject = new BasicDBObject("_id", null);
        dbObject.put("total", new BasicDBObject("$sum", "$score"));
//        DBObject group = new BasicDBObject("$group", dbObject);
        List list = new LinkedList();
        list.add(smatch);
        list.add(group);
        Cursor aggregate = collection.aggregate(list, AggregationOptions.builder().outputMode(AggregationOptions.OutputMode.CURSOR).build());
        if (aggregate.hasNext()) {
            System.out.println(aggregate.next().get("total"));
        }
}

以上的兩種寫法最終翻譯成mysql查詢程式碼為:

select sum(score) as total from user where name=? and subject = ?and time=?

mongodb的寫法為:

db.user.aggregate()
    .match({"name":"JONE","subject":"數學","update_time":"2019-01-02"}).group({
          _id: null,total:{$sum: "$score"}
    })

如果是關係型,也就是建立對應表的實體類POJO的話就用:(這段程式碼還沒測試,但大概思路、用法是這樣的)

Aggregation aggregation = Aggregation.newAggregation(
                match(Criteria.where("name").is(name).and("subject").is(subject).and("update_time").is(time)),
                group("_id").sum("score").as("total"),
                sort(Sort.Direction.DESC, "total"),
                limit(1));
        mongoTemplate.aggregate(aggregation,User.class,BasicDBObject.class);

以上純屬個人的一點開發實際來歷,希望可以幫到見到的你,如有好的想法、方法。

請留言,謝謝!