• spring-data-mongodb 使用原生aggregate语句


    除了特殊注释外,本文的测试结果均基于 spring-data-mongodb:1.10.6.RELEASE(spring-boot-starter:1.5.6.RELEASE),MongoDB 3.0.6


       考虑到大多数人都是来找答案的,所以先给出结论

    // import org.springframework.data.mongodb.core.MongoTemplate;
    mongoTemplate.getDb().doEval("db.user.aggregate([{$group:{_id:'$name',count:{$sum:'$age'}}}])");

    注意:

      1、mongo shell 使用js语法,可以使用单引号或者双引号表示字符串,这里使用单引号,可以避免大量的 转义符

      2、原生语句中的key:value部分,value只能是 [xxx] 、{xxx} 、 ‘xxx’ 三种格式的数据。

      2.1.2版本的spring-data-mongodb已经去掉了doEval方法,我们可以使用下面的方法自己拼接

    //spring-boot-starter:2.1.0.RELEASE spring-data-mongodb:2.1.2.RELEASE
    BasicDBObject bson = new BasicDBObject();
    bson.put("$eval","db.user.aggregate([{$group:{_id:'$name',count:{$sum:'$age'}}}])");
    Object object = mongoTemplate.getDb().runCommand(bson);

      其他的解决方法,如调用存储过程、拼接完整的BasicDBObject、继承Aggregate请见第三章:SPRING-DATA-MONGODB底层与MONGO-DRIVER的交互


      研究这个是因为遇到了一个业务需求,需要使用多种限制条件,返回多个统计字段。spring-data-mongodb提供的API不足以实现这么复杂的业务,所以就想到了直接使用原生的aggregate查询。

      mongo底层实现查询的方法主要有两种,一种是 db._collection_.doSomething({ ... }) ,另一种是db.runCommand({doSomething:_collection_ , ... }) ,我们将第一种称作函数,第二种称作命令

      那么,哪种才是 aggregate的原生查询?spring-data-mongodb底层究竟调用的是函数还是命令?如果你只是想完成工作的话,copy上面的代码,然后右上角点×,如果你想解决问题学点东西的话,欢迎继续看下去,我这边写了四章详细的分析过程,链接在底部

    ======2019-11-15补充原生语句转换方式补充======================================================

      提供了一个执行原生字符串aggregate语句的方法,对外暴露方法 dbAggregate(collectionName, pipeline)

      1 import com.mongodb.BasicDBList;
      2 import com.mongodb.BasicDBObject;
      3 import org.apache.commons.lang.StringUtils;
      4 import org.springframework.beans.factory.annotation.Autowired;
      5 import org.springframework.data.mongodb.core.MongoTemplate;
      6 import org.springframework.stereotype.Component;
      7 
      8 import javax.annotation.PostConstruct;
      9 import java.util.LinkedList;
     10 
     11 @Component
     12 public class MongoUtil {
     13 
     14     @Autowired
     15     MongoTemplate mongoTemplate;
     16 
     17     @PostConstruct
     18     public void test() {
     19         dbAggregate("user",
     20                 "([{$match:{name:wwl}},{$group:{_id:$name,count:{$sum:#NUM1}}},{$project:{count:#BOOtrue}}])");
     21     }
     22 
     23     /**
     24      * aggregate原生语句执行方法
     25      * @param collectionName 表名
     26      * @param pipeline 管道操作语句。【:后面的内容,默认string,数字或者布尔加标识符#NUM,#BOO】
     27      * @return {“result”:[结果], "ok":查询状态}
     28      */
     29     public Object dbAggregate(String collectionName, String pipeline){
     30         BasicDBObject bdr = new BasicDBObject();
     31         bdr.put("aggregate", collectionName);
     32         char[] c = pipeline.toCharArray();
     33         bdr.put("pipeline", appendWithChar(c));
     34         return mongoTemplate.getCollection("$cmd").findOne(bdr);
     35     }
     36 
     37 
     38     /**
     39      * 根据标点分组,并对分组数据处理转出最终的参数
     40      * @param c 待分组的字符串数组
     41      * @return 转换
     42      */
     43     private static Object appendWithChar(char[] c){
     44         LinkedList<Object> valuelist = new LinkedList();
     45         StringBuffer sb = new StringBuffer();
     46         for(char ele : c){
     47             if('{' == ele){
     48                 valuelist.add(new BasicDBObject());
     49             }else if('[' == ele){
     50                 valuelist.add(new BasicDBList());
     51             }else if(',' == ele || ']' == ele || '}' == ele){
     52                 if(sb.length() > 0){
     53                     valuelist.add(sb.toString());
     54                     sb.delete(0 , sb.length());
     55                 }
     56                 insertValue(valuelist);
     57             }else if(':' == ele){
     58                 valuelist.add(sb.toString());
     59                 sb.delete(0 , sb.length());
     60             }else{
     61                 sb.append(ele);
     62             }
     63         }
     64         return valuelist.getLast();
     65     }
     66 
     67     /**
     68      * 根据数据类型插入数据
     69      * @param valuelist [obj1,obj2] obj1.add(obj2) 或者 obj.put(obj1)。
     70      *                  add完后obj2失效,obj1有机会进入下一次插入数据判断;put完后obj1和obj2都失效
     71      */
     72     private static void insertValue(LinkedList<Object> valuelist) {
     73         Object value1 = checkValue(valuelist.removeLast());
     74         Object value2 = valuelist.getLast();
     75         if( value2  instanceof BasicDBList ){
     76             ((BasicDBList)value2).add(value1);
     77         }else{
     78             valuelist.removeLast();
     79             BasicDBObject dbObject = (BasicDBObject)valuelist.getLast();
     80             dbObject.put(value2.toString(), value1);
     81         }
     82     }
     83 
     84     /**
     85      * 根据标识符 #NUM#BOO 判断value是否需要强转
     86      * @param o 待判断是否需要强转的参数
     87      * @return  处理后的参数
     88      */
     89     private static Object checkValue(Object o) {
     90         try {
     91             String[] str = StringUtils.split(o.toString(), "NUM");
     92             if( 2 == str.length &&  StringUtils.equals("#",str[0]) ){
     93                 return Long.parseLong(str[1]);
     94             }
     95 
     96             String[] str2 = StringUtils.split(o.toString(), "BOO");
     97             if( 2 == str2.length &&  StringUtils.equals("#",str2[0]) ){
     98                 return Boolean.valueOf(str2[1]);
     99             }
    100         }catch (Exception e){
    101             System.out.println("强转失败");
    102             e.printStackTrace();
    103         }
    104         return o;
    105     }
    106 }


     目录

      一:spring-data-mongodb 使用原生aggregate语句

      二:mongo的runCommand与集合操作函数的关系

      三:spring-data-mongodb与mongo shell的对应关系

      四:mongo中的游标与数据一致性的取舍

  • 相关阅读:
    ABP框架(asp.net core 2.X+Vue)运行前端(转)
    Spring学习指南第3版例子程序导入idea20.02
    Docker运行jar包(转)
    centos7使用docker搭建运行环境并部署jar(转)
    centos7修改docker镜像源的方法(转)
    虚拟机下CentOS7安装Docker(转)
    Virtual Box配置CentOS7网络(图文教程)(转)
    虚拟机docker运行jar程序
    docker安装
    【591】通过 Terminal 直接获取 GitHub 源码
  • 原文地址:https://www.cnblogs.com/ttjsndx/p/9942776.html
Copyright © 2020-2023  润新知