• 2. MongoDB 应用与开发


    2. MongoDB 应用与开发

    2.1.MongoDB 安装

    • 官网下载安装介质:https://www.mongodb.com/download-center,选择适当的版本,这 里以 linux 版本 mongodb-linux-x86_64-4.0.4 为例

    https://www.mongodb.org/dl/linux/x86_64

    windows版地址

    Downloads for windows (mongodb.org)

    或者

    MongoDB Community Download | MongoDB

    1. linux安装

    tar zxvf mongodb-linux-x86_64-4.0.4.tgz
    mv mongodb-linux-linux-x86_64-4.0.4 mongodb
    mkdir -p mongodb/{data/db,log,conf} # cmd 使用的md命令, 使用{}可以建多个子目录
    vi mongodb/conf/mgdb.conf
    
    • 1
    • 2
    • 3
    • 4

    配置文档

    https://docs.mongodb.com/v2.4/reference/configuration-options/

    dbpath=/soft/mongodb/data/db #数据文件存放目录 
    
    logpath=/soft/mongodb/log/mongodb.log #日志文件存放目录 
    
    port=27017 #端口,默认 27017,可以自定义 
    
    logappend=true #开启日志追加添加日志 
    
    fork=true #以守护程序的方式启用,即在后台运行 
    
    bind_ip=0.0.0.0 #本地监听 IP,0.0.0.0 表示本地所有 IP 
    
    auth=false #是否需要验证权限登录(用户名和密码) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    修改环境变量

    vi /etc/profile 
    
    export MONGODB_HOME=/soft/mongodb 
    
    export PATH=$PATH:$MONGODB_HOME/bin 
    
    source /etc/profile
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    配置开机启动

    vi /usr/lib/systemd/system/mongodb.service
    
    • 1
    [Unit] 
    
    Description=mongodb 
    
    After=network.target remote-fs.target nss-lookup.target 
    
    [Service] 
    
    Type=forking 
    
    RuntimeDirectory=mongodb 
    
    PIDFile=/soft/mongodb/data/db/mongod.lock 
    
    ExecStart=/soft/mongodb/bin/mongod --config /soft/mongodb/conf/mgdb.confExecStop=/soft/mongodb/bin/mongod --shutdown --config /soft/mongodb/conf/mgdb.conf 
    
    PrivateTmp=true 
    
    [Install] 
    
    WantedBy=multi-user.target
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    开机启动

    systemctl daemon-reload 
    
    systemctl start mongodb 
    
    systemctl enable mongodb
    
    • 1
    • 2
    • 3
    • 4
    • 5

    启动 mongodb

    service mongodb stop 
    
    service mongodb start
    
    • 1
    • 2
    • 3

    https://docs.mongodb.com/v4.0/reference/configuration-options/#storage.dbPath

    storage: 
    	dbPath: "/soft/mongodb/data/db" 
    systemLog: 
    	destination: file 
    	path: "/soft/mongodb/log/mongodb.log" 
    net:
    	bindIp: 0.0.0.0 
    	port: 27017 
    processManagement: 
    	fork: true 
    setParameter: 
    	enableLocalhostAuthBypass: false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. windows版本安装

    安装

    Install MongoDB Community on Windows using msiexec.exe — MongoDB Manual

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cRQit4w2-1660461785660)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300753520.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e0ogwBOE-1660461785661)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300753027.png)]

    配置数据目录和日志目录

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qB1n2165-1660461785662)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300754544.png)]

    启动: 界面安装可能已经启动了

    mongod.exe --dbpath D:\MongoDB\Server\4.0\data
    
    • 1

    检测是否启动: http://localhost:27017

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNVfknLP-1660461785662)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300802116.png)]

    为了方便使用, 可以配置下环境变量

    mongo_home=D:\MongoDB\Server\4.0
    
    path=%mongo_home%\bin
    
    • 1
    • 2
    • 3

    使用mongo.exe客户端连接: 如果没有配置环境变量, 需要进入目录执行

    mongo.exe
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5FpA6RSB-1660461785663)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300808693.png)]

    默认有个test的库

    将mongodb配置成系统服务

    mongod --bind_ip 0.0.0.0 --logpath %mongo_home%\log\mongo.log --logappend --dbpath %mongo_home%\data ^
    --port 27017 --serviceName "MongoDB Server" --serviceDisplayName "MongoDB Server" --install
    
    • 1
    • 2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0sIvPRv-1660461785663)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300813787.png)]

    win+r

    services.msc
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZv11wxi-1660461785664)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207300815253.png)]

    可视化工具

    romomongo

    https://studio3t.com/download-studio3t-free

    navicat

    2.2. 快速入门

    1. 数据结构介绍

    { 
    	"_id" : ObjectId("59f938235d93fc4af8a37114"), 
    	"username" : "lison", 
    	"country" : "in11digo", 
    	"address" : { 
    		"aCode" : "邮编", 
    		"add" : "d11pff" 
    	},
    	"favorites" : { 
    		"movies" : ["杀破狼 2","1dushe","雷神 1"], 
    		"cites" : ["1sh","1cs","1zz"] 
    	}, 
    	"age" : 18"salary":NumberDecimal("2.099"), 
    	"lenght"1.79 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 翻译sql

    查询sql

    // 查询
    select * from users where favorites.cites has "深圳""北京";
    db.users.find({"favorites.cities":{"$all":["深圳", "北京"]}});
    
    // 更新
    update users set age=6 where username = 'lison';
    db.users.update({"username":"lison"},{"$set":{"age":6}});
    // 删除          
    delete from users where username = 'lison';
    db.users.deleteMany({"username":"lison"});
    
    // 插入
    insert into users("username", "conuty") values("lison", "深圳");
    var user1 = {
            "username" : "lison",
            "country" : "china",
            "address" : {
                    "aCode" : "411000",
                    "add" : "长沙"
            },
            "favorites" : {
                    "movies" : ["杀破狼2","战狼","雷神1"],
                    "cites" : ["长沙","深圳","上海"]
            },
            "age" : 18,
    	   "salary":NumberDecimal("18889.09"),
           "lenght" :1.79
    	    
    };
    db.users.insert(user1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    复杂点的查询

    // like查询
    select * from users  where username like '%s%' and (country= English or country= USA);
    // $regex使用
    db.users.find({"$and":[{"username":{"$regex":".*s.*"}},{"$or":[{"country":"English"},{"country":"USA"}]}]});
    
    // 更新集合
    update users  set favorites.movies add "小电影2 ", "小电影3" where favorites.cites  has "东莞";
    // $each, $addToSet使用
    db.users.updateMany({"favorites.cities":"东莞"},{"$addToSet":{"favorites.movies":{"$each":["小电影2 ", "小电影3"]}}});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. 事务

    关系数据的事务

    begin
     update  users  set lenght= lenght-1  where username = ‘james’
     update  users  set lenght= lenght+1  where username = ‘lison’
    commit
    
    • 1
    • 2
    • 3
    • 4

    错误示例

    s = db.getMongo().startSession()
    s.startTransaction()
    db.users.update({"username" : "james"},{"$inc":{"lenght":-1}})
    db.users.update({"username" : "lison"},{"$inc":{"lenght":1}})
    s.commitTransaction()
    s.abortTransaction()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以下示例必须要在集群环境下才可以使用(复制集)

    var s = db.getMongo().startSession(); // 获取会话
    s.startTransaction(); // 开启事务
    var usersCollection = s.getDatabase("lison").users; // 通过会话获取数据的集合信息
    usersCollection.update({"username" : "james"},{"$inc":{"lenght":-1}});
    usersCollection.update({"username" : "lison"},{"$inc":{"lenght":1}});
    s.commitTransaction(); // 提交事务
    s.abortTransaction(); // 中断回滚事务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.3 查询

    1. Java客户端操作[mongo-java-driver]

    pom依赖坐标

    <dependency> 
        <groupId>org.mongodbgroupId>
        <artifactId>mongo-java-driverartifactId> 
        <version>3.11.2version> 
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Document 方式

    Document类的特点

    本质就是一个map, 可以序列化, 实现了Bson的toBsonDocument方法用来获取BsonDocument; 所以当作一个map来操作即可

    public class Document implements Map<String, Object>, Serializable, Bson {
    	private final LinkedHashMap<String, Object> documentAsMap; // 存放数据的LinkedhashMap
        // append就是对put的封装, 并使用链式调用返回this, get也是封装了map的get
    	public Document append(final String key, final Object value) {
            documentAsMap.put(key, value);
            return this;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实例化db,doc,client

    //数据库
    private MongoDatabase db;
    //文档集合
    private MongoCollection<Document> doc;
    //连接客户端(内置连接池)
    private MongoClient client;
    
    @Before
    public void init() {
        /*client = new MongoClient("localhost", 27017);*/
        List<ServerAddress> asList =
            Arrays.asList(new ServerAddress("localhost", 27018), new ServerAddress("localhost", 27017),
                new ServerAddress("localhost", 27019));
        client = new MongoClient(asList);
        /* client = new MongoClient("localhost", 27031);*/
        db = client.getDatabase("lison");
        doc = db.getCollection("users");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    插入数据

    @Test
    public void insertDemo() {
        Document doc1 = new Document();
        doc1.append("username", "cang");
        doc1.append("country", "USA");
        doc1.append("age", 20);
        doc1.append("lenght", 1.77f);
        doc1.append("salary", new BigDecimal("6565.22"));//存金额,使用bigdecimal这个数据类型
    
        //添加“address”子文档
        Map<String, String> address1 = new HashMap<String, String>();
        address1.put("aCode", "0000");
        address1.put("add", "xxx000");
        doc1.append("address", address1);
    
        //添加“favorites”子文档,其中两个属性是数组
        Map<String, Object> favorites1 = new HashMap<String, Object>();
        favorites1.put("movies", Arrays.asList("aa", "bb"));
        favorites1.put("cites", Arrays.asList("东莞", "东京"));
        doc1.append("favorites", favorites1);
    
        Document doc2 = new Document();
    ...
        //使用insertMany插入多条数据
        doc.insertMany(Arrays.asList(doc1, doc2));
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    查询数据

    @Test
    public void testFind() {
        final List<Document> ret = new ArrayList<>();
        //block接口专门用于处理查询出来的数据
        Consumer<Document> printDocument = new Consumer<Document>() {
            @Override
            public void accept(Document document) {
                System.out.println(document);
                ret.add(document);
            }
        };
        //select * from users  where favorites.cites has "东莞"、"东京"
        //db.users.find({ "favorites.cites" : { "$all" : [ "东莞" , "东京"]}})
        Bson all = Filters.all("favorites.cites", Arrays.asList("东莞", "东京"));//定义数据过滤器,喜欢的城市中要包含"东莞"、"东京"
        FindIterable<Document> find = doc.find(all);
    
    	find.forEach(printDocument); // printDocument是定义一个如何处理数据的action, forEach是定义一个如何处理数据的框架, 所以框架是固定的, 但是action可以切换
    
        System.out.println("------------------>" + String.valueOf(ret.size()));
        ret.removeAll(ret);
    
        //select * from users  where username like '%s%' and (contry= English or contry = USA)
        // db.users.find({ "$and" : [ { "username" : { "$regex" : ".*c.*"}} , { "$or" : [ { "country" : "English"} , { "country" : "USA"}]}]})
    
        String regexStr = ".*c.*";
        Bson regex = Filters.regex("username", regexStr);//定义数据过滤器,username like '%s%'
        Bson or = Filters.or(Filters.eq("country", "English"),
            Filters.eq("country", "USA"));//定义数据过滤器,(contry= English or contry = USA)
        Bson and = Filters.and(regex, or);
        FindIterable<Document> find2 = doc.find(and);
        find2.forEach(printDocument);
        System.out.println("------------------>" + String.valueOf(ret.size()));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    更新数据

    @Test
    public void testUpdate() {
        //update  users  set age=6 where username = 'lison'
        //     db.users.updateMany({ "username" : "lison"},{ "$set" : { "age" : 6}},true)
    
        Bson eq = Filters.eq("username", "cang");//定义数据过滤器,username = 'cang'
        Bson set = Updates.set("age", 8);
        UpdateResult updateMany = doc.updateMany(eq, set);
        System.out.println("------------------>" + String.valueOf(updateMany.getModifiedCount()));//打印受影响的行数
    
        //update users  set favorites.movies add "小电影2 ", "小电影3" where favorites.cites  has "东莞"
        //db.users.updateMany({ "favorites.cites" : "东莞"}, { "$addToSet" : { "favorites.movies" : { "$each" : [ "小电影2 " , "小电影3"]}}},true)
    
        Bson eq2 = Filters.eq("favorites.cites", "东莞");//定义数据过滤器,favorites.cites  has "东莞"
        Bson addEachToSet =
            Updates.addEachToSet("favorites.movies", Arrays.asList("小电影2 ", "小电影3"));
        UpdateResult updateMany2 = doc.updateMany(eq2, addEachToSet);
        System.out.println("------------------>" + String.valueOf(updateMany2.getModifiedCount()));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    删除数据

    @Test
    public void testDelete() {
    
        //delete from users where username = ‘cang’
        //db.users.deleteMany({ "username" : "cang"} )
        Bson eq = Filters.eq("username", "cang");//定义数据过滤器,username='cang'
        DeleteResult deleteMany = doc.deleteMany(eq);
        System.out.println("------------------>" + String.valueOf(deleteMany.getDeletedCount()));//打印受影响的行数
    
        //delete from users where age >8 and age <25
        //db.users.deleteMany({"$and" : [ {"age" : {"$gt": 8}} , {"age" : {"$lt" : 25}}]})
    
        Bson gt = Filters.gt("age", 8);//定义数据过滤器,age > 8,所有过滤器的定义来自于Filter这个包的静态方法,需要频繁使用所以静态导入
        //     Bson gt = Filter.gt("age",8);
    
        Bson lt = Filters.lt("age", 25);//定义数据过滤器,age < 25
        Bson and = Filters.and(gt, lt);//定义数据过滤器,将条件用and拼接
        DeleteResult deleteMany2 = doc.deleteMany(and);
        System.out.println("------------------>" + String.valueOf(deleteMany2.getDeletedCount()));//打印受影响的行数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    事务操作

    @Test
    public void testTransaction() {
        //    begin
        //    update  users  set lenght= lenght-1  where username = ‘james’
        //    update  users  set lenght= lenght+1  where username = ‘lison’
        //    commit
        ClientSession clientSession = client.startSession();
        clientSession.startTransaction();
        Bson eq = Filters.eq("username", "james");// 带等于的话就是lte,gte, e出现在后面, 很合理, 因为等于也在后面
        Bson inc = Updates.inc("lenght", -1);
        doc.updateOne(clientSession, eq, inc);
    
        Bson eq2 = Filters.eq("username", "lison");
        Bson inc2 = Updates.inc("lenght", 1);
    
        doc.updateOne(clientSession, eq2, inc2);
    
        clientSession.commitTransaction();
        //clientSession.abortTransaction();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    POJO方式

    pojo类(省略getset方法): 数据封装就是pojo的封装

    Favorites

    public class Favorites {
       private List<String> movies;
       private List<String> cites;
    }
    
    • 1
    • 2
    • 3
    • 4

    Address

    public class Address {   
       private String aCode;
       private String add;
    }
    
    • 1
    • 2
    • 3
    • 4

    User: 只需要这个添加Document注解即可

    @Document(collection="users")
    public class User {
       private ObjectId id;
       private String username;
       private String country;
       private Address address;
       private Favorites favorites;
       private int age;
       private BigDecimal salary;
       private float lenght;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    初始化db,doc,client

    private MongoDatabase db;
    
    private MongoCollection<User> doc;
    
    private MongoClient client;
    
    @Before
    public void init() {
        //编解码器的list
        List<CodecRegistry> codecResgistes = new ArrayList<>();
        //list加入默认的编解码器集合
        codecResgistes.add(MongoClient.getDefaultCodecRegistry());
        
        //生成一个pojo的编解码器
        CodecRegistry pojoCodecRegistry =
            CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build());
        //list加入pojo的编解码器
        codecResgistes.add(pojoCodecRegistry);
        
        //通过编解码器的list生成编解码器注册中心
        CodecRegistry registry = CodecRegistries.fromRegistries(codecResgistes);
    
        //把编解码器注册中心放入MongoClientOptions
        //MongoClientOptions相当于连接池的配置信息
        MongoClientOptions build =
            MongoClientOptions.builder().
            writeConcern(WriteConcern.ACKNOWLEDGED).
            codecRegistry(registry).build();
    
        //ServerAddress serverAddress = new ServerAddress("localhost", 27017);
    
        List<ServerAddress> asList =
            Arrays.asList(new ServerAddress("localhost", 27018), new ServerAddress("localhost", 27017),
                new ServerAddress("localhost", 27019));
    
        client = new MongoClient(asList, build);
        db = client.getDatabase("lison");
    
        doc = db.getCollection("users", User.class);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    插入数据

    @Test
    public void insertDemo() {
        User user = new User();
        user.setUsername("cang");
        user.setCountry("USA");
        user.setAge(20);
        user.setLenght(1.77f);
        user.setSalary(new BigDecimal("6265.22"));
    
        //添加“address”子文档
        Address address1 = new Address();
        address1.setaCode("411222");
        address1.setAdd("sdfsdf");
        user.setAddress(address1);
    
        //添加“favorites”子文档,其中两个属性是数组
        Favorites favorites1 = new Favorites();
        favorites1.setCites(Arrays.asList("东莞", "东京"));
        favorites1.setMovies(Arrays.asList("西游记", "一路向西"));
        user.setFavorites(favorites1);
    
        User user1 = new User();
        user1.setUsername("chen");
        user1.setCountry("China");
        user1.setAge(30);
        user1.setLenght(1.77f);
        user1.setSalary(new BigDecimal("6885.22"));
        Address address2 = new Address();
        address2.setaCode("411000");
        address2.setAdd("我的地址2");
        user1.setAddress(address2);
        Favorites favorites2 = new Favorites();
        favorites2.setCites(Arrays.asList("珠海", "东京"));
        favorites2.setMovies(Arrays.asList("东游记", "一路向东"));
        user1.setFavorites(favorites2);
    
        //使用insertMany插入多条数据
        doc.insertMany(Arrays.asList(user, user1));
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    查询数据: 和Document方式不同点仅仅只是返回的对象不同, Document返回的是通用的Document对象, pojo返回的是User

    //文档集合
    private MongoCollection<Document> doc; // 这个doc的collection可以带泛型, Document方式
    private MongoCollection<User> doc; // User方式
    
    • 1
    • 2
    • 3
    @Test
    public void testFind() {
        final List<User> ret = new ArrayList<>();
        Consumer<User> printDocument = new Consumer<User>() {
            @Override
            public void accept(User t) {
                System.out.println(t.toString());
                ret.add(t);
            }
        };
    
        //select * from users  where favorites.cites has "东莞"、"东京"
        //db.users.find({ "favorites.cites" : { "$all" : [ "东莞" , "东京"]}})
        Bson all = Filters.all("favorites.cites", Arrays.asList("东莞", "东京"));//定义数据过滤器,喜欢的城市中要包含"东莞"、"东京"
        FindIterable<User> find = doc.find(all);
        find.forEach(printDocument);
        System.out.println("------------------>" + String.valueOf(ret.size()));
        ret.removeAll(ret);
    
        //select * from users  where username like '%s%' and (contry= English or contry = USA)
        // db.users.find({ "$and" : [ { "username" : { "$regex" : ".*c.*"}} , { "$or" : [ { "country" : "English"} , { "country" : "USA"}]}]})
        String regexStr = ".*c.*";
        Bson regex = Filters.regex("username", regexStr);//定义数据过滤器,username like '%s%'
        Bson or = Filters.or(Filters.eq("country", "English"), Filters.eq("country", "USA"));//定义数据过滤器,(contry= English or contry = USA)
        FindIterable<User> find2 = doc.find(Filters.and(regex, or));
        find2.forEach(printDocument);
        System.out.println("------------------>" + String.valueOf(ret.size()));
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    更新数据

    @Test
    public void testUpdate() {
        //update  users  set age=6 where username = 'lison'
        //db.users.updateMany({ "username" : "lison"},{ "$set" : { "age" : 6}},true)
        Bson eq = Filters.eq("username", "lison");//定义数据过滤器,username = 'lison'
        Bson set = Updates.set("age", 8);//更新的字段.来自于Updates包的静态导入
        UpdateResult updateMany = doc.updateMany(eq, set);
        System.out.println("------------------>" + String.valueOf(updateMany.getModifiedCount()));//打印受影响的行数
    
        //update users  set favorites.movies add "小电影2 ", "小电影3" where favorites.cites  has "东莞"
        //db.users.updateMany({ "favorites.cites" : "东莞"}, { "$addToSet" : { "favorites.movies" : { "$each" : [ "小电影2 " , "小电影3"]}}},true)
        Bson eq2 = Filters.eq("favorites.cites", "东莞");//定义数据过滤器,favorites.cites  has "东莞"
        Bson addEachToSet = Updates.addEachToSet("favorites.movies", Arrays.asList("小电影2 ", "小电影3"));//更新的字段.来自于Updates包的静态导入
        UpdateResult updateMany2 = doc.updateMany(eq2, addEachToSet);
        System.out.println("------------------>" + String.valueOf(updateMany2.getModifiedCount()));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    删除数据

    @Test
    public void testDelete() {
    
        //delete from users where username = ‘lison’
        //db.users.deleteMany({ "username" : "lison"} )
        Bson eq = Filters.eq("username", "lison");//定义数据过滤器,username='lison'
        DeleteResult deleteMany = doc.deleteMany(eq);
        System.out.println("------------------>" + String.valueOf(deleteMany.getDeletedCount()));//打印受影响的行数
    
        //delete from users where age >8 and age <25
        //db.users.deleteMany({"$and" : [ {"age" : {"$gt": 8}} , {"age" : {"$lt" : 25}}]})
        Bson gt = Filters.gt("age", 8);//定义数据过滤器,age > 8,所有过滤器的定义来自于Filter这个包的静态方法,需要频繁使用所以静态导入
    
        Bson lt = Filters.lt("age", 25);//定义数据过滤器,age < 25
        Bson and = Filters.and(gt, lt);//定义数据过滤器,将条件用and拼接
        DeleteResult deleteMany2 = doc.deleteMany(and);
        System.out.println("------------------>" + String.valueOf(deleteMany2.getDeletedCount()));//打印受影响的行数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    两种方式的总结:

    ​ 因为插入数据是插入一个文档, 所以Document方式传入的是一个Document, pojo方式是传入一个java bean

    ​ 因为查询需要返回数据, 所以Document返回的是一个Document对象, pojo返回的是一个 java bean

    因为更新和删除都没有文档传入和返回, 只有条件的封装和set封装, 所以都一样, 都是去封装bson

    2. Spring-data-mongodb 客户端

    pom依赖坐标

    <dependency> 
        <groupId>org.springframework.datagroupId>
        <artifactId>spring-data-mongodbartifactId>
        <version>2.2.1.RELEASEversion> 
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    applicationContext.xml创建bean

    
    <mongo:mongo-client id="mongo" host="localhost" port="27017" credentials="lison:lison@lison"
                        replica-set="localhost:27017,localhost:27018,localhost:27019">
    mongo:mongo-client>
    
    <mongo:mongo-client id="mongo" host="localhost" port="27017">
        <mongo:client-options
                              write-concern="ACKNOWLEDGED"
                              threads-allowed-to-block-for-connection-multiplier="5"
                              max-wait-time="1200"
                              connect-timeout="1000"/>
    mongo:mongo-client>
    
    
    <mongo:db-factory dbname="lison" mongo-ref="mongo"/>
    
    
    
    <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
        <constructor-arg name="mongoConverter" ref="mappingConverter"/>
    bean>
    
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <bean id="transactionManager" class="org.springframework.data.mongodb.MongoTransactionManager">
        <property name="dbFactory" ref="mongoDbFactory"/>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    使用mongoTemplate

    @Resource 
    private MongoOperations tempelate;
    
    • 1
    • 2

    插入数据: 封装pojo

    @Test
    public void insertDemo() {
        User user = new User();
        user.setUsername("cang");
        user.setCountry("USA");
        user.setAge(20);
        user.setLenght(1.77f);
        user.setSalary(new BigDecimal("6265.22"));
    
        //添加“address”子文档
        Address address1 = new Address();
        address1.setaCode("411222");
        address1.setAdd("sdfsdf");
        user.setAddress(address1);
    
        //添加“favorites”子文档,其中两个属性是数组
        Favorites favorites1 = new Favorites();
        favorites1.setCites(Arrays.asList("东莞", "东京"));
        favorites1.setMovies(Arrays.asList("西游记", "一路向西"));
        user.setFavorites(favorites1);
    
        User user1 = new User();
        user1.setUsername("chen");
        user1.setCountry("China");
        user1.setAge(30);
        user1.setLenght(1.77f);
        user1.setSalary(new BigDecimal("6885.22"));
        Address address2 = new Address();
        address2.setaCode("411000");
        address2.setAdd("我的地址2");
        user1.setAddress(address2);
        Favorites favorites2 = new Favorites();
        favorites2.setCites(Arrays.asList("珠海", "东京"));
        favorites2.setMovies(Arrays.asList("东游记", "一路向东"));
        user1.setFavorites(favorites2);
    
        tempelate.insertAll(Arrays.asList(user, user1));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    查询: 返回pojo

    @Test
    public void testFind() {
    
        //select * from users  where favorites.cites has "东莞"、"东京"
        //db.users.find({ "favorites.cites" : { "$all" : [ "东莞" , "东京"]}})
        Criteria all = Criteria.where("favorites.cites").all(Arrays.asList("东莞", "东京"));
        List<User> find = tempelate.find(Query.query(all), User.class);
        System.out.println(find.size());
        for (User user : find) {
            System.out.println(user.toString());
        }
    
        //select * from users  where username like '%s%' and (contry= English or contry = USA)
        // db.users.find({ "$and" : [ { "username" : { "$regex" : ".*s.*"}} , { "$or" : [ { "country" : "English"} , { "country" : "USA"}]}]})
        String regexStr = ".*c.*";
        //username like '%s%'
        Criteria regex = Criteria.where("username").regex(regexStr);
        //contry= EngLish
        Criteria or1 = Criteria.where("country").is("English");
        //contry= USA
        Criteria or2 = Criteria.where("country").is("USA");
    
        Criteria or = new Criteria().orOperator(or1, or2);
    
        Query query = Query.query(new Criteria().andOperator(regex, or));
    
        List<User> find2 = tempelate.find(query, User.class);
    
        System.out.println(find2.size());
        for (User user : find2) {
            System.out.println(user.toString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    更新: 就像是封装sql的where条件和set

    @Test
    public void testUpdate() {
        //update  users  set age=6 where username = 'lison'
        //db.users.updateMany({ "username" : "lison"},{ "$set" : { "age" : 6}},true)
        Query query = Query.query(Criteria.where("username").is("lison"));
        Update update = Update.update("age", 6);
        UpdateResult updateFirst = tempelate.updateMulti(query, update, User.class);
        System.out.println(updateFirst.getModifiedCount());
    
        //update users  set favorites.movies add "小电影2 ", "小电影3" where favorites.cites  has "东莞"
        //db.users.updateMany({ "favorites.cites" : "东莞"}, { "$addToSet" : { "favorites.movies" : { "$each" : [ "小电影2 " , "小电影3"]}}},true)
        query = Query.query(Criteria.where("favorites.cites").is("东莞"));
        update = new Update().addToSet("favorites.movies").each("小电影2 ", "小电影3");
        UpdateResult updateMulti = tempelate.updateMulti(query, update, User.class);
        System.out.println("--------------------->" + updateMulti.getModifiedCount());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    删除: 封装where

    @Test
    public void testDelete() {
    
        //delete from users where username = ‘lison’
        //db.users.deleteMany({ "username" : "lison"} )
        Query query = Query.query(Criteria.where("username").is("lison"));
        DeleteResult remove = tempelate.remove(query, User.class);
        System.out.println("--------------------->" + remove.getDeletedCount());
    
        //delete from users where age >8 and age <25
        //db.users.deleteMany({"$and" : [ {"age" : {"$gt": 8}} , {"age" : {"$lt" : 25}}]})
        query = Query.query(new Criteria().andOperator(Criteria.where("age").gt(8), Criteria.where("age").lt(25)));
        DeleteResult remove2 = tempelate.remove(query, User.class);
        System.out.println("--------------------->" + remove2.getDeletedCount());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    事务: 直接使用注解即可

    @Transactional
    public void doTransaction() {
        Query query = query(where("username").is("lison"));
        Update update = new Update().inc("lenght", 1);
        tempelate.updateMulti(query, update, User.class);
    
        query = query(where("username").is("james"));
        update = new Update().inc("lenght", -1);
        tempelate.updateMulti(query, update, User.class);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3. 类型转换器

    在 mongodb 3.4 版本里面新增了个数据类型 Decimal128, 但在前面操作的时候发现 User 里面的 salary 依然还是字符串

    {
        "_id": ObjectId("62e4b7c75173000062004e04"),
        "username": "james",
        "country": "English",
        "address": {
            "aCode": "311000",
            "add": "地址"
        },
        "favorites": {
            "movies": [
                "复仇者联盟",
                "战狼",
                "雷神1"
            ],
            "cites": [
                "西安",
                "东京",
                "上海"
            ]
        },
        "age": 24,
        "salary": "7889.09", // 这个使用的是字符串不是数字
        "lenght": 1.35
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们可以使用类型转换器

    1. 新增 BigDecimalToDecimal128Converter

    Converter: 转换器

    public class BigDecimalToDecimal128Converter implements Converter<BigDecimal, Decimal128> {
        @Override
        public Decimal128 convert(BigDecimal source) {
            return new Decimal128(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 新增 Decimal128ToBigDecimalConverter

    public class Decimal128ToBigDecimalConverter implements Converter<Decimal128, BigDecimal> {
        @Override
        public BigDecimal convert(Decimal128 source) {
            return source.bigDecimalValue();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 通过applicationContext.xml装配转换器(Converter)

    
    <mongo:mapping-converter base-package="len.hgy.convert">
        <mongo:custom-converters>
            <mongo:converter>
                <bean class="len.hgy.convert.BigDecimalToDecimal128Converter"/>
            mongo:converter>
            <mongo:converter>
                <bean class="len.hgy.convert.Decimal128ToBigDecimalConverter"/>
            mongo:converter>
        mongo:custom-converters>
    mongo:mapping-converter>
    
    
    <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
        <constructor-arg name="mongoConverter" ref="mappingConverter"/>
    bean>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4. 重新测试入库数据

    // 1
    {
        "_id": ObjectId("62e4b7c75173000062004e04"),
        "username": "james",
        "country": "English",
        "address": {
            "aCode": "311000",
            "add": "地址"
        },
        "favorites": {
            "movies": [
                "复仇者联盟",
                "战狼",
                "雷神1"
            ],
            "cites": [
                "西安",
                "东京",
                "上海"
            ]
        },
        "age": 24,
        "salary": NumberDecimal("7889.09"),
        "lenght": 1.35
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4. java 驱动与 mongoDB 兼容性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NhXDIg9E-1660461785665)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207312311269.png)]

    可见 mongodb 具备强大的向下兼容性

    5. java 驱动与 jdk 的兼容性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N3Kg2qlD-1660461785666)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207312312578.png)]

    6. spring data mongo 与 java mongo 驱动兼容性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Esqm3TVN-1660461785667)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207312312226.png)]

    7. MongoDB 数据类型

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpKrvFN1-1660461785668)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202207312314588.png)]

    8. 查询

    db.collection.find(query, projection);
    
    • 1
    • query :可选,使用查询操作符指定查询条件
    • projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需 省略该参数即可(默认省略)。 注意:0 表示字段排除,非 0 表示字段选择并排除其他字段,所有字段必须设置同样的值;
    • 需要以易读的方式来读取数据,可以使用 pretty() 方法;
    db.users.find({"$and":[{"username":"lison"},{"age":18}]},{"username":0,"age":0})
    
    • 1

    查询选择器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCQKUXbc-1660461785669)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208012355831.png)]

    查询选择器实战

    db.users.find({"username":{"$in":["lison","mark"]}}).pretty();
    
    db.users.find({"$or":[{"username":"lison"},{"username":"mark"}]});
    
    // 判断文档有没有关心的字段
    db.users.find({"length":{"$exists":true}}).pretty();
    // not 语句 会把不包含查询语句字段的文档 也检索出来
    db.users.find({"length":{"$not":{"$gte":1.77}}}).pretty();
    db.users.find({"$or":[{"length":{"$lt":1.77}},{"length":{"$exists":false}}]}).pretty();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查询选择

    • 映射
    db.users.find({},{"username":1});
    db.users.find({},{"username":1, "age":1});
    // 映射的value值必须是相同的, 意思就是如果选择使用排除法就使用排除法, 如果使用选择法就使用选择法, 不然会混乱的
    db.users.find({},{"username":0});
    
    • 1
    • 2
    • 3
    • 4
    • 排序
    // 1: 升序, -1: 降序, 理解成 +1 和 -1
    db.users.find().sort({"username":1}).pretty();
    
    • 1
    • 2
    • 跳过和限制
    skip(n): 跳过n条数据
    limit(n): 限制n条数据
    db.users.find().sort({"username":1}).limit(2).skip(2);
    
    • 1
    • 2
    • 3
    • 查询唯一值

      distinct(): 查询字段的唯一值

    // 这参数竟然不是json形式的入参
    db.users.distinct("username");
    
    • 1
    • 2

    字符串数组选择查询

    • 数组单元素查询
    db.users.find({"favorites.movies":"流浪地球"})
    
    • 1
    • 数组精确查找
    // 查询数组 严格按照数量、顺序相等
    db.users.find({"favorites.movies":["战神", "雷神","不死鸟"]}, {"favorites.movies":1})
    
    • 1
    • 2
    • 数多元素查询
    // 内容数量相等, 和顺序无关
    db.users.find({"favorites.movies":{"$all":["雷神1", "天堂"]}})
    // 内容包含, 和数据顺序无关
    db.users.find({"favorites.movies":{"$in":["雷神1", "天堂"]}})
    
    • 1
    • 2
    • 3
    • 4
    • 索引查询(数组索引)
    // 数组的第一个索引位置
    db.users.find({"favorites.movies.0":"鬼吹灯",{"favorites.movies":1}})
    
    • 1
    • 2
    • 返回数组子集
    // $slice 可以取两个元素数组, 分别表示跳过和限制条数
    db.users.find({}, {"favorites.movies":{"$slice":[1,2]}, "favorites":1})
    // 可以和下面结果对比
    db.users.find({},{"favorites":1})
    
    • 1
    • 2
    • 3
    • 4

    对象数组选择查询

    • 单元素查询
    // 说明: comments是一个数组
    db.users.find({"comments":{"author":"lison6", "context": "xxx"}, "commentTime": ISODate("2017-06-06T00:00:00Z")})
    
    • 1
    • 2
    • 查找 lison1 或者 lison12 评论过的 user ($in 查找符)
    // 备注:跟数量无关,跟顺序无关;
    db.users.find({"comments.author":{"$in":["lison1", "lison2"]}})
    
    • 1
    • 2
    • 查找 lison1 和 lison12 都评论过的 user
    // 备注:跟数量有关,跟顺序无关;
    db.users.find({"comments.author":{"$all":["lison12","lison1"]}}).pretty()
    
    • 1
    • 2
    • 查找 lison5 评语为包含"早上好"关键字的 user($elemMatch 查找符)
    // 备注:数组中对象数据要符合查询对象里面所有的字段,全元素匹配,和顺序无关;
    // $elemMatch: 匹配么一个元素
    db.users.find({"comments":{"$elemMatch":{"author":"lison5", "content":{"$regex":".*早上好.*"}}}})
    
    • 1
    • 2
    • 3

    9. Java 客户端解析

    原生客户端

    • MongoClient → MongoDatabase →MongoCollection
      • MongoClient 被设计成线程安全、可以被多线程共享的。通常访问数据库集群的应用只 需要一个实例
      • 如果需要使用 pojo 对象读写,需要将 PojoCodecProvider 注入到 client 中
    • 查询和更新的 API 类
      • 查询器:com.mongodb.client.model.Filters
      • 更新器:com.mongodb.client.model.Updates
      • 投影器:com.mongodb.client.model.Projections

    初始化

    // 静态导入
    import static com.mongodb.client.model.Aggregates.*;
    import static com.mongodb.client.model.Filters.*;
    import static com.mongodb.client.model.Projections.*;
    import static com.mongodb.client.model.Sorts.ascending;
    import static com.mongodb.client.model.Sorts.orderBy;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    private MongoDatabase db;
    
    private MongoCollection<Document> collection;
    
    private MongoCollection<Document> orderCollection;
    
    @Resource(name = "mongo")
    private MongoClient client;
    
    @Before
    public void init() {
        db = client.getDatabase("lison");
        collection = db.getCollection("users");
        orderCollection = db.getCollection("ordersTest");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    实例

    in

    // db.users.find({"username":{"$in":["lison", "mark", "james"]}}).pretty()
    // 查询姓名为lison、mark和james这个范围的人
    @Test
    public void testInOper() {
        Bson in = in("username", "lison", "mark", "james");
        FindIterable<Document> find = collection.find(in);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    exists

    // db.users.find({"lenght":{"$exists":true}}).pretty()
    // 判断文档有没有关心的字段
    @Test
    public void testExistsOper() {
        Bson exists = exists("lenght", true);
        FindIterable<Document> find = collection.find(exists);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    sort, limit, skip

    // db.users.find().sort({"username":1}).limit(1).skip(2)
    // 测试sort,limit,skip
    @Test
    public void testSLSOper() {
        Document sort = new Document("username", 1);
        FindIterable<Document> find = collection.find().sort(sort).limit(1).skip(2);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    not,gte

    // db.users.find({"lenght":{"$not":{"$gte":1.77}}}).pretty()
    // 查询高度小于1.77或者没有身高的人
    // not语句 会把不包含查询语句字段的文档 也检索出来
    @Test
    public void testNotOper() {
        Bson gte = gte("lenght", 1.77);
        Bson not = not(gte);
        FindIterable<Document> find = collection.find(not);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    字符串数组

    // db.users.find({"favorites.movies":"蜘蛛侠"})
    // 查询数组中包含"蜘蛛侠"
    @Test
    public void testArray1() {
        Bson eq = eq("favorites.movies", "蜘蛛侠");
        FindIterable<Document> find = collection.find(eq);
        printOperation(find);
    }
    
    // db.users.find({"favorites.movies":[ "妇联4","杀破狼2", "战狼", "雷神1","神奇动物在哪里"]},{"favorites.movies":1})
    // 查询数组等于[ “杀破狼2”, “战狼”, “雷神1” ]的文档,严格按照数量、顺序;
    @Test
    public void testArray2() {
        Bson eq = eq("favorites.movies", Arrays.asList("妇联4", "杀破狼2", "战狼", "雷神1", "神奇动物在哪里"));
        FindIterable<Document> find = collection.find(eq);
        printOperation(find);
    }
    
    //数组多元素查询
    @Test
    public void testArray3() {
        // db.users.find({"favorites.movies":{"$all":[ "雷神1", "战狼"]}},{"favorites.movies":1})
        // 查询数组包含["雷神1", "战狼" ]的文档,跟顺序无关
        Bson all = all("favorites.movies", Arrays.asList("雷神1", "战狼"));
        FindIterable<Document> find = collection.find(all);
        printOperation(find);
        //		db.users.find({"favorites.movies":{"$in":[ "雷神1", "战狼" ]}},{"favorites.movies":1})
        //		查询数组包含[“雷神1”, “战狼” ]中任意一个的文档,跟顺序无关,跟数量无关
        Bson in = in("favorites.movies", Arrays.asList("雷神1", "战狼"));
        find = collection.find(in);
        printOperation(find);
    }
    
    // // db.users.find({"favorites.movies.0":"妇联4"},{"favorites.movies":1})
    // 查询数组中第一个为"妇联4"的文档
    @Test
    public void testArray4() {
        Bson eq = eq("favorites.movies.0", "妇联4");
        FindIterable<Document> find = collection.find(eq);
        printOperation(find);
    }
    
    // db.users.find({},{"favorites.movies":{"$slice":[1,2]},"favorites":1})
    // $slice可以取两个元素数组,分别表示跳过和限制的条数;
    @Test
    public void testArray5() {
        Bson slice = slice("favorites.movies", 1, 2);
        Bson include = include("favorites");
        Bson projection = fields(slice, include);
        FindIterable<Document> find = collection.find().projection(projection);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    对象数组查询实例

    //db.users.find({"comments":{"author":"lison6","content":"lison评论6","commentTime":ISODate("2017-06-06T00:00:00Z")}})
    //备注:对象数组精确查找
    @Test
    public void testObjArray1() throws ParseException {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2017-06-06 08:00:00");
    
        Document comment =
            new Document().append("author", "lison6").append("content", "lison评论6").append("commentTime", commentDate);
        Bson eq = eq("comments", comment);
        FindIterable<Document> find = collection.find(eq);
        printOperation(find);
    }
    
    //数组多元素查询
    @Test
    public void testObjArray2() {
    
        //    查找lison1 或者 lison12评论过的user ($in查找符)
        //    db.users.find({"comments.author":{"$in":["lison1","lison12"]}}).pretty()
        //      备注:跟数量无关,跟顺序无关;
    
        Bson in = in("comments.author", Arrays.asList("lison1", "lison12"));
        FindIterable<Document> find = collection.find(in);
        printOperation(find);
    
        //    查找lison1 和 lison12都评论过的user
        //    db.users.find({"comments.author":{"$all":["lison12","lison1"]}}).pretty()
        //     备注:跟数量有关,跟顺序无关;
    
        Bson all = all("comments.author", Arrays.asList("lison12", "lison1"));
        find = collection.find(all);
        printOperation(find);
    }
    
    //查找lison5评语为包含“苍老师”关键字的user($elemMatch查找符)
    // db.users.find({"comments":{"$elemMatch":{"author" : "lison5", "content" : { "$regex" : ".*苍老师.*"}}}})
    //备注:数组中对象数据要符合查询对象里面所有的字段,$全元素匹配,和顺序无关;
    
    @Test
    public void testObjArray3() throws ParseException {
        Bson eq = eq("author", "lison5");
        Bson regex = regex("content", ".*苍老师.*");
        Bson elemMatch = Filters.elemMatch("comments", and(eq, regex));
        FindIterable<Document> find = collection.find(elemMatch);
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    聚合操作

    /**
     * db.orders.aggregate([
     * {"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}
     * ])
     */
    @Test
    public void aggretionTest1() throws Exception {
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(Document t) {
                logger.info("---------------------");
                System.out.println(t.toJson());
                logger.info("---------------------");
            }
        };
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-03 08:00:00");
    
        DBObject groupFileds = new BasicDBObject();
        groupFileds.put("useCode", "$useCode");
        groupFileds.put("month", eq("$month", "$orderTime"));
    
        List<Bson> aggregates = new ArrayList<Bson>();
        aggregates.add(match(lt("orderTime", commentDate)));
        aggregates.add(group(groupFileds, Accumulators.sum("sum", "$price")));
        aggregates.add(sort(eq("_id", 1)));
        AggregateIterable<Document> aggregate = orderCollection.aggregate(aggregates);
        aggregate.forEach(printBlock);
    }
    
    /**
     * db.orders.aggregate([{"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$unwind":"$Auditors"},
     * {"$group":{"_id":{"Auditors":"$Auditors"},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}])
     */
    @Test
    public void aggretionTest2() throws Exception {
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(Document t) {
                logger.info("---------------------");
                System.out.println(t.toJson());
                logger.info("---------------------");
            }
        };
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-03 08:00:00");
        List<Bson> aggregates = new ArrayList<Bson>();
        aggregates.add(match(lt("orderTime", commentDate)));
        aggregates.add(unwind("$Auditors"));
        aggregates.add(group("$Auditors", Accumulators.sum("sum", "$price")));
        aggregates.add(sort(eq("_id", 1)));
        AggregateIterable<Document> aggregate = orderCollection.aggregate(aggregates);
        aggregate.forEach(printBlock);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    demo实例

    / 新增评论时,使用$sort运算符进行排序,插入评论后,再按照评论时间降序排序
    public void demoStep1() {
        Bson filter = eq("username", "lison");
        Document comment =
            new Document().append("author", "cang").append("content", "lison是我的粉丝").append("commentTime", new Date());
        // $sort: {"commentTime":-1}
        Document sortDoc = new Document().append("commentTime", -1);
        PushOptions sortDocument = new PushOptions().sortDocument(sortDoc);
        // $each
        Bson pushEach = Updates.pushEach("comments", Arrays.asList(comment), sortDocument);
    
        UpdateResult updateOne = collection.updateOne(filter, pushEach);
        System.out.println(updateOne.getModifiedCount());
    }
    
    @Test
    // 查看人员时加载最新的三条评论;
    // db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty()
    public void demoStep2() {
        FindIterable<Document> find = collection.find(eq("username", "lison")).projection(slice("comments", 0, 3));
        printOperation(find);
    }
    
    @Test
    // 点击评论的下一页按钮,新加载三条评论
    // db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$id":1}).pretty();
    public void demoStep3() {
        // {"username":"lison"}
        Bson filter = eq("username", "lison");
        // "$slice":[3,3]
        Bson slice = slice("comments", 3, 3);
        // "$id":1
        Bson includeID = include("id");
    
        // {"comments":{"$slice":[3,3]},"$id":1})
        Bson projection = fields(slice, includeID);
    
        FindIterable<Document> find = collection.find(filter).projection(projection);
        printOperation(find);
    }
    
    @Test
    /**
     * db.users.aggregate([{"$match":{"username":"lison"}},
     {"$unwind":"$comments"},
     {$sort:{"comments.commentTime":-1}},
     {"$project":{"comments":1}},
     {"$skip":6},
     {"$limit":3}])
     */
    // 如果有多种排序需求怎么处理,使用聚合
    public void demoStep4() {
        final List<Document> ret = new ArrayList<Document>();
        Block<Document> printBlock = getBlock(ret);
        List<Bson> aggregates = new ArrayList<Bson>();
    
        aggregates.add(match(eq("username", "lison")));
        aggregates.add(unwind("$comments"));
        aggregates.add(sort(orderBy(ascending("comments.commentTime"))));
        aggregates.add(project(fields(include("comments"))));
        aggregates.add(skip(0));
        aggregates.add(limit(3));
    
        AggregateIterable<Document> aggregate = collection.aggregate(aggregates);
    
        printOperation(ret, printBlock, aggregate);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    10. Spring mongodb 解析

    依赖

    <dependency>
        <groupId>org.springframework.datagroupId>
        <artifactId>spring-data-mongodbartifactId>
        <version>2.2.1.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    配置文件: applicationContext.xml

    
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    
        <context:component-scan base-package="len.hgy">
    
        context:component-scan>
    
        
        <mongo:mongo-client id="mongo" host="localhost" port="27017" credentials="lison:lison@lison"
                            replica-set="localhost:27017,localhost:27018,localhost:27019">
        mongo:mongo-client>
    
        <mongo:mongo-client id="mongo" host="localhost" port="27017">
            <mongo:client-options
                    write-concern="ACKNOWLEDGED"
                    threads-allowed-to-block-for-connection-multiplier="5"
                    max-wait-time="1200"
                    connect-timeout="1000"/>
        mongo:mongo-client>
    
        
        <mongo:db-factory dbname="lison" mongo-ref="mongo"/>
    
    
        
        <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
            <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
            <constructor-arg name="mongoConverter" ref="mappingConverter"/>
        bean>
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
        <bean id="transactionManager" class="org.springframework.data.mongodb.MongoTransactionManager">
            <property name="dbFactory" ref="mongoDbFactory"/>
        bean>
    
        <mongo:mapping-converter base-package="len.hgy.convert">
            <mongo:custom-converters>
                <mongo:converter>
                    <bean class="len.hgy.convert.BigDecimalToDecimal128Converter"/>
                mongo:converter>
                <mongo:converter>
                    <bean class="len.hgy.convert.Decimal128ToBigDecimalConverter"/>
                mongo:converter>
            mongo:custom-converters>
        mongo:mapping-converter>
    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    测试用例

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringQueryTest {
    
        private static final Logger logger = LoggerFactory.getLogger(SpringQueryTest.class);
    
        @Resource
        private MongoOperations tempelate;
    
        // -----------------------------操作符使用实例------------------------------------------
    
        // db.users.find({"username":{"$in":["lison", "mark", "james"]}}).pretty()
        // 查询姓名为lison、mark和james这个范围的人
        @Test
        public void testInOper() {
            Query query = query(where("username").in("lison", "mark", "james"));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
        }
    
        // db.users.find({"lenght":{"$exists":true}}).pretty()
        // 判断文档有没有关心的字段
        @Test
        public void testExistsOper() {
    
            Query query = query(where("lenght").exists(true));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
        }
    
        // db.users.find().sort({"username":1}).limit(1).skip(2)
        // 测试sort,limit,skip
        @Test
        public void testSLSOper() {
    
            //Query query = query(where(null)).with(new Sort(new Sort.Order(Direction.ASC, "username"))).limit(1).skip(2);
            Query query = query(where(null)).with(Sort.by(Direction.ASC, "username")).limit(1).skip(2);
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
        }
    
        // db.users.find({"lenght":{"$not":{"$gte":1.77}}}).pretty()
        // 查询高度小于1.77或者没有身高的人
        // not语句 会把不包含查询语句字段的文档 也检索出来
    
        @Test
        public void testNotOper() {
            Query query = query(where("lenght").not().gte(1.77));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
        }
    
        // -----------------------------字符串数组查询实例------------------------------------------
    
        // db.users.find({"favorites.movies":"蜘蛛侠"})
        // 查询数组中包含"蜘蛛侠"
        @Test
        public void testArray1() {
            Query query = query(where("favorites.movies").is("蜘蛛侠"));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        // db.users.find({"favorites.movies":[ "妇联4","杀破狼2", "战狼", "雷神1","神奇动物在哪里"]},{"favorites.movies":1})
        // 查询数组等于[ “杀破狼2”, “战狼”, “雷神1” ]的文档,严格按照数量、顺序;
    
        @Test
        public void testArray2() {
            Query query = query(where("favorites.movies").is(Arrays.asList("妇联4", "杀破狼2", "战狼", "雷神1", "神奇动物在哪里")));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        //数组多元素查询
        @Test
        public void testArray3() {
            // db.users.find({"favorites.movies":{"$all":[ "雷神1", "战狼"]}},{"favorites.movies":1})
            // 查询数组包含["雷神1", "战狼" ]的文档,跟顺序无关
    
            Query query = query(where("favorites.movies").all(Arrays.asList("雷神1", "战狼")));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
            //		db.users.find({"favorites.movies":{"$in":[ "雷神1", "战狼" ]}},{"favorites.movies":1})
            //		查询数组包含[“雷神1”, “战狼” ]中任意一个的文档,跟顺序无关,跟数量无关
            query = query(where("favorites.movies").in(Arrays.asList("雷神1", "战狼")));
            find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        // // db.users.find({"favorites.movies.0":"妇联4"},{"favorites.movies":1})
        // 查询数组中第一个为"妇联4"的文档
    
        @Test
        public void testArray4() {
            Query query = query(where("favorites.movies.0").is("妇联4"));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        // db.users.find({},{"favorites.movies":{"$slice":[1,2]},"favorites":1})
        // $slice可以取两个元素数组,分别表示跳过和限制的条数;
    
        @Test
        public void testArray5() {
            Query query = query(where(null));
            query.fields().include("favorites").slice("favorites.movies", 1, 2);
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        // -----------------------------对象数组查询实例------------------------------------------
    
        //db.users.find({"comments":{"author":"lison6","content":"lison评论6","commentTime":ISODate("2017-06-06T00:00:00Z")}})
        //备注:对象数组精确查找
        //坑:居然和属性定义的顺序有关
        @Test
        public void testObjArray1() throws ParseException {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Date commentDate = formatter.parse("2017-06-06 08:00:00");
            Comment comment = new Comment();
            comment.setAuthor("lison6");
            comment.setCommentTime(commentDate);
            comment.setContent("lison评论6");
    
            Query query = query(where("comments").is(comment));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        //数组多元素查询
        @Test
        public void testObjArray2() {
    
            //		查找lison1 或者 lison12评论过的user ($in查找符)
            //		db.users.find({"comments.author":{"$in":["lison1","lison12"]}}).pretty()
            //		  备注:跟数量无关,跟顺序无关;
    
            Query query = query(where("comments.author").in(Arrays.asList("lison1", "lison12")));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
    
            //		查找lison1 和 lison12都评论过的user
            //		db.users.find({"comments.author":{"$all":["lison12","lison1"]}}).pretty()
            //		 备注:跟数量有关,跟顺序无关;
    
            query = query(where("comments.author").all(Arrays.asList("lison1", "lison12")));
            find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        @Test
        //(1)注意相关的实体bean要加上注解@document,@dbRef
        //(2)spring对dbRef进行了封装,发起了两次查询请求
        public void dbRefTest() {
            System.out.println("----------------------------");
            List<User> users = tempelate.findAll(User.class);
            System.out.println("----------------------------");
            System.out.println(users);
            //		System.out.println(users.get(0).getComments());
        }
    
        private void printUsers(List<User> find) {
            for (User user : find) {
                System.out.println(user);
            }
            System.out.println(find.size());
        }
    
        //---------------------------------------------------------
    
        //查找lison5评语为包含“苍老师”关键字的user($elemMatch查找符)
        //	db.users.find({"comments":{"$elemMatch":{"author" : "lison5", "content" : { "$regex" : ".*苍老师.*"}}}})
        //备注:数组中对象数据要符合查询对象里面所有的字段,$全元素匹配,和顺序无关;
    
        @Test
        public void testObjArray3() throws ParseException {
            //		and(where("author").is("lison5"),where("content").regex(".*苍老师.*")))
            Criteria andOperator =
                new Criteria().andOperator(where("author").is("lison5"), where("content").regex(".*苍老师.*"));
            Query query = query(where("comments").elemMatch(andOperator));
            List<User> find = tempelate.find(query, User.class);
            printUsers(find);
        }
    
        /**
         * db.orders.aggregate([
         * {"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
         * {"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
         * {"$sort":{"_id":1}}
         * ])
         */
        @Test
        public void aggretionTest1() throws Exception {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Date commentDate = formatter.parse("2015-04-04 00:00:00");
            Aggregation aggs = newAggregation(match(where("orderTime").lt(commentDate)),
                project("useCode", "price", "orderTime").and(DateOperators.DateToString.dateOf("orderTime").toString("%m"))
                    .as("month"), group("useCode", "month").sum("price").as("total"), sort(Sort.by(Direction.ASC, "_id")));
    
            AggregationResults<Object> aggregate = tempelate.aggregate(aggs, "orders", Object.class);
            List<Object> mappedResults = aggregate.getMappedResults();
            System.out.println(mappedResults);
    
        }
    
        /**
         * db.orders.aggregate([{"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
         * {"$unwind":"$Auditors"},
         * {"$group":{"_id":{"Auditors":"$Auditors"},"total":{"$sum":"$price"}}},
         * {"$sort":{"_id":1}}])
         */
        @Test
        public void aggretionTest2() throws Exception {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Date commentDate = formatter.parse("2015-04-04 00:00:00");
            Aggregation aggs = newAggregation(match(where("orderTime").lt(commentDate)), unwind("Auditors"),
                group("Auditors").sum("price").as("total"), sort(Sort.by(Direction.ASC, "_id")));
    
            AggregationResults<Object> aggregate = tempelate.aggregate(aggs, "orders", Object.class);
            List<Object> mappedResults = aggregate.getMappedResults();
            System.out.println(mappedResults);
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230

    11. Mongodb 连接池配置

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ztggLtqk-1660461785671)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208040157522.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xG5tOP15-1660461785672)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208040157401.png)]

    12. 使用 dbref

    ​ 单个 bson 文档最大不能超过 16M;当文档超过 16M 的时候,就应该考虑使用引用 (DBRef)了,在主表里存储一个 id 值,指向另一个表中的 id 值。

    DBRef 语法:{ “ r e f " : < v a l u e > , " ref" : , " ref":<value>,"id” : , “$db” : }

    • $ref:引用文档所在的集合的名称;

    • $id:所在集合的_id 字段值;

    • $db:可选,集合所在的数据库实例;

    Tips:DBRef 只是关联信息的数据载体,本身并不会去关联数据

    dbref 脚本示例

    var lison = db.users.findOne({"username":"lison"}); 
    var dbref = lison.comments; 
    db[dbref.$ref].findOne({"_id":dbref.$id})
    
    • 1
    • 2
    • 3

    创建一个数据

    // 原始数据
    {
        "_id": ObjectId("62f7ceef4c3000002e005562"),
        "username": "lison",
        "address": {
            "aCode": "411000",
            "add": "长沙"
        },
        "favorites": {
            "movies": [
                "杀破狼2",
                "战狼",
                "雷神1"
            ],
            "cites": [
                "长沙",
                "深圳",
                "上海"
            ]
        },
        "salary": NumberDecimal("18889.09"),
        "length": 1.79
    }
    
    db.users.findOne({"username":"lison"})
    db.users.insert({
        "username": "dbref",
        "comments": {
            // 三个字段必须具有这个顺序, 因为查询出来的顺序就是这样的: 集合, id, 数据库, 如果是同一个数据库可以省略
            // "comments": DBRef("users", ObjectId("62f7ceef4c3000002e005562"), "mgdb")
            "$ref": "users",
            "$id": ObjectId("62f7ceef4c3000002e005562"),
    		"$db": "mgdb"
        }
    })
    
    db.users.findOne({"username":"dbref"})
    // 1
    {
        "_id": ObjectId("62f7dcb04c3000002e005568"),
        "username": "dbref",
        "comments": DBRef("users", ObjectId("62f7ceef4c3000002e005562"), "mgdb")
    }
    // 省略$db
    db.users.insert({
        "username": "dbref2",
        "comments": {
            "$ref": "users",
            "$id": ObjectId("62f7ceef4c3000002e005562")
        }
    })
    // 1
    {
        "_id": ObjectId("62f7dd3f4c3000002e005569"),
        "username": "dbref2",
        "comments": DBRef("users", ObjectId("62f7ceef4c3000002e005562"))
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    查询

    var dbref1 = db.users.findOne({"username":"dbref"}); 
    var dbref = dbref1.comments; 
    // dbref.$ref就是应用的文档, dbref.$id 就是稳定的id
    db[dbref.$ref].findOne({"_id":dbref.$id})
    
    // 1 通过username=dbref关联查询出lison的数据
    {
        "_id": ObjectId("62f7ceef4c3000002e005562"),
        "username": "lison",
        "address": {
            "aCode": "411000",
            "add": "长沙"
        },
        "favorites": {
            "movies": [
                "杀破狼2",
                "战狼",
                "雷神1"
            ],
            "cites": [
                "长沙",
                "深圳",
                "上海"
            ]
        },
        "salary": NumberDecimal("18889.09"),
        "length": 1.79
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    JAVA 客户端解析

    // dbRef测试
    // dbref其实就是关联关系的信息载体,本身并不会去关联数据
    @Test
    public void dbRefTest() {
        FindIterable<Document> find = collection.find(eq("username", "lison"));
        printOperation(find);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QicT7T0z-1660461785673)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208040207054.png)]

    Spring data mongo 解析

    @Test
    //(1)注意相关的实体bean要加上注解@document,@dbRef
    //(2)spring对dbRef进行了封装,发起了两次查询请求
    public void dbRefTest() {
        System.out.println("----------------------------");
        List<User> users = tempelate.findAll(User.class);
        System.out.println("----------------------------");
        System.out.println(users);
        //    System.out.println(users.get(0).getComments());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccNdtFLX-1660461785673)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208040209014.png)]

    13. 聚合的理解

    聚合框架就是定义一个管道,管道里的每一步都为下一步输出数据数据 (类似于 JDK8 的 Stream API,既流式编程)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yXvE6o8u-1660461785674)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208040210408.png)]

    14. 常用的管道操作

    • $project:投影,指定输出文档中的字段;
    • m a t c h :用于过滤数据,只输出符合条件的文档。 match:用于过滤数据,只输出符合条件的文档。 match:用于过滤数据,只输出符合条件的文档。match 使用 MongoDB 的标准查询操作
    • $limit:用来限制 MongoDB 聚合管道返回的文档数。
    • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
    • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
    • $group:将集合中的文档分组,可用于统计结果。
    • $sort:将输入文档排序后输出。

    $group 操作符

    $group:可以分组的数据执行如下的表达式计算:

    $sum:计算总和。

    $avg:计算平均值。

    $min:根据分组,获取集合中所有文档对应值得最小值。

    $max:根据分组,获取集合中所有文档对应值得最大值。

    15. 聚合训练

    准备测试数据

    @Document(collection = "orders")
    public class Order {
        @Id
        private String id;
    
        private String orderCode;
    
        private String useCode;
    
        private Date orderTime;
    
        private BigDecimal price;
    
        private String[] Auditors;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    public class RondomDateTest {
    
        /**
         * 获取随机日期
         *
         * @param beginDate 起始日期,格式为:yyyy-MM-dd
         * @param endDate   结束日期,格式为:yyyy-MM-dd
         * @return
         */
        public static Date randomDate(String beginDate, String endDate) {
            try {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                Date start = format.parse(beginDate);
                Date end = format.parse(endDate);
    
                if (start.getTime() >= end.getTime()) {
                    return null;
                }
    
                long date = random(start.getTime(), end.getTime());
    
                return new Date(date);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static long random(long begin, long end) {
            long rtn = begin + (long)(Math.random() * (end - begin));
            if (rtn == begin || rtn == end) {
                return random(begin, end);
            }
            return rtn;
        }
    
        public static BigDecimal randomBigDecimal(float max, float min) {
            //         float Max = 10000, Min = 1.0f;
            BigDecimal db = new BigDecimal(Math.random() * (max - min) + min);
            return db.setScale(2, BigDecimal.ROUND_HALF_UP);// 保留30位小数并四舍五入
        }
    
        @Test
        public void testRondomDate() {
            for (int i = 0; i <= 10000; i++) {
                //            Date date = randomDate("2015-01-01","2017-10-31");
                //            System.out.println(new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(date));
                BigDecimal test = randomBigDecimal(10000, 1);
                System.out.println(test.toString());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    使用 GenarateOrdersTest 产生 100000 条测试数据,代码如下

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class GenarateOrdersTest {
    
        private static final Logger logger = LoggerFactory.getLogger(GenarateOrdersTest.class);
    
        @Resource
        private MongoOperations tempelate;
    
        //随机生成orderTest数据
        @Test
        public void batchInsertOrder() {
            String[] userCodes =
                new String[] {"james", "AV", "allen", "six", "peter", "mark", "king", "zero", "lance", "deer", "lison"};
            String[] auditors = new String[] {"auditor1", "auditor2", "auditor3", "auditor4", "auditor5"};
            List<Order> list = new ArrayList<Order>();
            Random rand = new Random();
            for (int i = 0; i < 1000; i++) {
                Order order = new Order();
                int num = rand.nextInt(11);
                order.setUseCode(userCodes[num]);
                order.setOrderCode(UUID.randomUUID().toString());
                order.setOrderTime(RondomDateTest.randomDate("2015-01-01", "2017-10-31"));
                order.setPrice(RondomDateTest.randomBigDecimal(10000, 1));
                int length = rand.nextInt(5) + 1;
                String[] temp = new String[length];
                for (int j = 0; j < temp.length; j++) {
                    temp[j] = getFromArrays(temp, auditors, rand);
                }
                order.setAuditors(temp);
                list.add(order);
            }
            tempelate.insertAll(list);
        }
    
        private String getFromArrays(String[] temp, String[] auditors, Random rand) {
            String ret = null;
            boolean test = true;
            while (test) {
                ret = auditors[rand.nextInt(5)];
                int i = 0;
                for (String _temp : temp) {
                    i++;
                    if (ret.equals(_temp)) {
                        break;
                    }
                }
                if (i == temp.length) {
                    test = false;
                }
    
            }
            return ret;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    1. 查询 2015 年 4 月 3 号之前,每个用户每个月消费的总金额,并按用户名进行排序:

    db.orders.aggregate([
        {
            "$match": {
                "orderTime": {
                    "$lt": new Date("2015-04-03T16:00:00.000Z")
                }
            }
        },
        {
            "$group": {
                // 第一个key是分组
                "_id": { // 这个是重命名
                    "useCode": "$useCode",
                    "month": { // 这个也是对日志操作后的重名命名
                        // 这里的month是一个函数操作, 也可以改成$year
                        // $xx, 在:右边就是字段, 在:左边就是内置函数操作
                        "$month": "$orderTime"
                    }
                },
                // 后面的key都是分组的计算结果
                "total": {
                    "$sum": "$price"
                },
    			"avg": {
    				"$avg" : "$price"
    			}
                
            }
        },
        {
            "$sort": {
                "_id": 1
            }
        }
    ]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ayhgMHaK-1660461785674)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208131907003.png)]

    2. 查询 2015 年 4 月 3 号之前,每个审核员分别审批的订单总金额,按审核员名称进行排序

    db.orders.aggregate([
        {
            "$match": {
                "orderTime": {
                    "$lt": new Date("2015-04-03T00:00:00.000Z")
                }
            }
        },
        {
            "$unwind": "$Auditors"
        },
        {
            "$group": {
                "_id": {
                    "Auditors": "$Auditors"
                },
                "total": {
                    "$sum": "$price"
                }
            }
        },
        {
            "$sort": {
                "_id": 1
            }
        }
    ]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUMQJSuF-1660461785675)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208131909481.png)]

    3. Java 代码

    // 静态导入
    import static com.mongodb.client.model.Aggregates.*;
    import static com.mongodb.client.model.Filters.*;
    import static com.mongodb.client.model.Projections.*;
    import static com.mongodb.client.model.Sorts.ascending;
    import static com.mongodb.client.model.Sorts.orderBy;
    
    /**
     * db.orders.aggregate([
     * {"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}
     * ])
     */
    @Test
    public void aggretionTest1() throws Exception {
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(Document t) {
                logger.info("---------------------");
                System.out.println(t.toJson());
                logger.info("---------------------");
            }
        };
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-03 08:00:00");
    
        DBObject groupFileds = new BasicDBObject();
        groupFileds.put("useCode", "$useCode");
        // 这种函数操作字段的直接使用eq连接即可, 所以只要是冒号连接的都可以直接使用eq
        groupFileds.put("month", eq("$month", "$orderTime"));
    
        List<Bson> aggregates = new ArrayList<Bson>();
        aggregates.add(match(lt("orderTime", commentDate)));
        // 有专门的group方法, , 但是sum没有使用专门的方法
        aggregates.add(group(groupFileds, Accumulators.sum("sum", "$price")));
        // 有专门的sort方法
        aggregates.add(sort(eq("_id", 1)));
        AggregateIterable<Document> aggregate = orderCollection.aggregate(aggregates);
        aggregate.forEach(printBlock);
    }
    
    /**
     * db.orders.aggregate([{"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$unwind":"$Auditors"},
     * {"$group":{"_id":{"Auditors":"$Auditors"},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}])
     */
    @Test
    public void aggretionTest2() throws Exception {
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(Document t) {
                logger.info("---------------------");
                System.out.println(t.toJson());
                logger.info("---------------------");
            }
        };
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-03 08:00:00");
        List<Bson> aggregates = new ArrayList<Bson>();
        // 都有专门的函数, 但是sum没有使用专门的方法
        aggregates.add(match(lt("orderTime", commentDate)));
        aggregates.add(unwind("$Auditors"));
        aggregates.add(group("$Auditors", Accumulators.sum("sum", "$price")));
        aggregates.add(sort(eq("_id", 1)));
        AggregateIterable<Document> aggregate = orderCollection.aggregate(aggregates);
        aggregate.forEach(printBlock);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    4. Spring Data 代码

    // 静态导入
    import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
    import static org.springframework.data.mongodb.core.query.Criteria.where;
    import static org.springframework.data.mongodb.core.query.Query.query;
    
    /**
     * db.orders.aggregate([
     * {"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}
     * ])
     */
    @Test
    public void aggretionTest1() throws Exception {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-04 00:00:00");
        Aggregation aggs = newAggregation(match(where("orderTime").lt(commentDate)),
            project("useCode", "price", "orderTime").and(DateOperators.DateToString.dateOf("orderTime").toString("%m"))
                .as("month"), group("useCode", "month").sum("price").as("total"), sort(Sort.by(Direction.ASC, "_id")));
    
        AggregationResults<Object> aggregate = tempelate.aggregate(aggs, "orders", Object.class);
        List<Object> mappedResults = aggregate.getMappedResults();
        System.out.println(mappedResults);
    
    }
    
    /**
     * db.orders.aggregate([{"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
     * {"$unwind":"$Auditors"},
     * {"$group":{"_id":{"Auditors":"$Auditors"},"total":{"$sum":"$price"}}},
     * {"$sort":{"_id":1}}])
     */
    @Test
    public void aggretionTest2() throws Exception {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date commentDate = formatter.parse("2015-04-04 00:00:00");
        Aggregation aggs = newAggregation(match(where("orderTime").lt(commentDate)), unwind("Auditors"),
            group("Auditors").sum("price").as("total"), sort(Sort.by(Direction.ASC, "_id")));
    
        AggregationResults<Object> aggregate = tempelate.aggregate(aggs, "orders", Object.class);
        List<Object> mappedResults = aggregate.getMappedResults();
        System.out.println(mappedResults);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    2.4 更新

    1. 新增操作

    insertOne:插入单个文档

    insertMany:插入多个文档

    如果数据库和集合不存在,insert 操作将自动创建;

    对于插入的数据,mongoDB 自动生成 ObjectId 作为_id 字段(物理主键)

    2. 删除操作

    deleteOne(query):删除单个文档

    deleteMany(query):删除多个文档

    删除操作是不会删除索引的,就算你把数据全部删除;

    3. 修改

    更新的方法

    替换更新

    db.users.find({"username":"lison"})
    db.users.update({"username":"lison"}, {"country":"USA"}) // 替换更新, 会把除了id和第二个参数没有指定的字段全部删除
    db.users.find( {"country":"USA"})
    // 1
    {
        "_id": ObjectId("62e803f2597c0000a60068c2"),
        "country": "USA"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    操作符更新 (推荐使用)

    • 性能更好

    • 原子性操作

    db.users.update({"username":"james"},{"$set":{"country":"USA"}})
    
    • 1

    修改语法

    db.collection.update( 
        <query>, 
        <update>, 
        { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } 
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参数说明

    • query : update 的查询条件,类似 sql update 查询内 where 后面的;
    • update : update 的对象和一些更新的操作符(如 , , ,inc…)等,也可以理解为 sql update 查询内 set 后面的
    • upsert : 可选,这个参数的意思是,如果不存在 update 的记录,是否插入,true 为插入, 默认是 false,不插入。
    • multi : 可选,mongodb 默认是 false,只更新找到的第一条记录,如果这个参数为 true, 就把按条件查出来多条记录全部更新。
    • writeConcern :可选,写策略配置。

    示例

    db.users.update({"username":"cang"},{"$set":{"age":18}},{"upsert":true})
    
    • 1

    更新选择器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rlyLoIsz-1660461785676)(https://fbed-1303888495.cos.ap-guangzhou.myqcloud.com/typora/202208140015300.png)]

    删除字段示例

    这里如果使用 s e t 指定为空串就会变成空串 , 但是使用 set指定为空串就会变成空串, 但是使用 set指定为空串就会变成空串,但是使用unset指定为空串就会变成删除这个字段

    db.users.updateMany({"username":"lison"},{"$unset":{"country":"","age":""}})
    
    • 1

    更新字段名称示例

    db.users.updateMany({"username":"lison"},{"$rename":{"lenght":"height", "username":"name"}})
    
    • 1

    $each 作用示例

    将一个数组打散, 放入数组中, 而不是把整个数组当作一个数据放入数组中–拉平

    db.users.updateMany({ "username" : "james"}, { "$addToSet" : { "favorites.movies" : [ "小电影 2 " , "小电影 3"]}}) 
    db.users.updateMany({ "username" : "james"}, { "$addToSet" : { "favorites.movies" : { "$each" : [ "小电影 2 " , "小电影 3"]}}})
    
    • 1
    • 2

    删除字符串数组中元素示例

    // 不会删除数据, 因为$pull会把入参的数组看成是要删除集合数组中的一个元素, 自然是没有匹配的数据用于删除
    db.users.updateMany({ "username" : "james"}, { "$pull" : { "favorites.movies" : [ "小电影 2 " , "小 电影 3"]}}) 
    // 正确的写法
    db.users.updateMany({ "username" : "james"}, { "$pull" : { "favorites.movies" : "复仇者联盟"}}) 
    
    // 会删除所有匹配的数据
    db.users.updateMany({ "username" : "james"}, { "$pullAll" : { "favorites.movies" : [ "小电影 2 " , " 小电影 3"]}}) 
    
    // 使用$in也可以让$pull具有$pullAll的功能
    db.users.updateMany({ "username" : "james"}, { "$pull" : { "favorites.movies" : {$in:[ "小电影 2 " , "小电影 3"]}}})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    向对象数组中插入元素

    增加一条

    db.users.updateOne({
        "username": "james"
    }, {
        "$push": { // 自动添加的是数组
            "comments": { // 如果没有comments字段, 会自动添加字段
                "author": "lison23",
                "content": "ydddyyytttt",
                "commentTime": ISODate("2019-01-06T00:00:00")
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    增加两条: 使用$each

    db.users.updateOne({
        "username": "james"
    }, {
        "$push": {
            "comments": {
                "$each": [{ // 避免数组当成了一个数据插入
                    "author": "lison22",
                    "content": "yyyytttt",
                    "commentTime": ISODate("2019-07-06T00:00: 00")
                }, {
                    "author": "lison23",
                    "content": "ydddyyytttt",
                    "commentTime": ISODate("2019-06-06T00:00:00")
                }]
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    新增并排序

    db.users.updateOne({
        "username": "james"
    }, {
        "$push": {
            "comments": {
                "$each": [{ // 拉平操作
                    "author": "lison22",
                    "content": "yyyytttt",
                    "commentTime": ISODate("2019-04-06T00:00: 00")
                }, {
                    "author": "lison23",
                    "content": "ydddyyytttt",
                    "commentTime": ISODate("2019-05-06T00:00:00")
                }],
                $sort: { // 排序操作
                    "commentTime":  - 1
                }
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    删除对象数组中元素示

    db.users.update({"username":"james"}, {"$pull":{"comments":{"author":"lison22"}}})
    
    // 多字段删除
    db.users.update({"username":"lison"}, {"$pull":{"comments":{"author":"lison5", "content":"ooxx"}}})
    
    • 1
    • 2
    • 3
    • 4

    更新对象数组中元素,$符号示例

    db.users.updateMany({
        "username": "james",
        "comments.author": "lison23"
    }, {
        "$set": {
            "comments.$.content": "xxoo",
            "comments.$.author": "lison10"
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    含义:精确修改某人某一条精确的评论,如果有多个符合条件的数据,则修改第一条数据。 无法批量修改数组元素,也无法对数组元素做批量更新

    更新的注意点

    • mongodb 的更新都是原子的,mongodb 所有的写操作都是有锁的。mongoDB 2.2 之前锁 级别为实例级别,mongoDB 2.2 到 3.2 之前的版本锁级别为数据库级别,mongoDB 3.2 以后,WiredTiger 的锁级别是文档级别;
    • findAndModify 命令:在同一往返过程中原子更新文档并返回它;

    findAndModify 命令示例

    • 常规的 update 的方法不能返回更新后的数据 db.fam.update({“name”:“morris1”},{“$inc”:{“age”:1}})
    • 使用 findandModify 方法在修改数据同时返回更新前的数据或更新后的数据
    db.fam.findAndModify({query:{name:'morris1'}, update:{$inc:{age:1}}, 'new':true});
    
    • 1

    测试脚本

    JAVA 客户端实现

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class JavaUpdateObjArray {
    
        private static final Logger logger = LoggerFactory.getLogger(JavaUpdateObjArray.class);
    
        private MongoDatabase db;
    
        private MongoCollection<Document> collection;
    
        @Resource(name = "mongo")
        private MongoClient client;
    
        @Before
        public void init() {
            db = client.getDatabase("lison");
            collection = db.getCollection("users");
        }
    
        //--------------------------------------upsert demo--------------------------------------------------------------
    
        //测试upsert
        //db.users.update({"username":"cang"},{"$set":{"age":18}},{"upsert":true})
        @Test
        public void upsertTest() {
            Bson filter = eq("username", "cang");
            Bson update = set("age", 18);
            UpdateOptions upsert = new UpdateOptions().upsert(true);
            UpdateResult updateOne = collection.updateOne(filter, update, upsert);
            System.out.println(updateOne.getModifiedCount());
            System.out.println(updateOne.getUpsertedId());
    
        }
    
        //测试unset,删除字段示例
        //db.users.updateMany({"username":"lison"},{"$unset":{"country":"","age":""}})
        @Test
        public void unsetTest() {
            Bson filter = eq("username", "lison");
            Bson country = unset("country");
            Bson age = unset("age");
            Bson update = combine(country, age);
            UpdateResult updateOne = collection.updateMany(filter, update);
            System.out.println(updateOne.getModifiedCount());
            System.out.println(updateOne.getUpsertedId());
    
        }
    
        //测试rename,更新字段名称示例
        //db.users.updateMany({"username":"lison"},{"$rename":{"lenght":"height", "username":"name"}})
    
        @Test
        public void renameTest() {
            Bson filter = eq("username", "lison");
            Bson rename1 = rename("lenght", "height");
            Bson rename2 = rename("username", "name");
            Bson update = combine(rename1, rename2);
            UpdateResult updateOne = collection.updateMany(filter, update);
            System.out.println(updateOne.getModifiedCount());
            System.out.println(updateOne.getUpsertedId());
    
        }
    
        //测试pull pullAll,删除字符串数组中元素示例
        //    db.users.updateMany({ "username" : "james"}, { "$pull" : { "favorites.movies" : [ "小电影2 " , "小电影3"]}})
        //    db.users.updateMany({ "username" : "james"}, { "$pullAll" : { "favorites.movies" : [ "小电影2 " , "小电影3"]}})
    
        @Test
        public void pullAllTest() {
            Bson filter = eq("username", "james");
            Bson pull = pull("favorites.movies", Arrays.asList("小电影2 ", "小电影3"));
            UpdateResult updateOne = collection.updateMany(filter, pull);
            System.out.println(updateOne.getModifiedCount());
            System.out.println(updateOne.getUpsertedId());
    
            Bson pullAll = pullAll("favorites.movies", Arrays.asList("小电影2 ", "小电影3"));
            updateOne = collection.updateMany(filter, pullAll);
            System.out.println(updateOne.getModifiedCount());
            System.out.println(updateOne.getUpsertedId());
        }
    
        //--------------------------------------insert demo--------------------------------------------------------------
    
        //给james老师增加一条评论($push)
        //db.users.updateOne({"username":"james"},
        //                         {"$push":{"comments":{"author":"lison23",
        //                                     "content":"ydddyyytttt",
        //                                     "commentTime":ISODate("2019-01-06T00:00:00")}}})
    
        @Test
        public void addOneComment() {
            Document comment = new Document().append("author", "lison23").append("content", "ydddyyytttt")
                .append("commentTime", getDate("2019-01-06"));
            Bson filter = eq("username", "james");
            Bson update = push("comments", comment);
            UpdateResult updateOne = collection.updateOne(filter, update);
            System.out.println(updateOne.getModifiedCount());
    
        }
    
        //    给james老师批量新增两条评论($push,$each)
        //    db.users.updateOne({"username":"james"},
        //            {"$push":{"comments":
        //                       {"$each":[{"author":"lison22","content":"yyyytttt","commentTime":ISODate("2019-02-06T00:00:00")},
        //                                 {"author":"lison23","content":"ydddyyytttt","commentTime":ISODate("2019-03-06T00:00:00")}]}}})
    
        @Test
        public void addManyComment() {
            Document comment1 = new Document().append("author", "lison33").append("content", "lison33lison33")
                .append("commentTime", getDate("2019-02-06"));
            Document comment2 = new Document().append("author", "lison44").append("content", "lison44lison44")
                .append("commentTime", getDate("2019-03-06"));
    
            Bson filter = eq("username", "james");
            Bson pushEach = pushEach("comments", Arrays.asList(comment1, comment2));
            UpdateResult updateOne = collection.updateOne(filter, pushEach);
            System.out.println(updateOne.getModifiedCount());
    
        }
    
        //    给james老师批量新增两条评论并对数组进行排序($push,$eachm,$sort)
        //    db.users.updateOne({"username":"james"},
        //           {"$push": {"comments":
        //                     {"$each":[ {"author":"lison22","content":"yyyytttt","commentTime":ISODate("2019-04-06T00:00:00")},
        //                                {"author":"lison23","content":"ydddyyytttt","commentTime":ISODate("2019-05-06T00:00:00")} ],
        //                       $sort: {"commentTime":-1} } } })
    
        @Test
        public void addManySortComment() {
            Document comment1 = new Document().append("author", "lison00").append("content", "lison00lison00")
                .append("commentTime", getDate("2019-04-06"));
            Document comment2 = new Document().append("author", "lison01").append("content", "lison01lison01")
                .append("commentTime", getDate("2019-05-06"));
    
            Bson filter = eq("username", "james");
    
            Document sortDoc = new Document().append("commentTime", -1);
            PushOptions pushOption = new PushOptions().sortDocument(sortDoc);
    
            Bson pushEach = pushEach("comments", Arrays.asList(comment1, comment2), pushOption);
    
            UpdateResult updateOne = collection.updateOne(filter, pushEach);
            System.out.println(updateOne.getModifiedCount());
    
        }
    
        //--------------------------------------delete demo--------------------------------------------------------------
    
        //    删除lison1对james的所有评论 (批量删除)
        //    db.users.update({"username":“james"},
        //                               {"$pull":{"comments":{"author":"lison33"}}})
    
        @Test
        public void deleteByAuthorComment() {
            Document comment = new Document().append("author", "lison33");
            Bson filter = eq("username", "james");
            Bson update = pull("comments", comment);
            UpdateResult updateOne = collection.updateOne(filter, update);
            System.out.println(updateOne.getModifiedCount());
        }
    
        //    删除lison5对lison评语为“lison是苍老师的小迷弟”的评论(精确删除)
        //    db.users.update({"username":"lison"},
        //            {"$pull":{"comments":{"author":"lison5",
        //                                  "content":"lison是苍老师的小迷弟"}}})
        @Test
        public void deleteByAuthorContentComment() {
            Document comment = new Document().append("author", "lison5").append("content", "lison是苍老师的小迷弟");
            Bson filter = eq("username", "lison");
            Bson update = pull("comments", comment);
            UpdateResult updateOne = collection.updateOne(filter, update);
            System.out.println(updateOne.getModifiedCount());
        }
    
        //--------------------------------------update demo--------------------------------------------------------------
        //    db.users.updateMany({"username":"james","comments.author":"lison01"},
        //            {"$set":{"comments.$.content":"xxoo",
        //                        "comments.$.author":"lison10" }})
        //     含义:精确修改某人某一条精确的评论,如果有多个符合条件的数据,则修改最后一条数据。无法批量修改数组元素
        @Test
        public void updateOneComment() {
            Bson filter = and(eq("username", "james"), eq("comments.author", "lison01"));
            Bson updateContent = set("comments.$.content", "xxoo");
            Bson updateAuthor = set("comments.$.author", "lison10");
            Bson update = combine(updateContent, updateAuthor);
            UpdateResult updateOne = collection.updateOne(filter, update);
            System.out.println(updateOne.getModifiedCount());
    
        }
    
        //--------------------------------------findandModify demo--------------------------------------------------------------
        //使用findandModify方法在修改数据同时返回更新前的数据或更新后的数据
        //db.fam.findAndModify({query:{name:'morris1'},
        //    update:{$inc:{age:1}},
        //    'new':true});
    
        @Test
        public void findAndModifyTest() {
            Bson filter = eq("name", "morris1");
            Bson update = inc("age", 1);
            //   //实例化findAndModify的配置选项
            FindOneAndUpdateOptions fauo = new FindOneAndUpdateOptions();
            //   //配置"new":true
            fauo.returnDocument(ReturnDocument.AFTER);//
            MongoCollection<Document> numCollection = db.getCollection("fam");
            Document ret = numCollection.findOneAndUpdate(filter, update, fauo);
            System.out.println(ret.toJson());
        }
    
        private Date getDate(String string) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    
            Date parse = null;
            try {
                parse = sdf.parse(string);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return parse;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222

    Spring Data 实现

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringUpdateObjArray {
    
        private static final Logger logger = LoggerFactory.getLogger(SpringUpdateObjArray.class);
    
        @Resource
        private MongoOperations tempelate;
    
        //--------------------------------------upsert demo--------------------------------------------------------------
    
        //测试upsert
        //db.users.update({"username":"cang"},{"$set":{"age":18}},{"upsert":true})
        @Test
        public void upsertTest() {
            Query query = query(Criteria.where("username").is("cang"));
            Update set = new Update().set("age", 18);
            UpdateResult upsert = tempelate.upsert(query, set, User.class);
            System.out.println(upsert.getModifiedCount());
            System.out.println(upsert.getUpsertedId());
    
        }
    
        //测试unset,删除字段示例
        //db.users.updateMany({"username":"lison"},{"$unset":{"country":"","age":""}})
        @Test
        public void unsetTest() {
            Query query = query(Criteria.where("username").is("lison"));
            Update unset = new Update().unset("country").unset("age");
    
            UpdateResult upsert = tempelate.updateMulti(query, unset, User.class);
            System.out.println(upsert.getModifiedCount());
        }
    
        //测试rename,更新字段名称示例
        //db.users.updateMany({"username":"lison"},{"$rename":{"lenght":"height", "username":"name"}})
    
        @Test
        public void renameTest() {
            Query query = query(Criteria.where("username").is("lison"));
            Update rename = new Update().rename("lenght", "height").rename("username", "name");
            UpdateResult upsert = tempelate.updateMulti(query, rename, User.class);
            System.out.println(upsert.getModifiedCount());
    
        }
    
        //测试pull pullAll,删除字符串数组中元素示例
        //    db.users.updateMany({ "username" : "james"}, { "$pull" : { "favorites.movies" : [ "小电影2 " , "小电影3"]}})
        //    db.users.updateMany({ "username" : "james"}, { "$pullAll" : { "favorites.movies" : [ "小电影2 " , "小电影3"]}})
    
        @Test
        public void pullAllTest() {
    
            Query query = query(Criteria.where("username").is("james"));
            Update pull = new Update().pull("favorites.movies", Arrays.asList("小电影2 ", "小电影3"));
            UpdateResult upsert = tempelate.updateMulti(query, pull, User.class);
            System.out.println(upsert.getModifiedCount());
    
            query = query(Criteria.where("username").is("james"));
            Update pullAll = new Update().pullAll("favorites.movies", new String[] {"小电影2 ", "小电影3"});
            upsert = tempelate.updateMulti(query, pullAll, User.class);
            System.out.println(upsert.getModifiedCount());
        }
    
        //--------------------------------------insert demo--------------------------------------------------------------
    
        //给james老师增加一条评论($push)
        //db.users.updateOne({"username":"james"},
        //                         {"$push":{"comments":{"author":"lison23",
        //                                     "content":"ydddyyytttt",
        //                                     "commentTime":ISODate("2019-01-06T00:00:00")}}})
        @Test
        public void addOneComment() {
            Query query = query(Criteria.where("username").is("james"));
            Comment comment = new Comment();
            comment.setAuthor("lison23");
            comment.setContent("ydddyyytttt");
            comment.setCommentTime(getDate("2019-01-06"));
            Update push = new Update().push("comments", comment);
            UpdateResult updateFirst = tempelate.updateFirst(query, push, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //    给james老师批量新增两条评论($push,$each)
        //  db.users.updateOne({"username":"james"},
        //            {"$push":{"comments":
        //                       {"$each":[{"author":"lison22","content":"yyyytttt","commentTime":ISODate("2019-02-06T00:00:00")},
        //                                 {"author":"lison23","content":"ydddyyytttt","commentTime":ISODate("2019-03-06T00:00:00")}]}}})
        @Test
        public void addManyComment() {
            Query query = query(Criteria.where("username").is("james"));
            Comment comment1 = new Comment();
            comment1.setAuthor("lison55");
            comment1.setContent("lison55lison55");
            comment1.setCommentTime(getDate("2019-02-06"));
            Comment comment2 = new Comment();
            comment2.setAuthor("lison66");
            comment2.setContent("lison66lison66");
            comment2.setCommentTime(getDate("2019-03-06"));
            //Update push = new Update().pushAll("comments", new Comment[]{comment1,comment2});
            //Update push = new Update().push("comments", new Comment[]{comment1,comment2});
            Update push = new Update().push("comments").each(new Comment[] {comment1, comment2});
            UpdateResult updateFirst = tempelate.updateFirst(query, push, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //  给james老师批量新增两条评论并对数组进行排序($push,$eachm,$sort)
        //  db.users.updateOne({"username":"james"},
        //           {"$push": {"comments":
        //                     {"$each":[ {"author":"lison22","content":"yyyytttt","commentTime":ISODate("2019-04-06T00:00:00")},
        //                                {"author":"lison23","content":"ydddyyytttt","commentTime":ISODate("2019-05-06T00:00:00")} ],
        //                       $sort: {"commentTime":-1} } } })
        @Test
        public void addManySortComment() {
            Query query = query(Criteria.where("username").is("james"));
            Comment comment1 = new Comment();
            comment1.setAuthor("lison77");
            comment1.setContent("lison55lison55");
            comment1.setCommentTime(getDate("2019-04-06"));
            Comment comment2 = new Comment();
            comment2.setAuthor("lison88");
            comment2.setContent("lison66lison66");
            comment2.setCommentTime(getDate("2019-05-06"));
    
            Update update = new Update();
            PushOperatorBuilder pob = update.push("comments");
            pob.each(comment1, comment2);
            pob.sort(Sort.by(Direction.DESC, "commentTime"));
    
            System.out.println("---------------");
            UpdateResult updateFirst = tempelate.updateFirst(query, update, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //--------------------------------------delete demo--------------------------------------------------------------
    
        //    删除lison1对james的所有评论 (批量删除)
        //    db.users.update({"username":“james"},
        //                               {"$pull":{"comments":{"author":"lison23"}}})
    
        @Test
        public void deleteByAuthorComment() {
            Query query = query(Criteria.where("username").is("james"));
    
            Comment comment1 = new Comment();
            comment1.setAuthor("lison55");
    
    
    /*    BasicDBObject comment1 = new BasicDBObject ();
          comment1.put("author","lison23");*/
    
            Update pull = new Update().pull("comments", comment1);
            UpdateResult updateFirst = tempelate.updateFirst(query, pull, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //    删除lison5对lison评语为“lison是苍老师的小迷弟”的评论(精确删除)
        //    db.users.update({"username":"lison"},
        //            {"$pull":{"comments":{"author":"lison5",
        //                                  "content":"lison是苍老师的小迷弟"}}})
        @Test
        public void deleteByAuthorContentComment() {
            Query query = query(Criteria.where("username").is("lison"));
            Comment comment1 = new Comment();
            comment1.setAuthor("lison5");
            comment1.setContent("lison是苍老师的小迷弟");
            Update pull = new Update().pull("comments", comment1);
            UpdateResult updateFirst = tempelate.updateFirst(query, pull, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //--------------------------------------update demo--------------------------------------------------------------
        //    db.users.updateMany({"username":"james","comments.author":"lison1"},
        //            {"$set":{"comments.$.content":"xxoo",
        //                        "comments.$.author":"lison10" }})
        //     含义:精确修改某人某一条精确的评论,如果有多个符合条件的数据,则修改最后一条数据。无法批量修改数组元素
        @Test
        public void updateOneComment() {
            Query query = query(where("username").is("lison").and("comments.author").is("lison4"));
            Update update = update("comments.$.content", "xxoo").set("comments.$.author", "lison11");
            UpdateResult updateFirst = tempelate.updateFirst(query, update, User.class);
            System.out.println(updateFirst.getModifiedCount());
        }
    
        //--------------------------------------findandModify demo--------------------------------------------------------------
    
        //使用findandModify方法在修改数据同时返回更新前的数据或更新后的数据
        //db.fam.findAndModify({query:{name:'morris1'},
        //    update:{$inc:{age:1}},
        //    'new':true});
    
        @Test
        public void findAndModifyTest() {
            Query query = query(where("name").is("morris1"));
            Update update = new Update().inc("age", 1);
            FindAndModifyOptions famo = FindAndModifyOptions.options().returnNew(true);
    
            Doc doc = tempelate.findAndModify(query, update, famo, Doc.class);
            System.out.println(doc.toString());
        }
    
        private Date getDate(String string) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    
            Date parse = null;
            try {
                parse = sdf.parse(string);
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return parse;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215

    4. 查询实战演练

    需求描述

    A. 查看一个人的信息,打开页面只显示三条评论

    B. 点击评论的下一页按钮,新加载三条评论

    C. 默认按照评论时间降序,但是也可以选择按照姓名排序

    难点

    A. 数组中数据的排序问题?

    B. 数组中的数据怎么按照指定的方式进行排序?

    C. 每次仅仅加载三条评论信息(可以包含 id 字段)?

    提示

    A. 添加数据时注意排序

    B. 查询的时候投影是有技巧的

    C. 排序考虑聚合?

    脚本

    (1)考虑到默认顺序,所以新增评论时,使用$sort 运算符按照评论时间降序排序;

    db.users.updateOne({
        username: "lison"
    }, {
        $push: {
            comments: {
                $each: [{
                    author: "james",
                    content: "xxoo",
                    commentTime: ISODate("2018-01-01T04:09:00.000Z")
                }],
                $sort: {
                    commcommentTime: - 1
                }
            }
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意: s o r t 操作符必须和 sort 操作符必须和 sort操作符必须和each 配合使用

    (2)由于评论已经按照时间降序排序,所以查看人员时直接加载最新的三条评论;

    db.users.find({
        "username": "lison"
    }, {
        "comments": {
            "$slice": [0, 3]
        }
    }).pretty()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (3)点击评论的下一页按钮,新加载后三条评论(注意:仅仅加载评论的数据,人员信息 不加载)

    db.users.find({
        "username": "lison"
    }, {
        "comments": { // 投影还可以进行分片操作, 没有值确定0,1就默认是全是1
            "$slice": [3, 3]
        },
        "$id": 1
    }).pretty();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (4)如果有多种排序需求怎么处理?使用聚合

    db.users.aggregate([{
        "$match": {
            "username": "lison"
        }
    }, {
        "$unwind": "$comments"
    }, {
        $sort: {
            "comments.commentTime": 1
        }
    }, {
        "$project": {
            "comments": 1
        }
    }, {
        "$skip": 0
    }, {
        "$limit": 3
    }])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    代码实现

    // 新增评论时,使用$sort运算符进行排序,插入评论后,再按照评论时间降序排序
    public void demoStep1() {
        Bson filter = eq("username", "lison");
        Document comment =
            new Document().append("author", "cang").append("content", "lison是我的粉丝").append("commentTime", new Date());
        // $sort: {"commentTime":-1}
        Document sortDoc = new Document().append("commentTime", -1);
        PushOptions sortDocument = new PushOptions().sortDocument(sortDoc);
        // $each
        Bson pushEach = Updates.pushEach("comments", Arrays.asList(comment), sortDocument);
    
        UpdateResult updateOne = collection.updateOne(filter, pushEach);
        System.out.println(updateOne.getModifiedCount());
    }
    
    @Test
    // 查看人员时加载最新的三条评论;
    // db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty()
    public void demoStep2() {
        FindIterable<Document> find = collection.find(eq("username", "lison")).projection(slice("comments", 0, 3));
        printOperation(find);
    }
    
    @Test
    // 点击评论的下一页按钮,新加载三条评论
    // db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$id":1}).pretty();
    public void demoStep3() {
        // {"username":"lison"}
        Bson filter = eq("username", "lison");
        // "$slice":[3,3]
        Bson slice = slice("comments", 3, 3);
        // "$id":1
        Bson includeID = include("id");
    
        // {"comments":{"$slice":[3,3]},"$id":1})
        Bson projection = fields(slice, includeID);
    
        FindIterable<Document> find = collection.find(filter).projection(projection);
        printOperation(find);
    }
    
    @Test
    /**
     * db.users.aggregate([{"$match":{"username":"lison"}},
     {"$unwind":"$comments"},
     {$sort:{"comments.commentTime":-1}},
     {"$project":{"comments":1}},
     {"$skip":6},
     {"$limit":3}])
     */
    // 如果有多种排序需求怎么处理,使用聚合
    public void demoStep4() {
        final List<Document> ret = new ArrayList<Document>();
        Block<Document> printBlock = getBlock(ret);
        List<Bson> aggregates = new ArrayList<Bson>();
    
        aggregates.add(match(eq("username", "lison")));
        aggregates.add(unwind("$comments"));
        aggregates.add(sort(orderBy(ascending("comments.commentTime"))));
        aggregates.add(project(fields(include("comments"))));
        aggregates.add(skip(0));
        aggregates.add(limit(3));
    
        AggregateIterable<Document> aggregate = collection.aggregate(aggregates);
    
        printOperation(ret, printBlock, aggregate);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    2.5 其他命令

    其他常用命令

    show dbs :显示数据库列表

    show collections :显示集合列表

    db : 显示当前数据库

    db.stats() :显示数据库信息

    db.serverStatus() : 查看服务器状态

    db.dropDatabase():删除数据库

    db.help(),db.collection.help():内置帮助,显示各种方法的说明;

    db.users.find().size():获取查询集合的数量;

    db.users.drop():删除集合;

    MongoDB 怎么优雅关机?

    在生产环境,不要用 kill -9 关掉 mongodb 的进程,很可能造成 mongodb 的数据丢失;

    优雅的关机:

    • 第一种方式
    use admin 
    
    db.shutdownServer() 
    
    • 1
    • 2
    • 3
    • 第二种方式
    mongod --shutdown -f mongodb.conf (service mongodb start) 
    
    mongod --shutdown -f /soft/mongodb/conf/mgdb.conf --auth 
    
    • 1
    • 2
    • 3

    数据管理命令

    • 数据备份 mongodump

    mongodump -p 27022 -d mgdb-o /soft/backup

    -p :端口; -d :备份的数据库名称 ; -o:指定备份的路径

    其本质为:执行查询,然后写入文件;

    • 数据恢复 mongorestore

    mongorestore -p 27022

    -d mydb /soft/backup/lison --drop

    –drop 已存在 lison 库则删除原数据库,去掉–drop 则是合并

    • 数据导出 mongoexport**(针对集合)**

    mongoexport -p 27022 -d lison -c users -f id,username,age,salary --type=json -o

    /soft/backup/users.json

    -c :指定导出的集合; -f :要导出的字段; --type:导出的文件格式类型[csv,json]

    • 数据导入 mongoimport(针对集合)

    mongoimport -p 27022 -d lison -c users /soft/backup/users.json --upsert

    –upsert 表示更新现有数据,如果不适用—upsert,则导入时已经存在的文档会报 id 重复,数据不再插入,也可以使用—drop 删除原有数据

    2.6.安全

    Role-Based Access Control 基于角色的访问控制

    image-20220814160240449

    image-20220814160308434

    客户端授权

    shell 脚本创建用

    db.createUser({
        'user': 'boss',
        'pwd': 'boss',
        'roles': [{
            'role': 'userAdminAnyDatabase',
            'db': 'admin'
        }]
    });
    
    
    db.createUser({
        'user': 'lison',
        'pwd': 'lison',
        'roles': [{
            'role': 'readWrite',
            'db': 'lison'
        }]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    服务器启动需要加上 auth 参数连接服务器才需要验证

    mongod -f /soft/mongodb/conf/mgdb.conf --auth

    切换到数据库上,才能给当前数据库创建用户

    MongoDB 权限初始化过程

    1. 启动 mongodb

    2. 数据库增加安全模式后,初始化一个“userAdminAnyDatabase”非常重要

    通过客户端连接,使用 admin 数据库, 执行如下脚本:

    db.createUser({
        'user': 'boss',
        'pwd': 'boss',
        'roles': [{
            'role': 'userAdminAnyDatabase',
            'db': 'admin'
        }]
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 使用刚创建成功的用户登录:db.auth(“boss”,“boss”);

    2. 切换到 lison 数据库(use lison),创建读写权限用户:

    db.createUser({
        'user': 'lison',
        'pwd': 'lison',
        'roles': [{
            'role': 'readWrite',
            'db': 'lison'
        }]
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 使用读写权限用户 lison 登录,db.auth(“lison”,“lison”),登录后测试;

    也可以以非 auth 模式启动,然后创建用户后,用 auth 模式启动

    db.createUser({
        'user': 'root',
        'pwd': 'root',
        'roles': [{
            'role': 'root',
            'db': 'admin'
        }]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Java 客户端安全认证

    MongoCredential 类包括每个受支持的身份验证机制的静态工厂方法。

    public static MongoCredential createCredential(final String userName, final String database, final char[] password) {
        return new MongoCredential(null, userName, database, password);
    }
    
    • 1
    • 2
    • 3
    import static com.mongodb.client.model.Filters.in;
    
    public class JavaAuthTest {
    
        private static final Logger logger = LoggerFactory.getLogger(JavaAuthTest.class);
    
        private MongoDatabase db;
    
        private MongoCollection<Document> collection;
    
        private MongoClient client;
    
        @Before
        public void init() {
            MongoCredential createCredential = MongoCredential.createCredential("lison", "lison", "lison".toCharArray());
            MongoClientOptions mco =
                MongoClientOptions.builder().writeConcern(WriteConcern.JOURNALED).connectionsPerHost(100)
                    .readPreference(ReadPreference.secondary()).threadsAllowedToBlockForConnectionMultiplier(5)
                    .maxWaitTime(120000).connectTimeout(10000).build();
            List<ServerAddress> asList = Arrays.asList(new ServerAddress("localhost", 27017));
            this.client = new MongoClient(asList, createCredential, mco);
            db = client.getDatabase("lison");
            collection = db.getCollection("users");
        }
    
        // -----------------------------操作符使用实例------------------------------------------
    
        // db.users.find({"username":{"$in":["lison", "mark", "james"]}}).pretty()
        // 查询姓名为lison、mark和james这个范围的人
        @Test
        public void testInOper() {
            Bson in = in("username", "lison", "mark", "james");
            FindIterable<Document> find = collection.find(in);
            printOperation(find);
        }
    
        // ---------------------------------------------------------------------------
    
        //返回对象的处理器,打印每一行数据
        private Block<Document> getBlock(final List<Document> ret) {
            Block<Document> printBlock = new Block<Document>() {
                @Override
                public void apply(Document t) {
                    logger.info("---------------------");
                    logger.info(t.toJson());
                    logger.info("---------------------");
                    ret.add(t);
                }
            };
            return printBlock;
        }
    
        //打印查询出来的数据和查询的数据量
        private void printOperation(FindIterable<Document> find) {
            final List<Document> ret = new ArrayList<Document>();
            Block<Document> printBlock = getBlock(ret);
            find.forEach(printBlock);
            System.out.println(ret.size());
            ret.removeAll(ret);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    Spring 客户端安全认证

    <mongo:mongo-client id="mongo" host="localhost" port="27017">
        <mongo:client-options
                write-concern="ACKNOWLEDGED"
                threads-allowed-to-block-for-connection-multiplier="5"
                max-wait-time="1200"
                connect-timeout="1000"/>
    mongo:mongo-client>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    fgetc/fputc 和 fgets/fputs 的详细用法
    Java知识点--IO流(上)
    【JVM笔记】方法区的内部结构与运行时常量池
    如何给PDF文件添加线形式的注释?怎么添加PDF删除线?
    打破对ChatGPT的依赖以及如何应对ChatGPT的错误和幻觉
    wsl2安装百度apollo及其基本配置
    第五届“传智杯”全国大学生计算机大赛(练习赛)传智杯 #5 练习赛] 平等的交易
    Vuex入门
    揭秘”智能定投“
    2022安洵杯babybf
  • 原文地址:https://blog.csdn.net/yin18827152962/article/details/126331733