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

news2024/11/17 19:35:59

目录

一、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在越来越多的领域都有着不错的运用,因此感兴趣的同学可以在此基础上深入研究。

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

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

相关文章

redis主从复制哨兵Cluster

目录 前言 一、模式介绍 1.1 主从复制 1.2 哨兵 1.3 集群 二、主从复制 2.1 主从复制的作用 2.2 主从复制流程 2.3 搭建Redis 主从复制 三、Redis 哨兵模式 3.1 哨兵模式原理 3.2 哨兵模式的作用 3.3 哨兵组成结构 3.4 哨兵故障转移机制 3.5 搭建Redis 哨兵模式…

利用频谱仪进行简单的2.4G 频率测试

一、概述 1. 信号源 我们开发2.4G 无线产品的时候&#xff0c;经常需要对产品的无线信号进行测试&#xff0c;以确定精确的频率。在进行频率测试之前&#xff0c;我们的2.4G 射频芯片需要进入单载波模式。 2. 频谱仪 这里选择的是普源的频谱仪。测试范围是 9kHz - 3.2GHz。…

【工具篇】Lombok 介绍及使用(详细教程)

Lombok 介绍及使用 一&#xff0c;Lombok介绍 在 Java 开发中&#xff0c;常常需要编写大量的getter、setter方法、equals和hashCode方法、构造函数等重复且繁琐的代码。 为了减少 Java 代码中的冗余和样板代码&#xff0c;提高代码的可读性和开发效率&#xff0c;就有了Lomb…

数据结构: 线性表(无哨兵位单链表实现)

文章目录 1. 线性表的链式表示: 链表1.1 顺序表的优缺点1.2 链表的概念1.3 链表的优缺点1.4 链表的结构 2. 单链表的定义2.1 单链表的结构体2.2 接口函数 3. 接口函数的实现3.1 动态申请一个结点 (BuySListNode)3.2 单链表打印 (SListPrint)3.3 单链表尾插 (SListPushBack)3.4 …

【雕爷学编程】MicroPython动手做(20)——掌控板之三轴加速度6

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

【LangChain】向量存储(Vector stores)

概要 存储和搜索非结构化数据的最常见方法之一是嵌入它并存储生成的嵌入向量&#xff0c;然后在查询时嵌入非结构化查询并检索与嵌入查询“最相似”的嵌入向量。向量存储负责存储嵌入数据并为您执行向量搜索。 内容 本篇讲述与 VectorStore 相关的基本功能。使用向量存储的一…

【Git】远程仓库的创建、SSH协议克隆、拉取、推送

目录 一、创建远程仓库 二、HTTPS协议克隆仓库 三、SSH协议克隆仓库 四、向远程仓库推送 五、从远程仓库拉取 六、忽略特殊文件 七、配置命令别名 一、创建远程仓库 首先我们可以从GitHub或者Gitee中创建自己的个人仓库 工作台 - Gitee.comhttps://gitee.com/ 二、HTT…

Sestra 实用教程(三)输入与输出

目 录 一、前言二、分析流程三、输入文件3.1 模型与荷载3.2 分析控制数据 四、输出文件五、参考文献 一、前言 SESAM &#xff08;Super Element Structure Analysis Module&#xff09;是由挪威船级社&#xff08;DNV-GL&#xff09;开发的一款有限元分析&#xff08;FEA&…

关于在VS2017中编译Qt项目遇到的问题

关于在VS2017中编译Qt项目遇到的问题 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。如何在VS2017里部署的Qt Designer上编辑槽函数 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。 链接 如何在VS2017里部署的Qt Design…

【LeetCode】解码方法

这里写目录标题 题目描述算法流程编程代码代码优化 链接: 解码方法 题目描述 算法流程 编程代码 class Solution { public:int numDecodings(string s) {int n s.size();vector<int> dp(n);dp[0] s[0] ! 0;if(n 1) return dp[0];if(s[1] < 9 && s[1] >…

python之编写form表单提交到后端

一、环境配置 我们先去python的框架中下载Flask&#xff0c;具体的配置我给大家找了一篇博客讲解&#xff0c;环境调试没问题后&#xff0c;开始我们form表单提交的过程 Python之flask框架_python flask_【网络星空】的博客-CSDN博客 二、前端代码 在VScode里编写前端的代码为…

移动端加入购物车界面设计

效果图 源码如下 页面设计 <template><div class"container"><!--商品详情 start--><van-image class"goods-item-image" :src"goods.goodsHeadImg"></van-image><div class"goods-price">&…

【安装vue脚手架报错:npm install -g @vue-cli pm ERR! code EINVALIDTAGNAME 】

当我们执行npm install -g vue-cli时候会报错&#xff1a; npm ERR! Invalid tag name “vue-cli” of package “vue-cli”: Tags may not have any characters that encodeURIComponent encodes. npm ERR! A complete log of this run can be found in: /Users/wuwenlu/.npm/…

【MySQL】MySQL索引、事务、用户管理

20岁的男生穷困潦倒&#xff0c;20岁的女生风华正茂&#xff0c;没有人会一直风华正茂&#xff0c;也没有人会一直穷困潦倒… 文章目录 一、MySQL索引特性&#xff08;重点&#xff09;1.磁盘、OS、MySQL&#xff0c;在进行数据IO时三者的关系2.索引的理解3.聚簇索引&#xff0…

仿找靓机链接生成 独立后台管理

教程&#xff1a;修改数据库账号密码直接使用。 源码带有教程! 下载程序&#xff1a;https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

软考A计划-系统集成项目管理工程师-项目采购管理-下

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

《Java面向对象程序设计》学习笔记

最近因考研专业课&#xff0c;在学习耿祥义老师的《Java面向对象程序设计&#xff08;第3版&#xff09;-微课视频版》 就打算放一些自己的学习笔记&#xff0c;可能不会及时更新&#xff0c;见谅。 计划弄个专栏&#xff0c;书上的每章对应专栏里的一篇文章。 专栏&#xf…

【并发专题】深入理解并发可见性、有序性、原子性与JMM内存模型

目录 课程内容一、JMM模型1.什么是JMM模型2.JMM内存区域模型3.JMM内存模型与硬件内存架构的关系4.JMM存在的必要性5.数据同步八大原子操作6.指令重排现象与并发编程的可见性&#xff0c;原子性与有序性问题 学习总结 课程内容 一、JMM模型 1.什么是JMM模型 Java内存模型&…

eda、gnm、anm究竟是个啥?

安装prody pip install prody -i https://pypi.tuna.tsinghua.edu.cn/simpleeda、anm、gnm eda(essential dynamics analysis) 另一个名字PCA(Principal Component Analysis) 或 NMA(Normal Mode Analysis)。 eda分析可以帮助人们理解生物大分子基本的运动模式和构象变化。…

【JavaSE】Java方法的使用

【本节目标】 1. 掌握方法的定义以及使用 2. 掌握方法传参 3. 掌握方法重载 4. 掌握递归 目录 1.方法概念及使用 1.1什么是方法(method) 1.2 方法定义 1.3 方法调用的执行过程 1.4 实参和形参的关系 2. 方法重载 2.1 为什么需要方法重载 2.2 方法重载概念 3. 递归 3.…