spring-data-mongodb使用原生aggregate语句(更新中)

有营养的yyl      2022-04-18     399

关键词:

除了特殊注释外,本文的测试结果均基于 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中的游标与数据一致性的取舍

使用 spring-data-mongodb 进行审计

】使用spring-data-mongodb进行审计【英文标题】:Auditingwithspring-data-mongodb【发布时间】:2017-05-2215:59:45【问题描述】:我正在尝试使用springdatamongodb启用自动审计字段,如here所述。下面是我的配置类@Configuration@EnableWebMvc@ComponentScan(b... 查看详情

将 Spring 安全 ACL 与 spring-data-mongodb 一起使用

】将Spring安全ACL与spring-data-mongodb一起使用【英文标题】:UsingSpringsecurityACLswithspring-data-mongodb【发布时间】:2017-11-0704:23:57【问题描述】:spring-security-acl文档声明如下:Pleasenotethatourout-of-the-boxAclServiceandrelateddatabaseclassesalluse 查看详情

spring-data-mongodb 使用 fieldName 而不是 _id

】spring-data-mongodb使用fieldName而不是_id【英文标题】:spring-data-mongodbusingthefieldNameinsteadof_id【发布时间】:2019-10-0812:46:41【问题描述】:我有一个属性为的PojoClassA@Id@Field("item_id")privateStringitemId;当我尝试根据itemId更新MongoDB集合中... 查看详情

使用 Kotlin 的 spring-data-mongodb 上的 @Transient 在读取期间导致异常

】使用Kotlin的spring-data-mongodb上的@Transient在读取期间导致异常【英文标题】:@Transientonspring-data-mongodbwithKotlinresultsinexceptionduringread【发布时间】:2019-06-0607:53:51【问题描述】:我的项目中使用Kotlin、SpringBoot2.0和MongoDB(使用SpringDa... 查看详情

mongodb分组函数的使用(spring-data-mongodb)

这两天要做mongodb日志的模块,下面记录一下。一、首先要导入一批数据,使用springboot来完成。配置mongodb的复制集:在application.yml文件中配置uri来完成格式:mongodb://用户名:密码@ip:端口[,ip:端口][,ip:端口]/数据库名下面注入mongoTem... 查看详情

MongoDB Aggregation - 如何使用 spring-data-mongodb 将查询表达式应用到匹配阶段?

】MongoDBAggregation-如何使用spring-data-mongodb将查询表达式应用到匹配阶段?【英文标题】:MongoDBAggregation-Howcaniapplyqueryexpressionintomatchstageusingspring-data-mongodb?【发布时间】:2021-12-0623:59:05【问题描述】:我有包含动态字段的文档,我... 查看详情

如何在 spring-boot 中禁用 spring-data-mongodb 自动配置

】如何在spring-boot中禁用spring-data-mongodb自动配置【英文标题】:Howtodisablespring-data-mongodbautoconfigurationinspring-boot【发布时间】:2015-04-2903:39:04【问题描述】:有没有人试过在spring-boot中禁用mongodb的自动配置?我正在尝试使用spring-d... 查看详情

是否可以在 spring-data-mongodb 中注入自定义 Jackson ObjectMapper?

】是否可以在spring-data-mongodb中注入自定义JacksonObjectMapper?【英文标题】:IsispossibletoinjectacustomJacksonObjectMapperinspring-data-mongodb?【发布时间】:2014-02-2312:50:12【问题描述】:我们在我们的应用程序中使用CodaHale指标,并使用Json模... 查看详情

spring-data-mongodb 在一个 Mongo 实例中连接多个数据库

】spring-data-mongodb在一个Mongo实例中连接多个数据库【英文标题】:Spring-data-mongodbconnecttomultipledatabasesinoneMongoinstance【发布时间】:2012-08-1804:53:40【问题描述】:我正在使用最新的spring-data-mongodb(1.1.0.M2)和最新的MongoDriver(2.9.0-RC1)。... 查看详情

使用 getDataAsExcel 导出 AG-Grid

】使用getDataAsExcel导出AG-Grid【英文标题】:AG-GridexportusinggetDataAsExcel【发布时间】:2021-05-0712:28:56【问题描述】:我正在使用ag网格导出引用this链接的表。我们可以使用exportDataAsExcel方法导出,下载的excel表格的表格名称为aggrid。... 查看详情

如何以 ManyToMany (RDBMS) 的形式加入两个集合 spring-data-mongodb

】如何以ManyToMany(RDBMS)的形式加入两个集合spring-data-mongodb【英文标题】:HowcanIjointwocollectionsspring-data-mongdbasManyToMany(RDBMS)【发布时间】:2017-05-2222:16:59【问题描述】:我有来自https://github.com/szerhusenBC/jwt-spring-security-demo/的这个示... 查看详情

您如何在 ag-grid 模块中使用多个组件?

】您如何在ag-grid模块中使用多个组件?【英文标题】:howdoyouusemultiplecomponentsinanag-gridmodule?【发布时间】:2021-02-1207:45:47【问题描述】:我正在尝试使用ag-grid和模块将数据集放在单独的组件上。与教程相反,它显示仅在应用程... 查看详情

如何使用 ag-grid 禁用行?

】如何使用ag-grid禁用行?【英文标题】:howtomakerowdisabledwithag-grid?【发布时间】:2021-01-0811:47:48【问题描述】:我与ag-grid合作,我发现如何在文档中禁用列(https://www.ag-grid.com/documentation-main/documentation.php)但在阅读文档后,我从来... 查看详情

Ag-grid:使用“agSelectCellEditor”时清除单元格

】Ag-grid:使用“agSelectCellEditor”时清除单元格【英文标题】:Ag-grid:clearcellwhen"agSelectCellEditor"isused【发布时间】:2021-04-1606:04:27【问题描述】:如ag-griddocumentation中所述:如果Backspace或删除被按下。但这在使用“agSelectCell... 查看详情

使用 AG 网格时是不是可以只显示列行?

】使用AG网格时是不是可以只显示列行?【英文标题】:IsitpossibletotoonlyshowColumnrowwhenusingAGgrid?使用AG网格时是否可以只显示列行?【发布时间】:2021-06-2721:15:05【问题描述】:我是AG-GridforJavaScript的新手。我想知道我是否有可能... 查看详情

使用 ag-grid 对数据进行分组

】使用ag-grid对数据进行分组【英文标题】:Agroupdatausingag-grid【发布时间】:2017-10-0520:58:44【问题描述】:我目前在我的项目中使用ag-grid。我正在尝试使用agrouping功能对数据进行分组。在我的coldef我有这个:headerName:"Parent",field:"... 查看详情

为啥没有使用 vuejs 将新行添加到 ag-grid?

】为啥没有使用vuejs将新行添加到ag-grid?【英文标题】:Whyarenewrowsnotbeingaddedtoag-gridusingvuejs?为什么没有使用vuejs将新行添加到ag-grid?【发布时间】:2020-12-1018:12:31【问题描述】:我在一个vue.js应用程序中有一个ag-grid组件,声明... 查看详情

将 ag-grid 与 flexbox 一起使用

】将ag-grid与flexbox一起使用【英文标题】:Usingag-gridwithflexbox【发布时间】:2019-04-0722:23:57【问题描述】:我一直在尝试将ag-grid与flex布局一起使用,但它似乎没有按预期工作。以下stackblitz有一个可重现的示例:https://stackblitz.com/... 查看详情