• mongodb整合springbootQ


    SpringBoot整合MongoDB_一个冬天的童话的博客-CSDN博客_mongodb的依赖SpringBoot整合MongoDB的过程https://blog.csdn.net/m0_53563908/article/details/1268980981,环境配置

    1.引入依赖

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-data-mongodbartifactId>
    4. dependency>

    2.配置yml

    1. spring:
    2. data:
    3. mongodb:
    4. uri: mongodb://localhost:27017/test?authSource=admin

    3.使用时注入mongoTemplate

    1. @Autowired
    2. private MongoTemplate mongoTemplate;

    2 集合操作

    1. package com.example.mongodb01;
    2. import org.junit.jupiter.api.Test;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.boot.test.context.SpringBootTest;
    5. import org.springframework.data.mongodb.core.MongoTemplate;
    6. @SpringBootTest
    7. class Mongodb01ApplicationTests {
    8. @Autowired
    9. MongoTemplate mongoTemplate;
    10. /*
    11. * 创建集合
    12. * */
    13. @Test
    14. public void testCreateCollection(){
    15. boolean emp = mongoTemplate.collectionExists("emp");
    16. if(emp){
    17. //删除集合
    18. mongoTemplate.dropCollection("emp");
    19. }
    20. //创建集合
    21. mongoTemplate.createCollection("emp");
    22. }
    23. }

    3.文档操作

    相关注解

     创建实体

    1. package com.example.mongodb01.entity;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import org.springframework.data.annotation.Id;
    6. import org.springframework.data.mongodb.core.mapping.Document;
    7. import org.springframework.data.mongodb.core.mapping.Field;
    8. import java.util.Date;
    9. /**
    10. * @ProjectName: mongodb01
    11. * @packageName: com.example.mongodb01.entity
    12. * @author: xmhz45
    13. * @create: 2022/12/3 18:37
    14. */
    15. @Document("emp")//对应emp集合中的一个文档
    16. @Data
    17. @AllArgsConstructor
    18. @NoArgsConstructor
    19. public class EmpLoyee {
    20. @Id //映射文档中的_id
    21. private Integer id;
    22. @Field("username")
    23. private String name;
    24. @Field
    25. private int age;
    26. @Field
    27. private Double salary;
    28. @Field
    29. private Date birthday;
    30. }

    添加文档

    insert方法返回值是新增的Document对象,里面包含了新增后id的值。如果集合不存在会自动创建集合通过Spring Data MongoDB会给集合中多加一个class的属性,存储新增时Document对应)ava中类的全限定路径,这么做为了查询时能把Document转换为Java类型。

    1. /**
    2. * 添加文档
    3. */
    4. @Test
    5. public void testInsert(){
    6. EmpLoyee empLoyee = new EmpLoyee(1,"小王",30,10000.0,new Date());
    7. System.out.println("1");
    8. //添加文档
    9. //save: _id存在时更新数据
    10. //mongoTemplate.save(employee);
    11. //insert: _id存在抛出异常 支持批量操作
    12. mongoTemplate.insert(empLoyee);
    13. List list = Arrays.asList(
    14. new EmpLoyee(2,"张三1",21,5000.0,new Date()),
    15. new EmpLoyee(3,"张三2",22,6000.0,new Date()),
    16. new EmpLoyee(4,"张三3",23,7000.0,new Date())
    17. );
    18. //插入多条数据
    19. mongoTemplate.insert(list,EmpLoyee.class);
    20. }

    查询文档

    1. @Test
    2. public void testFind(){
    3. System.out.println("===========查看所有文档===========");
    4. //查看所有文档
    5. List list = mongoTemplate.findAll(EmpLoyee.class);
    6. list.forEach(System.out::println);
    7. System.out.println("===========findOne返回第一个文档===========");
    8. //如果查询结果是多个,返回其中第一个文档对象
    9. EmpLoyee one = mongoTemplate.findOne(new Query(), EmpLoyee.class);
    10. System.out.println(one);
    11. System.out.println("===========根据_id查询===========");
    12. EmpLoyee e = mongoTemplate.findById(1, EmpLoyee.class);
    13. System.out.println(e);
    14. System.out.println("===========条件查询===========");
    15. //new Query() 表示没有条件
    16. //查询薪资大于等于8000的员工
    17. //Query query = new Query(Criteria.where("salary").gte(8000));
    18. //查询薪资大于4000小于10000的员工
    19. //Query query = new Query(Criteria.where("salary").where("salary").gt(4000).lt(10000));
    20. //正则查询(模糊查询) java中正则不需要有//
    21. //Query query = new Query(Criteria.where("name").regex("王"));
    22. //and or 多条件查询
    23. Criteria criteria = new Criteria();
    24. //and 查询年龄大于25&薪资大于8000的员工
    25. //criteria.andOperator(Criteria.where("age").gt(25),Criteria.where("salary").gt(8000));
    26. //or 查询姓名是张三或者薪资大于8000的员工
    27. criteria.orOperator(Criteria.where("name").is("张三1"),Criteria.where("salary").gt(8000));
    28. Query query = new Query(criteria);
    29. //sort排序
    30. //query.with(Sort.by(Sort.Order.desc("salary")));
    31. //skip limit 分页 skip用于指定跳过记录数 limit则用于限定返回结果数量
    32. query.with(Sort.by(Sort.Order.desc("salary")))
    33. .skip(0) //指定跳过记录数
    34. .limit(4); //每页显示记录数
    35. //查询结果
    36. List empLoyees = mongoTemplate.find(query,EmpLoyee.class);
    37. empLoyees.forEach(System.out::println);
    38. }

    使用json字符串格式查询

    更新文档

    在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受行数影响,如果更新后的结果和更新前的结果是相同,返回0。
    。updateFirst() 只更新满足条件的第一条记录
    。 updateMulti() 更新所有满足条件的记录
    。 upsert0 没有符合条件的记录则插入数据

    1. @Test
    2. public void testUpdate(){
    3. //query设置查询条件
    4. Query query = new Query(Criteria.where("salary").gte(7000));
    5. System.out.println("==========更新前==========");
    6. List empLoyees = mongoTemplate.find(query, EmpLoyee.class);
    7. empLoyees.forEach(System.out::println);
    8. Update update = new Update();
    9. //设置更新属性
    10. update.set("salary",18000);
    11. //updateFirst() 只更新满足条件的第一条记录
    12. //UpdateResult updateResult = mongoTemplate.updateFirst(query, update, EmpLoyee.class);
    13. //updateMulti() 更行所有满足条件的记录
    14. UpdateResult updateResult = mongoTemplate.updateMulti(query, update, EmpLoyee.class);
    15. //upsert() 没有符合条件的记录则插入数据
    16. //update.setOnInsert("id",11);//指定_id
    17. //UpdateResult updateResult = mongoTemplate.upsert(query, update, EmpLoyee.class);
    18. //返回修改的记录数
    19. System.out.println(updateResult.getModifiedCount());
    20. System.out.println("=============更新后============");
    21. empLoyees = mongoTemplate.find(query, EmpLoyee.class);
    22. empLoyees.forEach(System.out::println);
    23. }

     删除文档

    1. @Test
    2. public void testDelete(){
    3. //删除所有文档 不如用dropCollection()
    4. //mongoTemplate.remove(new Query(),EmpLoyee.class);
    5. //条件删除
    6. Query query = new Query(Criteria.where("salary").gte(10000));
    7. mongoTemplate.remove(query,EmpLoyee.class);
    8. }

    4.聚合操作

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

    • 单一作用聚合:提供了对常见聚合过程的简单访问,提作都从单个集合聚合文档.
    • 聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念。文档进入多级管道,将文档转换为聚合结果
    • MapReduce提作具有两个阶段:处理每个文档并向每个输入文档发射一个或多个对象的map阶段,以及reduce组合map操作的输出阶段。

    4.1单一作用聚合

    MongoDB提供 db.collection.estimatedDocumentCount()[忽略查询条件];db.collection.count(),db.collection.distinct() 这类单一作用的聚合函数。所有这些操作都聚合来自单个集合的文档,虽然这些操作提供了对公共聚合过程的简单访问,但它们缺乏聚合管道和map-Redue的灵活性和功能

     4.2 聚合管道

    什么是 MongoDB 聚合框架

    MongoDB 聚合框架 (Aggregation Framework) 是一个计算框架,它可以:

    • 作用在一个或几个集合上;
    • 对集合中的数据进行的一系列运算;
    • 将这些数据转化为期望的形式;

    从效果而言,聚合框架相当于 SQL 查询中的GROUP BY、 LEFT OUTER JOIN、 AS等

    管道 (Pipeline) 和阶段 (Stage)

    整个聚合运算过程称为管道 (Pipeline) ,它是由多个阶段 (Stage) 组成的,每个管道:

    • 接受一系列文档(原始数据) ;
    • 每个阶段对这些文档进行一系列运算;
    • 结果文档输出给下一个阶段;

     聚合管道操作语法

    pipeline = [$stage1, $stage2,...$stageN];

    db .collection .aggregate(pipeline,{options})

    • pipelines 一组数据聚合阶段。除$out、$Merge和$geonear阶段之外,每个阶段都可以在管道中出现多次。
    • options 可选,聚合操作的其他参数。包含: 查询计划、是否使用临时文件、游标、最大操作时间、读写策路、强制索引等等

     常用的管道聚合阶段

     数据准备

    准备数据集,执行脚本

    1. var tags = ["nosql", "mongodb" , "document" , "developer" , "popular"];
    2. var types = ["technology","sociality","travel","novel","literature"];
    3. var books=[];
    4. for(var i=0;i<50;i++){
    5. var typeIdx = Math.floor(Math.random()*types.length);
    6. var tagIdx = Math.floor(Math.random()*tags.length);
    7. var tagIdx2 = Math.floor(Math.random()*tags.length);
    8. var favCount = Math.floor(Math.random()*100);
    9. var username = "xx00"+Math.floor(Math.random()*10);
    10. var age = 20 + Math.floor(Math.random()*15);
    11. var book = {
    12. title:"book-"+i,
    13. type: types[typeIdx],
    14. tag: [tags[tagIdx],tags[tagIdx2]],
    15. favCount: favCount,
    16. author: {name :username , age :age}
    17. };
    18. books.push(book)
    19. }
    20. db.books.insertMany(books);
    db.books.find().pretty() 显示json格式

    $project


    投影操作,将原始字段投影成指定名称,如将集合中的 title 投影成 name

    db.books.aggregate([{$project: {name:"$title"}}])

    $proiect 可以灵活控制输出文档的格式,也可以剔除不需要的字段  0不显示,1显示,默认为0

    db.books.aggregate([{$project:{name:"$title",_id:0,type:1,author:1}}])

    从嵌套文档中排除字段

    db.books.aggregate([

            {$project:{name:"$title",_id:0,type:1,"author.name":1}}
    ])
    或者
    db.books.aggregate([
    {$project:{name:"$title",_id:0,type:1,author:{name:1}}}

    ])

    $match

    $match用于对文档进行筛选,之后可以在得到的文档子集上做聚合,$match可以使用除了地理空间之外的所有常规查询操作符,在实际应用中尽可能将$match放在管道的前面位置。这样有两个好处:一是可以快速将不需要的文档过滤掉,以减少管道的工作量;二是如果再投射和分组之前执行$match,查询可以使用索引。

    db.books.aggregate([{$match:{type:"technology"}}])

    db.books.aggregate([{$match:{type:"technology",title:/book-2/}}])

     

     筛选管道操作和其他管道操作配合时候时,尽量放到开始阶段,这样可以减少后续管道操作符要操作的文档数,提升效率

    $count
    计数并返回与查询匹配的结果数

    db.books.aggregate([{$match:{type:"technology"}},{$count: "type_count"}])

    $match阶段筛选出type匹配technology的文档,并传到下一阶段,

    $count阶段返回聚合管道中剩余文档的计数,并将该值分配给type_count

    $group


    按指定的表达式对文档进行分组,并将每个不同分组的文档输出到下一个阶段。输出文档包含一个

    _id字段,该字段按键包含不同的组。输出文档还可以包含计算字段,该字段保存由$group的_id字段分组的一些accumulator表达式的值。$group不会输出具体的文档而只是统计信息。

    { $group: { _id: , : { : },...}}

    • id字段是必填的;但是,可以指定id值为null来为整个输入文档计算累计值。
    • 剩余的计算字段是可选的,并使用运算符进行计算。
    •  _id和表达式可以接受任何有效的表达式。

    accumulator操作符

     $group阶段的内存限制为100M。默认情况下,如果stage超过此限制,$group将产生错误。但是,要允许处理大型数据集,请将allowDiskUse选项设置为true以启用$group操作以写入临时文件。

    book的数量,收藏总数和平均值

    db.books.aggregate([
    {$group:{_id:null ,count:{$sum:1},pop:{$sum:"$favCount"} ,avg:{$avg:"$favCount"}}}

    ])

     统计每个作者的book收藏总数

    db.books.aggregate([
            {$group:{_id:"$author.name",pop:{$sum:"$favCount"}}}

    ])

    统计每个作者的每本book的收藏数

    db.books .aggregate([
            {$group:{_id: {name:"{author.name",title:"$title"} ,pop:{$sum:"$favCount"}}}

    ])

    每个作者的book的type合集

    db.books.aggregate([
            {$group:{_id:"$author.name",types :{$addToSet:"$type"}}}

    ])

    $unwind

    可以将数组拆分为单独的文档
    v3.2+支持如下语法:

    {

            $unwind:
                    {

                         #要指定字段路径,在字段名称前加上$符并用引号括起来。

                            path: ,
                       #可选,一个新字段的名称用于存放元素的数组索引。该名称不能以$开头。                               includeArrayIndex: ,
                     #可选,default :false,若为true,如果路径为空,缺少或为空数组,则$unwind输出文档

                            preserveNullAndEmptyArrays :

    }}

    姓名为xx006的作者的book的tag数组拆分为多个文档

    db.books.aggregate([

            {$match:{"author.name":"xx006"}},

            {$unwind:"$tag"}

    ])

    每个作者的book的tag合集

    db.books.aggregate([
            {$unwind:"$tag"},
            {$group:{_id:"$author.name",types :{$addToset:"$tag"}}}

    ])

    案例
    示例数据

    1. db.books.insert([
    2. {
    3. "title":"book-51",
    4. "type":"technology",
    5. "favCount": 11,
    6. "tag":[],
    7. "author" : {
    8. "name": "fox",
    9. "age": 28
    10. }
    11. },{
    12. "title":"book-52",
    13. "type":"technology",
    14. "favCount": 15,
    15. "author" : {
    16. "name":"fox",
    17. "age": 28
    18. }
    19. },{
    20. "title":"book-53",
    21. "type":"technology",
    22. "tag":[
    23. "nosql",
    24. "document"
    25. ],
    26. "favCount":20,
    27. "author": {
    28. "name":"fox",
    29. "age":28
    30. }
    31. }])

    测试

    # 使用includeArrayIndex选项来输出数组元素的数组索引

    db.books.aggregate([

            {$match:{"author.name":"fox"}},

            {$unwind:{path:"$tag", includeArrayIndex: "arrayIndex"}}

    ])
    # 使用preserveNullAndEmptyArrays选项在输出中包含缺少size字段,null或空数组的文档

    db.books.aggregate([

            {$match:{"author.name":"fox"}},

            {$unwind: {path:"$tag",preserveNullAndEmptyArrays : true}}

    ])

    $limit

    限制传递到管道中下一阶段的文档数

    db.books.aggregate([
            {$limit : 5 }

    ])

    此操作仅返回管道传递给它的前5个文档。 $limit对其传递的文档内容没有影响。

    注意:当$sort在管道中的$limt之前立即出现时,$sort操作只会在过程中维持前n个结果,其中n是指定的限制,而MongoDB只需要将n个项存储在内存中。
    $skip

    跳过进入stage的指定数量的文档,并将其余文档传递到管道中的下一个阶段

    db.books.aggregate([
            {$skip : 5 }

    ])

    此操作将跳过管道传递给它的前5个文档。 $skip对沿着管道传递的文档的内容没有影响。
    $sort
    对所有输入文档进行排序,并按排序顺序将它们返回到管道。
    语法:

    { $sort: { : , : ... }}

    要对字段进行排序,请将排序顺序设置为1或-1,以分别指定升序或降序排序,如下例所示:

    db.books.aggregate([

            {$sort : {favCount:-1,title:1}}

    ])

    $lookup

    Mongodb 3.2版本新增,主要用来实现多表关联查询,相当关系型数据库中多表关联查询。每个输入待处理的文档,经过$lookup 阶段的处理,输出的新文档中会包含一个新生成的数组(可根据需要命名新key)。数组列存放的数据是来自被join集合的适配文档,如果没有,集合为空(即 为[])
    语法:

    db.collection.aggregate([{
            $lookup:{

                    from: "",

                    localField:"",

                    foreignField: "",

                    as:""

    })

  • 相关阅读:
    【位运算】把整数以二进制形式打印出来玩玩
    搭建discuz论坛并攻破盗取数据库
    webgl 系列 —— 变换矩阵和动画
    SEBlock | ECABlock | CBAM
    linux 系统调用流程分析
    图像处理ASIC设计方法 笔记10 插值算法的流水线架构
    【408数据结构与算法】—栈与递归(十二)
    Using lxd to do vlan test (by quqi99)
    Godot 添加Nuget 引用
    自定义弹窗(含生成zxing二维码功能)看这一篇就够了
  • 原文地址:https://blog.csdn.net/weixin_68509156/article/details/128164322