【微服务】springboot整合mongodb使用详解

目录

一、mongodb简介

1.1 什么是mongodb

1.2 mongodb特点

二、mongodb中的核心术语

2.1 mogodb与数据库对比

2.2 mongodb中的核心概念

2.3 与关系数据库的差异

2.3.1 半结构化

2.3.2 支持多级嵌套

2.3.3 关系弱化

三、mongodb 技术优势和应用场景

3.1 mongodb 技术优势

3.2 mongodb 应用场景

3.3 什么时候选择 mongodb

四、快速部署 mongodb

4.1 搭建过程

4.1.1 拉取镜像

4.2.2 启动镜像

4.2 创建账户

4.2.1 登录mongo容器,并进入到【admin】数据库

4.2.2 创建一个用户

4.3 连接与测试

4.3.1连接mongo数据库

4.3.2 插入与查询数据

4.3.3 使用客户端连接

五、整合springboot使用

5.1 前置说明

5.1.1 jpa方式整合

5.1.2 MongoTemplate 方式整合

5.2 准备一个数据库

5.3 jpa方式整合使用

5.3.1 创建一个springboot的工程

5.3.2 导入核心依赖

5.3.3 application 配置文件

5.3.4 实体数据对象

5.3.5 jpa数据持久层

5.3.6 核心操作接口

5.3.7 常用复杂查询接口

5.4 MongoTemplate 方式整合使用

5.4.1 核心增删接口

5.4.2 复杂查询接口

5.4.3 聚合查询

5.4.4 聚合统计查询案例

六、结语


一、mongodb简介

1.1 什么是mongodb

MongoDB是一个文档数据库(以 JSON 为数据模型),由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。

1.2 mongodb特点

  • MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的;
  • 它支持的数据结构非常松散,数据格式为BSON,一种类似JSON的二进制形式的存储格
    式,简称Binary JSON ,和JSON一样支持内嵌的文档对象和数组对象,因此可以存储比较复杂的数据类型;
  • MongoDB最大特点是支持的查询语言非常强大,语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引;
  • 原则上 Oracle 和MySQL 能做的事情,MongoDB 都能做(包括 ACID 事务);

二、mongodb中的核心术语

在正式学习mogodb之前,有必要对mogodb中的基本术语和相关的语法做一个简单了解,就像学习mysql必须掌握其基本语法、DDL、DML等一样的道理,当然学习mogodb时可以类比mysql中的数据库,表和字段进行理解。

2.1 mogodb与数据库对比

MongoDB  概念与关系型数据库( RDBMS )非常类似,见下表;

2.2 mongodb中的核心概念

关于上述表中关于mongodb各个术语,再做简单的补充说明:

数据库(database)

最外层的概念,可以理解为逻辑上的名称空间,一个数据库包含多个不同名称的集合

集合(collection)

相当于SQL中的表,一个集合可以存放多个不同的文档

文档(document)

一个文档相当于数据表中的一行,由多个不同的字段组成

字段(field)

文档中的一个属性,等同于列(column) 

索引(index)

独立的检索式数据结构,与SQL概念一致 

id

每个文档中都拥有一个唯一的id字段,相当于SQL中的主键(primary key) 

视图(view)

可以看作一种虚拟的(非真实存在的)集合,与SQL中的视图类似。从MongoDB3.4版本开始提供了视图功能,其通过聚合管道技术实现 

聚合操作($lookup)

MongoDB用于实现“类似”表连接(tablejoin)的聚合操作符 

2.3 与关系数据库的差异

尽管这些概念大多与SQL标准定义类似,但MongoDB与传统RDBMS仍然存在不少差异,包括:

2.3.1 半结构化

半结构化,在一个集合中,文档所拥有的字段并不需要是相同的,而且也不需要对所用的字段进行声明,因此,MongoDB具有很明显的半结构化特点。

2.3.2 支持多级嵌套

除了松散的表结构,文档还可以支持多级的嵌套、数组等灵活的数据类型,非常契合面向对象的编程模型。

2.3.3 关系弱化

弱关系,MongoDB没有外键的约束,也没有非常强大的表连接能力。类似的功能需要使用聚合管道技术来弥补。 

三、mongodb 技术优势和应用场景

3.1 mongodb 技术优势

传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,逐渐开始显得吃力,对于数据库来说,尽管可以通过集群或其他方式对单节点的mysql实例进行扩展,但这样带来的成本和代价也是巨大的,由于mongodb从一开始就是为分布式而生,面对海量数据,高并发的场景有得天独厚的优势,同时其丰富的集群模式可以适应企业不同的数据运维和部署场景。

关于三高的解释补充

1、High performance - 对数据库高并发读写的需求;

2、Huge Storage - 对海量数据的高效率存储和访问的需求;

3、High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求;

3.2 mongodb 应用场景

从目前阿里云 MongoDB 云数据库上的用户看,MongoDB 的应用已经渗透到各个领域,这里总结如下:

游戏场景

使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新;

物流场景

使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以MongoDB 内嵌
数组的形式来存储,一次查询就能将订单所有的变更读取出来;

社交场景

使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实
现附近的人、地点等功能; 

物联网场景

使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析; 

大数据应用

使用云数据库MongoDB作为大数据的云存储系统,随时进行数据提取分析,掌握行业动态

3.3 什么时候选择 mongodb

在上述的应用场景中,数据操作方面有如下一些共同的特点:

1)数据量大;

2)写入操作频繁(读写可能都很频繁);

3)价值较低的数据,对事务性要求不高;

对于这样的数据,我们更适合使用MongoDB来实现数据的存储。在实际架构选型上,除了上述的三个特点外,如果你还犹豫是否要选择它?可以考虑以下的一些问题:

1、应用不需要事务及复杂 join 支持;
2、新应用,需求会变,数据模型无法确定,想快速迭代开发;
3、应用需要2000-3000以上的读写QPS(更高也可以);
4、应用需要TB甚至 PB 级别数据存储;
5、应用发展迅速,需要能快速水平扩展;
6、应用要求存储的数据不丢失;
7、应用需要99.999%高可用;
8、应用需要大量的地理位置查询、文本查询;

如果上述有1个符合,可以考虑 MongoDB,2个及以上的符合,选择 MongoDB肯定不会错。

四、快速部署 mongodb

为方便后面代码整合使用,下面通过docker快速部署起一个mongodb的服务,按照下面的操作步骤执行;

4.1 搭建过程

4.1.1 拉取镜像

docker pull mongo:4.4

4.2.2 启动镜像

在启动镜像之前先创建一个数据目录的映射

mkdir data

使用下面的命令启动镜像

docker run -itd --name mongo -v /usr/local/mongo/data:/data/db -p 27017:27017 mongo:4.4 --auth

参数说明:

1)-p 27017:27017,映射容器服务的 27017 端口到宿主机的 27017 端口,外部可以直接通过 宿主机 ip:27017 访问到 mongo 的服务;

2)--auth:需要密码才能访问容器服务;

4.2 创建账户

4.2.1 登录mongo容器,并进入到【admin】数据库

docker exec -it mongo mongo admin

4.2.2 创建一个用户

mongo 默认是没有用户的,这里root只是自己指定的用户名,可以自己命名

db.createUser({ user:'root',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},'readWriteAnyDatabase']});

参数说明:

  • 【user:‘root’ 】:设置用户名为root;
  • 【pwd:‘123456’】:设置密码为123456;
  • 【role:‘userAdminAnyDatabase’】:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限;
  • 【db: ‘admin’】:可操作的数据库;
  • 【‘readWriteAnyDatabase’】:赋予用户读写权限;

dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile

4.3 连接与测试

4.3.1连接mongo数据库

db.auth('root', '123456');

4.3.2 插入与查询数据

db.user.insert({"name":"zhangsan","age":18});
db.user.find();

4.3.3 使用客户端连接

使用客户端工具连接,如下图所示,在下面填写自己的连接信息即可;

五、整合springboot使用

5.1 前置说明

springboot与mongodb整合使用通常有两种方式,这两种方式在开发过程中结合实际需要都可以选择;

5.1.1 jpa方式整合

使用过spingboot的jpa操作mysql的同学应该不陌生,这种方式的好处很明显,就是jpa中针对常用的对于数据库的CRUD操作相关的API做了封装,开发者对于常用的增删改查功能开发起来效率很高,缺点是如果业务操作比较复杂,需要编写比较复杂的sql语句时,使用jpa就不太方便了,尽管jpa也可以通过注解编写部分类sql语句,但是语法还是有一定上手成本的。

5.1.2 MongoTemplate 方式整合

很多第三方组件都与springboot做了整合集成,比如像redis提供了redisTemplate,kafka集成springboot时提供了kafkaTemplate等类似,mongodb也提供了MongoTemplate ,MongoTemplate 提供了满足日常开发需要的丰富的API,基本上涵盖了大部分的场景需求,学习成本较低,网上可以参阅的资料也比较丰富。可以作为第一选择。

为了更好的满足日常开发中的需要,下面将这两种方式的使用做一个详细的介绍。

5.2 准备一个数据库

为了方便后文中代码的测试,基于上面已经搭建完成的mongodb服务,使用root账户登录进去之后,创建一个名为 book的collection集合,创建也比较简单,使用上面的root用户登录进客户端之后,直接输入下面的命令即可创建;

use 你的数据库名称

5.3 jpa方式整合使用

5.3.1 创建一个springboot的工程

创建一个空的springboot工程,目录结构如下:

5.3.2 导入核心依赖

为了接口测试方便,引入了swagger的相关依赖;

    <dependencies>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <!--swagger API获取-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>

        <!--swagger-ui API获取-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

    </dependencies>

5.3.3 application 配置文件

application.yml中添加如下配置

spring:
  data:
    mongodb:
      uri: mongodb://root:123456@IP地址:27017/book?authSource=admin&authMechanism=SCRAM-SHA-1

server:
  port: 8082

5.3.4 实体数据对象

创建一个Book类,作为与mongo中的book这个collection集合的映射,对象中的属于对应着数据库中的collection的各个字段;

@Document(collection="book")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookInfo {
    @Id
    private String id;

    @Field("name")
    private String name;

    @Field("price")
    private Integer price;

    @Field("publish_time")
    private String publishTime;

    @Field("comment")
    private String comment;

}

5.3.5 jpa数据持久层

添加一个与mongodb集合交互的jpa接口,继承MongoRepository接口,使用过jpa的同学对此应该不陌生;

public interface BookRepository  extends MongoRepository<BookInfo,String> {

}

5.3.6 核心操作接口

为方便测试,创建一个接口类,由于基本的增删改查逻辑相对比较简单,直接调用MongoRepository即可,下面贴出基本的增删改查接口

    @Autowired
    private BookService bookService;
	
	@PostMapping("/save")
    public String save(BookInfo bookInfo) {
        return bookService.saveBook(bookInfo);
    }

    @PostMapping("/update")
    public String update(BookInfo bookInfo) {
        return bookService.update(bookInfo);
    }

    @GetMapping("/delete")
    public String deleteById(String id) {
        return bookService.deleteById(id);
    }

    @PostMapping("/detail")
    public BookInfo findById(String id) {
        return bookService.findById(id);
    }

业务实现

    @Autowired
    private BookRepository bookRepository;

    /**
     * 保存
     * @param bookInfo
     * @return
     */
    public String saveBook(BookInfo bookInfo){
        bookInfo.setId(IdUtil.generateId());
        bookRepository.save(bookInfo);
        return "save book info success";
    }

    /**
     * 修改
     * @param bookInfo
     * @return
     */
    public String update(BookInfo bookInfo) {
        if(StringUtils.isEmpty(bookInfo.getId())){
            throw new RuntimeException("ID不能为空");
        }
        bookRepository.save(bookInfo);
        return "save book info success";
    }

    /**
     * 根据ID删除
     * @param id
     * @return
     */
    public String deleteById(String id) {
        bookRepository.deleteById(id);
        return "delete success";
    }

    /**
     * 查询所有
     * @return
     */
    public List<BookInfo> findList() {
        return bookRepository.findAll();
    }

    /**
     * 根据ID获取
     * @param id
     * @return
     */
    public BookInfo findById(String id) {
        Optional<BookInfo> bookInfoOptional = bookRepository.findById(id);
        return bookInfoOptional.isPresent() ? bookInfoOptional.get() : null;
    }

以查询详情接口为例,在swagger中做一下测试,效果如下:

5.3.7 常用复杂查询接口

相信很多项目使用mongodb的一个重要的原因就是使用mongo进行查询时性能很高,尤其是针对一些复杂的查询场景时优势很明显,下面结合开发中其他比较常用的几个查询场景做一下补充;

模糊查询

主要用到了ExampleMatcher这个匹配对象,类似于mysql的: like '%关键字%';

    /**
     * 模糊查询
     * @param name
     * @return
     */
    public List<BookInfo> query(String name) {
        //构建匹配器对象
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //设置默认字符串匹配方式:模糊查询
                .withIgnoreCase(true);      //设置默认大小写忽略方式:true为忽略大小写
        BookInfo bookInfo = new BookInfo();
        bookInfo.setName(name);
        Example<BookInfo> bookInfoExampleExample = Example.of(bookInfo,matcher);
        List<BookInfo> bookInfos = bookRepository.findAll(bookInfoExampleExample);
        return bookInfos;
    }

分页查询

public Page<BookInfo> pageQuery(Integer pageNo,Integer pageSize,String name) {

        //构造排序器,设置排序字段
        Sort sort = Sort.by(Sort.Direction.ASC,"price");
        Pageable pageable = null;
        if(Objects.isNull(pageNo) || Objects.isNull(pageSize)){
            pageNo = 0;
            pageSize=10;
        }
        pageable = PageRequest.of(pageNo, pageSize, sort);

        //创建匹配器,设置查询条件
        ExampleMatcher matcher = ExampleMatcher.matching() //构建匹配器对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //设置默认字符串匹配方式:模糊查询
                .withIgnoreCase(true); //设置默认大小写忽略方式:true为忽略大小写

        //设置查询条件
        BookInfo bookInfo = new BookInfo();
        bookInfo.setName(name);
        Example<BookInfo> bookExample = Example.of(bookInfo,matcher);
        //查询
        Page<BookInfo> page = bookRepository.findAll(bookExample, pageable);
        return page;
    }

sql方式查询

某些情况下可能已有的API并不能很好的满足,就需要通过编写sql的方式实现了,在使用jpa操作mysql的时候俗称hql语言,关于使用MongoRepository编写类sql的相信资料可以参阅相关资料,网上可以搜到很多,其核心语法仍然是mongodb自身的那些操作语句,只是需要遵照Java中的编写规范;

下面提供了几个常用的查询,提供参考

public interface BookRepository  extends MongoRepository<BookInfo,String> {
	
	//根据名称查询
    @Query("{name: ?0}")
    List<BookInfo> findByBookName(String name);

	//查询价格大于某个数的集合
    @Query(value = "{price: { $gt: ?0 }}")
    List<BookInfo> findByBookPriceThan(int price);
	
	//查询ID在某个集合中,类似mysql 的 in ()语法
    @Query(value = "{'_id':{$in:?0}}")
    List<BookInfo> findAllByIdIn(List<String> ids);
}

5.4 MongoTemplate 方式整合使用

MongoTemplate相对于MongoRepository来说,从API的使用上来说,选择更丰富,编码也更友好,同时API的使用也更加符合编码的习惯,下面再使用MongoTemplate 的方式做一下操作演示

配置文件等信息暂时不用修改

5.4.1 核心增删接口

接口层

@RestController
@RequestMapping("/template")
public class BookTemplateController {

    @Autowired
    private BookTemplateService bookTemplateService;

    @PostMapping("/save")
    public BookInfo save(BookInfo bookInfo){
        return bookTemplateService.save(bookInfo);
    }

    @PostMapping("/update")
    public BookInfo update(BookInfo bookInfo){
        return bookTemplateService.update(bookInfo);
    }

    @GetMapping("/delete")
    public String delete(String id){
        return bookTemplateService.delete(id);
    }

    @GetMapping("/details")
    public BookInfo details(String id){
        return bookTemplateService.findById(id);
    }

}

业务实现

@Service
public class BookTemplateService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public BookInfo save(BookInfo bookInfo) {
        bookInfo.setId(IdUtil.generateId());
        BookInfo book = mongoTemplate.save(bookInfo);
        return book;
    }

    public BookInfo update(BookInfo bookInfo) {
        if(StringUtils.isEmpty(bookInfo.getId())){
            throw new RuntimeException("ID为空");
        }
        Query query = Query.query(Criteria.where("_id").is(bookInfo.getId()));
        BookInfo dbBook = mongoTemplate.findOne(query, BookInfo.class);
        if(Objects.isNull(dbBook)){
            return null;
        }
        Update update = new Update();
        if(!StringUtils.isEmpty(bookInfo.getName())){
            update.set("name",bookInfo.getName());
        }
        if(!StringUtils.isEmpty(bookInfo.getComment())){
            update.set("comment",bookInfo.getComment());
        }
        if(Objects.nonNull(bookInfo.getPrice())){
            update.set("price",bookInfo.getPrice());
        }
        mongoTemplate.updateFirst(query,update,BookInfo.class);
        return bookInfo;
    }

    public String delete(String id) {
        Query query = new Query(Criteria.where("_id").is(id));
        mongoTemplate.remove(query,BookInfo.class);
        return "deleted";
    }

    public BookInfo findById(String id) {
        BookInfo bookInfo = mongoTemplate.findById(id, BookInfo.class);
        return bookInfo;
    }
}

5.4.2 复杂查询接口

在实际业务开发中,更多的情况下会用到mongodb较复杂的查询,下面列举一些常用的复杂查询的场景提供参考

json字符串方式查询

如果在API调用过程中觉得书写不习惯的话,也支持原生的json语句查询,即将在客户端命令行中的查询语句转为json作为一个完整的语句进行查询

    public List<BookInfo> queryByJson(String name,String type,Integer price) {
        //String json1 = "{name:'" + name +"'}";
        String json2 = "{$or:[{price:{$gt: '" +price+"'}},{type: '"+type+"'}]}";
        Query query = new BasicQuery(json2);
        //查询结果
        List<BookInfo> employees = mongoTemplate.find(query, BookInfo.class);
        return employees;
    }

模糊查询

类似于mysql中的like

    //模糊查询
    public List<BookInfo> queryLike(String key){
        Query query = new Query(Criteria.where("name").regex(key));
        List<BookInfo> bookInfos = mongoTemplate.find(query, BookInfo.class);
        return bookInfos;
    }

范围查询

    //查询价格大于某个值
    public List<BookInfo> queryMoreThan(Integer price){
        Query query = new Query(Criteria.where("price").gte(price));
        List<BookInfo> bookInfos = mongoTemplate.find(query, BookInfo.class);
        return bookInfos;
    }

多条件查询

类似于mysql中的多个条件通过and的查询

    //多条件查询
    public List<BookInfo> queryMultiParams(String name,String type,Integer price){
        //or 条件查询
        Criteria criteria = new Criteria();
        /*criteria.orOperator(
                Criteria.where("name").regex(name),
                Criteria.where("price").gte(price),
                Criteria.where("type").is(type)
        );*/

        //and 条件查询
        criteria.andOperator(
                Criteria.where("price").gte(price),
                Criteria.where("type").is(type)
        );

        Query query = new Query(criteria);
        List<BookInfo> bookInfos = mongoTemplate.find(query, BookInfo.class);
        return bookInfos;
    }

分页查询

    //分页查询
    public PageInfo<BookInfo> pageQuery(Integer pageIndex,Integer pageSize,String type){
        Criteria criteria = new Criteria();
        if(!StringUtils.isEmpty(type)){
            criteria = Criteria.where("price").gte(type);
        }
        //TODO 如果有更多的查询条件,继续拼接 ...
        Query query = new Query(criteria);
        if(Objects.isNull(pageIndex) || Objects.isNull(pageSize)){
            pageIndex = 1;
            pageSize = 2;
        }

        //查询总数
        long total = mongoTemplate.count(query, BookInfo.class);
        System.out.println(total);

        //查询结果根据价格排个序
        query.with(Sort.by(Sort.Order.desc("price")))
                .skip((pageIndex-1) * pageSize) //指定跳过记录数
                .limit(pageSize); //每页显示记录数

        List<BookInfo> bookInfos = mongoTemplate.find(query, BookInfo.class);

        PageInfo<BookInfo> pageInfo = new PageInfo(pageIndex,pageSize,total,bookInfos);
        return pageInfo;
    }

5.4.3 聚合查询

聚合操作处理数据记录并返回计算结果(诸如统计平均值,求和等)。聚合操作组值来自多个文档,可以对分组数据执行各种操作以返回单个结果。聚合操作包含三类:单一作用聚合、聚合管道、MapReduce。

单一作用聚合

提供了对常见聚合过程的简单访问,操作都从单个集合聚合文档;

聚合管道

聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念。文档进入多级管道,将文档转换为聚合结果;

MapReduce

MapReduce操作具有两个阶段:处理每个文档并向每个输入文档发射一个或多个对象的map阶段,以及reduce组合map操作的输出阶段。

5.4.4 聚合统计查询案例

关于分组聚合统计的内容比较多,限于篇幅这里不做展开,我们将book这个collection集合的字段进行扩展(为了进行分组聚合使用),并插入一些数据,如下所示

下面我们要实现的需求是,

按照book的type字段进行分组,

1)统计每个type类型下的book的阅读总数,平均阅读数;

2)统计每个type类型下的book的喜欢总数,平均喜欢的数量;

完整的代码如下

    public void groupQuery(){
        // 查询条件
        Criteria criteria = new Criteria();
        MatchOperation matchOperation = Aggregation.match(criteria);

        // 查询包括的字段
        ProjectionOperation projectionOperation = Aggregation.project("id", "name","price", "type","readCount","likeCount");

        // 分组统计
        GroupOperation groupOperation = Aggregation.group("type")
                //.first("type").as("type")
                .count().as("typeCount")
                .sum("likeCount").as("totalLike")
                .avg("likeCount").as("avgLikeCount")
                .sum("readCount").as("totalReadCount")
                .sum("readCount").as("avgReadCount");

        AggregationResults<Map> totalAuthorResult = mongoTemplate.aggregate(Aggregation.newAggregation(BookInfo.class,
                matchOperation, projectionOperation, groupOperation), Map.class);

        //获取分类总数
        int typeCount = (int) totalAuthorResult.getMappedResults().size();
        System.out.println(typeCount);

        //得到最终分组聚合的结果
        List<Map> mappedResults = totalAuthorResult.getMappedResults();

        for(Map map :mappedResults){
            System.out.println(map.keySet());
        }

    }

通过debug,可以看到查询得到的结果如下

如果你需要获取最终的各个分组的统计结果,只需遍历上述的结果集即可。

六、结语

关于mongodb的技术体系是比较庞大的,本文只是冰山一角,要深入学习和掌握mongodb还需要实际开发过程中通过经验的积累才能加深对mongodb的理解和运用,随着技术的发展,mongodb在越来越多的领域都有着不错的运用,因此感兴趣的同学可以在此基础上深入研究。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C#】并行编程实战:并行编程中的模式

本章将介绍并行编程模式&#xff0c;重点是理解并行代码问题场景并使用并行编程/异步技术解决他们。本章会介绍几种最重要的编程模式。 本教程学习工程&#xff1a;魔术师Dix / HandsOnParallelProgramming GitCode 1、MapReduce 模式 引入 MapReduce 是为了解决处理大数据的问…

睡眠健康数据分析

项目背景 背景描述 本数据集涵盖了与睡眠和日常习惯有关的诸多变量。如性别、年龄、职业、睡眠时间、睡眠质量、身体活动水平、压力水平、BMI类别、血压、心率、每日步数、以及是否有睡眠障碍等细节。 数据集的主要特征&#xff1a; 综合睡眠指标&#xff1a; 探索睡眠持续时…

UNISOT让食品欺诈无处遁形

​​发表时间&#xff1a;2023年5月11日 全世界的消费者开始越来越关注食物的来源和采购方式。这是因为人们渴望吃得更健康、更用心&#xff0c;同时人们也认识到了购买可持续且合乎伦理道德的产品的必要性。 近年来&#xff0c;人们对食品溯源的渴望进一步加速&#xff0c;原…

Python读取csv、Excel文件生成图表

简介 本文章介绍了通过读取 csv 或 Excel 文件内容&#xff0c;将其转换为折线图或柱状图的方法&#xff0c;并写入 html 文件中。 目录 1. 读取CSV文件 1.1. 生成折线图 1.1.1. 简单生成图表 1.1.2. 设置折线图格式 1.2. 生成柱状图 1.2.1. 简单生成图表 1.2.2. 设置柱…

文章详情页 - 评论功能的实现

目录 1. 准备工作 1.1 创建评论表 1.2 创建评论实体类 1.3 创建 mapper 层评论接口和对应的 xml 实现 1.4 准备评论的 service 层 1.5 准备评论的 controller 层 2. 总的初始化详情页 2.1 加载评论列表 2.1.1 实现前端代码 2.1.2 实现后端代码 2.2 查询当前登录用户的…

Docker安装es以及ik分词器

1、拉取镜像 docker pull elasticsearch:7.10.12、下载对应版本的ik分词、并将它们解压到ik文件夹下&#xff0c;如图 https://github.com/medcl/elasticsearch-analysis-ik/releases 3、在服务器上创建文件夹 mkdir /usr/elklog/elk/es mkdir /usr/elklog/elk/es/data mkdi…

【C进阶】回调函数(指针进阶2,详解,小白必看)

目录 6. 函数指针数组 6.1简单计算器 6.2函数指针数组实现计算器 7. 指向函数指针数组的指针(仅作了解即可) 8.回调函数 8.1关于回调函数的理解​编辑 8.1.1用回调函数改良简单计算器 8.2qsort库函数的使用 8.2.1冒泡排序 8.2.2qsort的概念 8.3冒泡排序思想实现qsor…

docker存储空间报错解决(谨慎操作,会影响原来的容易镜像,不熟练切勿操作)

报错内容 [rootDream package]# docker build -t imapp . [] Building 21.0s (6/19)> [internal] load build definition from Dockerfile 0.1s> > transferring …

TCP 三次握手四次挥手浅析

大家都知道传输层中的TCP协议是面向连接的&#xff0c;提供可靠的连接服务&#xff0c;其中最出名的就是三次握手和四次挥手。 一、三次握手 三次握手的交互过程如下 喜欢钻牛角尖的我在学习三次握手的时候就想到了几个问题&#xff1a;为什么三次握手是三次&#xff1f;不是…

AnimateDiff论文解读-基于Stable Diffusion文生图模型生成动画

文章目录 1. 摘要2. 引言3. 算法3.1 Preliminaries3.2. Personalized Animation3.3 Motion Modeling Module 4. 实验5.限制6. 结论 论文&#xff1a; 《AnimateDiff: Animate Your Personalized Text-to-Image Diffusion Models without Specific Tuning》 github: https://g…

销售易和管易云接口打通对接实战

销售易和管易云接口打通对接实战 来源系统:销售易 销售易CRM支持企业从营销、销售到服务的全流程自动化业务场景&#xff0c;创新性地利用AI、大数据、物联网等新型互联网技术打造双中台型CRM&#xff1b;既能帮助B2B企业连接外部经销商、服务商、产品以及最终用户&#xff0c;…

支持多种通信方式和协议方便接入第三方服务器或云平台

2路RS485串口是一种常用的通信接口&#xff0c;可以支持Modbus Slave协议&#xff0c;并可接入SCADA、HMI、DSC、PLC等上位机。它还支持Modbus RTU Master协议&#xff0c;可用于扩展多达48个Modbus Slave设备&#xff0c;如Modbus RTU远程数据采集模块、电表、水表、柴油发电机…

【Rust学习 | 基础系列3 | Hello, Rust】编写并运行第一个Rust程序

文章目录 前言一&#xff0c;创建项目二&#xff0c;两种编译方式1. 使用rustc编译器编译2. 使用Cargo编译 总结 前言 在开始学习任何一门新的编程语言时&#xff0c;都会从编写一个简单的 “Hello, World!” 程序开始。在这一章节中&#xff0c;将会介绍如何在Rust中编写并运…

强推!大语言模型『百宝书』,一文缕清所有大模型!

夕小瑶科技说 原创 作者 | 王思若 最近&#xff0c;大型语言模型无疑是AI社区关注的焦点&#xff0c;各大科技公司和研究机构发布的大模型如同过江之鲫&#xff0c;层出不穷又眼花缭乱。 让笔者恍惚间似乎又回到了2020年国内大模型“军备竞赛”的元年&#xff0c;不过那时候…

DSA之图(4):图的应用

文章目录 0 图的应用1 生成树1.1 无向图的生成树1.2 最小生成树1.2.1 构造最小生成树1.2.2 Prim算法构造最小生成树1.2.3 Kruskal算法构造最小生成树1.2.4 两种算法的比较 1.3 最短路径1.3.1 两点间最短路径1.3.2 某源点到其他各点最短路径1.3.3 Dijkstra1.3.4 Floyd 1.4 拓扑排…

【前端知识】React 基础巩固(三十六)——RTK中的异步操作

React 基础巩固(三十六)——RTK中的异步操作 一、RTK中使用异步操作 引入RTK中的createAsyncThunk&#xff0c;在extraReducers中监听执行状态 import { createSlice, createAsyncThunk } from "reduxjs/toolkit"; import axios from "axios";export cons…

第七篇:k8s集群使用helm3安装Prometheus Operator

安装Prometheus Operator 目前网上主要有两种安装方式&#xff0c;分别为&#xff1a;1. 使用kubectl基于manifest进行安装 2. 基于helm3进行安装。第一种方式比较繁琐&#xff0c;需要手动配置yaml文件&#xff0c;特别是需要配置pvc相关内容时&#xff0c;涉及到的yaml文件太…

程序员做项目必用的工具【更新中...】

每个程序员多多少少都会有自己简化项目的小工具&#xff0c;我采访了我们公司所有的工程师总结了程序员必备工具篇。 一.unisms 官网&#xff1a;https://unisms.apistd.com/ 不会有人这年头写注册登录还是自己写验证码模块吧&#xff1f; 你该得拥有一个短信验证码平台了&…

【GUI】基于开关李雅普诺夫函数的非线性系统稳定(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

pytest 入门

1,安装pytest 打开终端或命令提示符窗口,在终端中运行以下命令来安装pytest: pip install pytestpip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest 确保您的系统上已经安装了Python。您可以在终端中运行以下命令来检查Python的安装情况: pytest --version…