使用Spring Data MongoDB和Spring Boot进行数据聚合

MongoDB聚合框架旨在对文档进行分组并将其转换为聚合结果。 聚合查询包括定义将在管道中执行的几个阶段。 如果您对有关该框架的更深入的细节感兴趣,那么
mongodb docs是一个很好的起点。

这篇文章的重点是编写一个用于查询mongodb的Web应用程序,以便从数据库中获取汇总结果。 借助Spring Boot和Spring Data,我们将以非常简单的方式进行操作。 实际上,实现该应用程序确实非常快,因为Spring Boot会处理所有必要的设置,而Spring Data将帮助我们配置存储库。

可以在我的Github存储库中找到源代码。

1申请

在遍历代码之前,让我们看一下我们要对应用程序执行的操作。

我们的领域是我们分布在多个仓库中的产品的集合:

@Document
public class Product {@Idprivate final String id;private final String warehouse;private final float price;public Product(String id, String warehouse, float price) {this.id = id;this.warehouse = warehouse;this.price = price;}public String getId() {return id;}public String getWarehouse() {return warehouse;}public float getPrice() {return price;}
}

我们的目标是收集价格范围内的所有产品,并按仓库分组,并收集总收入和每个分组的平均价格。

在此示例中,我们的仓库存储以下产品:

new Product("NW1", "Norwich", 3.0f);
new Product("LN1", "London", 25.0f);
new Product("LN2", "London", 35.0f);
new Product("LV1", "Liverpool", 15.2f);
new Product("MN1", "Manchester", 45.5f);
new Product("LV2", "Liverpool", 23.9f);
new Product("LN3", "London", 55.5f);
new Product("LD1", "Leeds", 87.0f);

该应用程序将查询价格在5.0到70.0之间的产品。 所需的聚合管道步骤如下:

gregationPipeline(1)

我们将最终得到按仓库分组的汇总结果。 每个组将包含每个仓库的产品清单,平均产品价格和总收入,这实际上是价格的总和。

2 Maven依赖

如您所见,我们有一个简短的pom.xml,其中包含Spring Boot依赖项:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.3.3.RELEASE</version><relativePath/>
</parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>

通过将spring-boot-starter-parent定义为父pom,我们设置了Spring Boot的默认设置。 主要是它设置了可能使用的一堆库的版本,例如Spring或Apache Commons。 例如,我们正在使用的Spring Boot 1.3.3将4.2.5.RELEASE设置为Spring框架版本。 如前几篇文章所述,它没有将库添加到我们的应用程序中,而只是设置版本。

定义父项后,我们只需添加三个依赖项:

  • spring-boot-starter-web:主要包括Spring MVC库和嵌入式Tomcat服务器。
  • spring-boot-starter-test:包括JUnit,Mockito,Hamcrest和Spring Test等测试库。
  • spring-boot-starter-data-mongodb:此依赖项包括MongoDB Java驱动程序和Spring Data Mongo库。

3应用程序设置

多亏了Spring Boot,应用程序设置与依赖项设置一样简单:

@SpringBootApplication
public class AggregationApplication {public static void main(String[] args) {SpringApplication.run(AggregationApplication.class, args);}
}

运行main方法时,我们将启动侦听8080端口的Web应用程序。

4资料库

既然我们已经正确配置了应用程序,我们就可以实现存储库。 这也不难,因为Spring Data负责所有布线。

@Repository
public interface ProductRepository extends MongoRepository<Product, String> {}

以下测试证明我们的应用程序已正确设置。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AggregationApplication.class)
@WebAppConfiguration
public class AggregationApplicationTests {@Autowiredprivate ProductRepository productRepository;@Beforepublic void setUp() {productRepository.deleteAll();}@Testpublic void contextLoads() {}@Testpublic void findById() {Product product = new Product("LN1", "London", 5.0f);productRepository.save(product);Product foundProduct = productRepository.findOne("LN1");assertNotNull(foundProduct);}
}

我们没有实现save和findOne方法。 由于我们的存储库正在扩展MongoRepository,因此已经定义了它们。

5聚合查询

最后,我们设置了应用程序并解释了所有步骤。 现在我们可以专注于聚合查询。

由于我们的聚合查询不是基本查询,因此我们需要实现一个自定义存储库。 这些步骤是:

使用我们需要的方法创建自定义存储库:

public interface ProductRepositoryCustom {List<WarehouseSummary> aggregate(float minPrice, float maxPrice);
}

修改第一个存储库以扩展我们的自定义存储库:

@Repository
public interface ProductRepository extends MongoRepository<Product, String>, ProductRepositoryCustom {}

创建一个实现来编写聚合查询:

public class ProductRepositoryImpl implements ProductRepositoryCustom {private final MongoTemplate mongoTemplate;@Autowiredpublic ProductRepositoryImpl(MongoTemplate mongoTemplate) {this.mongoTemplate = mongoTemplate;}@Overridepublic List<WarehouseSummary> aggregate(float minPrice, float maxPrice) {...}
}

现在,我们将实现postgo开头所述的mongodb管道阶段。

我们的第一个操作是match操作。 我们将过滤掉超出我们价格范围的所有产品文档:

private MatchOperation getMatchOperation(float minPrice, float maxPrice) {Criteria priceCriteria = where("price").gt(minPrice).andOperator(where("price").lt(maxPrice));return match(priceCriteria);
}

流水线的下一个阶段是组操作。 除了按仓库对文档进行分组之外,在此阶段,我们还进行以下计算:

  • last:返回组中最后一个文档的仓库。
  • addToSet:收集所有分组文档的所有唯一产品ID,从而形成一个数组。
  • 平均:计算组中所有价格的平均值。
  • sum:汇总组中的所有价格。
private GroupOperation getGroupOperation() {return group("warehouse").last("warehouse").as("warehouse").addToSet("id").as("productIds").avg("price").as("averagePrice").sum("price").as("totalRevenue");
}

管道的最后阶段是项目运营。 在这里,我们指定聚合的结果字段:

private ProjectionOperation getProjectOperation() {return project("productIds", "averagePrice", "totalRevenue").and("warehouse").previousOperation();
}

查询的构建如下:

public List<WarehouseSummary> aggregate(float minPrice, float maxPrice) {MatchOperation matchOperation = getMatchOperation(minPrice, maxPrice);GroupOperation groupOperation = getGroupOperation();ProjectionOperation projectionOperation = getProjectOperation();return mongoTemplate.aggregate(Aggregation.newAggregation(matchOperation,groupOperation,projectionOperation), Product.class, WarehouseSummary.class).getMappedResults();
}

在聚合方法中,我们指示输入类,这是我们的产品文档。 下一个参数是输出类,它是一个DTO,用于存储结果聚合:

public class WarehouseSummary {private String warehouse;private List<String> productIds;private float averagePrice;private float totalRevenue;

我们应该通过测试来证明该结果符合我们的预期:

@Test
public void aggregateProducts() {saveProducts();List<WarehouseSummary> warehouseSummaries = productRepository.aggregate(5.0f, 70.0f);assertEquals(3, warehouseSummaries.size());WarehouseSummary liverpoolProducts = getLiverpoolProducts(warehouseSummaries);assertEquals(39.1, liverpoolProducts.getTotalRevenue(), 0.01);assertEquals(19.55, liverpoolProducts.getAveragePrice(), 0.01);
}private void saveProducts() {productRepository.save(new Product("NW1", "Norwich", 3.0f));productRepository.save(new Product("LN1", "London", 25.0f));productRepository.save(new Product("LN2", "London", 35.0f));productRepository.save(new Product("LV1", "Liverpool", 15.2f));productRepository.save(new Product("MN1", "Manchester", 45.5f));productRepository.save(new Product("LV2", "Liverpool", 23.9f));productRepository.save(new Product("LN3", "London", 55.5f));productRepository.save(new Product("LD1", "Leeds", 87.0f));
}private WarehouseSummary getLiverpoolProducts(List<WarehouseSummary> warehouseSummaries) {return warehouseSummaries.stream().filter(product -> "Liverpool".equals(product.getWarehouse())).findAny().get();
}

六,结论

Spring Data与MongoDB聚合框架具有良好的集成。 添加Spring Boot来配置应用程序,让我们集中精力构建查询。 对于构建过程,Aggregation类具有几种静态方法,可帮助我们实现不同的管道阶段。

我正在Google Plus和Twitter上发布我的新帖子。 如果您要更新新内容,请关注我。

翻译自: https://www.javacodegeeks.com/2016/04/data-aggregation-spring-data-mongodb-spring-boot.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354211.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结合前段修改mysql表数据_jquery实现点击文字可编辑并修改保存至数据库

这个方法网上可以查到很多&#xff0c;但是好多只有点击文字编辑并保持&#xff0c;但是没有完整的代码写怎么保存到数据库。因为本人才疏学浅&#xff0c;费啦好长时间才写好把修改的内容只用一条sql语句保存到数据库&#xff0c;今天在这里和大家分享这是运行图片这是前台页面…

java 设置两个方法互斥_分享两个操作Java枚举的实用方法

1. 前言Java枚举在开发中是非常实用的。今天再来分析几个小技巧并且回答一些同学的的疑问。首先要说明的是我的枚举建立在以下的范式之中&#xff1a;枚举统一接口范式2. 如何把枚举值绑定的下拉列表这种场景非常常见&#xff0c;如果你把状态、类别等属性封装成枚举的结构&…

Spring管理的交易说明-第2部分(JPA)

在本系列的第一部分中 &#xff0c;我展示了事务如何在普通JDBC中工作 。 然后&#xff0c;我展示了Spring如何管理基于JDBC的事务。 在本系列的第二部分中&#xff0c;我将首先展示事务如何在普通的JPA中工作。 然后展示Spring如何管理基于JPA的事务。 资金转移 为了帮助说明…

CCC数字钥匙设计【BLE】--车主配对之BLE OOB配对

本文主要介绍CCC3.0采用BLE进行车主配对时&#xff0c;关于蓝牙OOB配对的内容。 首先&#xff0c;介绍下BLE Pairing的一些基础知识&#xff0c;有一些基本概念。之后&#xff0c;再着重介绍CCC规范定义的BLE OOB配对流程。 1、BLE Pairing基础知识 下面先简单介绍下BLE 5.0协…

Linux 查看内存状态

# 查看系统内存 命令&#xff1a;free 注&#xff1a;默认k单位显示注&#xff1a;-m 以MB注&#xff1a;-g以GB 单位显示total used free shared buffers cached Mem: 497 463 33 0 13 124 -/ buffe…

Altium Designer导入pcb原件之后都是绿的

转载于:https://www.cnblogs.com/chulin/p/8342041.html

在JConsole和VisualVM中查看DiagnosticCommandMBean

我已经将JConsole用作合适的通用JMX客户端已有很多年了。 该工具通常随Oracle JDK一起提供&#xff0c;并且易于使用。 在JMX交互方面&#xff0c;JConsole优于VisualVM的最大优点是JConsole带有内置的MBeans选项卡&#xff0c;而必须为VisualVM中的相同功能应用插件。 但是&am…

人人商城生成app教程_人人商城APP打包教程(APICLOUD版)

一.APP环境搭建和配置编译1.登录APICLOUD后台新建应用step1 注册账号注册apicloud 账号并登录APICLOUD控制台step2 新建应用再账户下面找到开发控制台>开发控制台>创建应用 填写应用名和说明&#xff0c;必选Native App创建Native App2 .开发工具下载安装APICLOUD开发工具…

WPF快速入门系列(2)——深入解析依赖属性

一、引言 感觉最近都颓废了&#xff0c;好久没有学习写博文了&#xff0c;出于负罪感&#xff0c;今天强烈逼迫自己开始更新WPF系列。尽管最近看到一篇WPF技术是否老矣的文章&#xff0c;但是还是不能阻止我系统学习WPF。今天继续分享WPF中一个最重要的知识点——依赖属性。 二…

圆台下料展开计算方法_怎么画 圆锥台展开图

展开全部1、 画出圆台的主视抄图(等腰梯形)袭&#xff1a;圆台的上bai下底直径为等腰梯形du的上zhi下底&#xff0c;圆台的高为等dao腰梯形的高&#xff1b;2、将等腰梯形补画成等腰三角形&#xff1b;(图中的虚线三角形即为补画部分)&#xff1b;3、以三角形的顶点为圆心&…

.31-浅析webpack源码之doResolve事件流(3)

放个流程图&#xff1a; 这里也放一下request对象内容&#xff0c;这节完事后如下(把vue-cli的package.json也复制过来了)&#xff1a; /*{ context: { issuer: , compiler: undefined },path: d:\\workspace\\doc,request: ./input.js,query: ,module: false,directory: false…

c++ 虚函数,纯虚函数的本质区别

转载博客&#xff1a;https://mp.weixin.qq.com/s?__bizMzAxNzYzMTU0Ng&amp;mid2651289202&amp;idx1&amp;sn431ffd1fae4823366a50b68aed2838d4&amp;chksm80114627b766cf31f72018ef5f1fe29591e9f6f4bd72018e7aea849342ca6f0a271fb38465ae#rd 学习C的多态性&…

云通讯短信验证码实例

1.注册登录云通讯 http://www.yuntongxun.com/user/login 2.创建应用得到应用相关信息 3.下载对应相关的Demo示例  http://www.yuntongxun.com/doc/rest/sms/3_2_2_3.html 4.send.php文件添加代码方便后续操作 session_start(); //随机验证码 $code rand(100000,999999)…

java 数组 内存_图解Java数组的内存分配

1. Java数组是静态的Java是静态语言&#xff0c;所以Java的数组也是静态的&#xff0c;即&#xff1a;数组被初始化后&#xff0c;长度不可变静态初始化&#xff1a;显式指定每个数组元素的初始值&#xff0c;系统决定数组长度String[] books new String[]{"疯狂Java讲义…

libgdx和Kotlin –类[2D平台原型]

这篇文章是libgdx和Kotlin文章的后续文章。 我已经决定开发一个简单的2D平台程序的原型&#xff08;沿着我的早期文章中的Star Assault进行介绍&#xff09;&#xff0c;但是我一直在使用和学习Kotlin&#xff0c;而不是Java。 对于本教程&#xff0c;该项目应处于上一篇文章…

spring jmx_JMX和Spring –第2部分

spring jmx这篇文章从本教程的第1部分继续。 嗨&#xff0c;在我的上一篇文章中&#xff0c;我解释了如何通过Spring设置JMX服务器以及如何通过身份验证和授权保护对它的访问。 在本文中&#xff0c;我将展示如何实现一个简单的MBean&#xff0c;该MBean允许用户在运行时更改L…

LeetCode:位运算实现加法

LeetCode&#xff1a;位运算实现加法 写在前面 位运算符 实现加法的思路 两个加数&#xff0c;比如5(101)和6(110)&#xff0c;如何不用加法就能得出两者之和呢&#xff1f; 我们知道二进制计算中&#xff0c;如果使用异或将会产生无进位的两者之和&#xff0c;而两数相与将会产…

[机器学习] 模型评价参数,准确率,召回率,F1-score

很久很久以前&#xff0c;我还是有个建筑梦的大二少年&#xff0c;有一天&#xff0c;讲图的老师看了眼我的设计图&#xff0c;说&#xff1a;“我觉得你这个设计做得很紧张”&#xff0c;当时我就崩溃&#xff0c;对紧张不紧张这样的评价标准理解无能。多年后我终于明白老师当…

java记录登陆时间_Spring security如何实现记录用户登录时间功能

一、原理分析spring security提供了一个接口 AuthenticationSuccessHandler,该接口中只有一个方法&#xff0c;用来进行登录成功后的操作public interface AuthenticationSuccessHandler {/*** Called when a user has been successfully authenticated.** param request the r…

bzoj3680

$模拟退火$ $这种全局最优的问题用模拟退火$ $模拟退火就是每次向四周随机移动&#xff0c;移动的幅度和温度成正比&#xff0c;如果新的位置更优就接受&#xff0c;否则按一定概率接收&#xff0c;概率和温度成正比$ $最后稳定后再在最优解附近蹦跶几下看看有没有更好的$ $你问…