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

配置文档

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 #是否需要验证权限登录(用户名和密码) 

修改环境变量

vi /etc/profile 

export MONGODB_HOME=/soft/mongodb 

export PATH=$PATH:$MONGODB_HOME/bin 

source /etc/profile

配置开机启动

vi /usr/lib/systemd/system/mongodb.service
[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

开机启动

systemctl daemon-reload 

systemctl start mongodb 

systemctl enable mongodb

启动 mongodb

service mongodb stop 

service mongodb start

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

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

检测是否启动: 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

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

mongo.exe

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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

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

win+r

services.msc

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 
}

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);

复杂点的查询

// 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"]}}});

3. 事务

关系数据的事务

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

错误示例

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()

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

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(); // 中断回滚事务

2.3 查询

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

pom依赖坐标

<dependency> 
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId> 
    <version>3.11.2</version> 
</dependency>
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;
    }
}

实例化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");
}

插入数据

@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));

}

查询数据

@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()));
}

更新数据

@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()));
}

删除数据

@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()));//打印受影响的行数
}

事务操作

@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();
}
POJO方式

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

Favorites

public class Favorites {
   private List<String> movies;
   private List<String> cites;
}

Address

public class Address {   
   private String aCode;
   private String add;
}

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;
}

初始化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);
}

插入数据

@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));

}

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

//文档集合
private MongoCollection<Document> doc; // 这个doc的collection可以带泛型, Document方式
private MongoCollection<User> doc; // User方式
@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()));

}

更新数据

@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()));
}

删除数据

@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()));//打印受影响的行数
}

两种方式的总结:

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

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

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

2. Spring-data-mongodb 客户端

pom依赖坐标

<dependency> 
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>2.2.1.RELEASE</version> 
</dependency>

applicationContext.xml创建bean

<!-- mongodb连接池配置 -->
<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>

<!-- mongodb数据库工厂配置 -->
<mongo:db-factory dbname="lison" mongo-ref="mongo"/>


<!-- mongodb模板配置 -->
<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>

使用mongoTemplate

@Resource 
private MongoOperations tempelate;

插入数据: 封装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));
}

查询: 返回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());
    }
}

更新: 就像是封装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());
}

删除: 封装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());
}

事务: 直接使用注解即可

@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);
}

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. 新增 BigDecimalToDecimal128Converter

Converter: 转换器

public class BigDecimalToDecimal128Converter implements Converter<BigDecimal, Decimal128> {
    @Override
    public Decimal128 convert(BigDecimal source) {
        return new Decimal128(source);
    }
}
2. 新增 Decimal128ToBigDecimalConverter
public class Decimal128ToBigDecimalConverter implements Converter<Decimal128, BigDecimal> {
    @Override
    public BigDecimal convert(Decimal128 source) {
        return source.bigDecimalValue();
    }
}
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>
<!--注入转换器-->
<!-- mongodb模板配置 -->
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    <constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>
<!--mappingConverter对应标签的mongo:mapping-converter-->
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
}

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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();

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

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

// 这参数竟然不是json形式的入参
db.users.distinct("username");
字符串数组选择查询
  • 数组单元素查询
db.users.find({"favorites.movies":"流浪地球"})
  • 数组精确查找
// 查询数组 严格按照数量、顺序相等
db.users.find({"favorites.movies":["战神", "雷神","不死鸟"]}, {"favorites.movies":1})
  • 数多元素查询
// 内容数量相等, 和顺序无关
db.users.find({"favorites.movies":{"$all":["雷神1", "天堂"]}})
// 内容包含, 和数据顺序无关
db.users.find({"favorites.movies":{"$in":["雷神1", "天堂"]}})
  • 索引查询(数组索引)
// 数组的第一个索引位置
db.users.find({"favorites.movies.0":"鬼吹灯",{"favorites.movies":1}})
  • 返回数组子集
// $slice 可以取两个元素数组, 分别表示跳过和限制条数
db.users.find({}, {"favorites.movies":{"$slice":[1,2]}, "favorites":1})
// 可以和下面结果对比
db.users.find({},{"favorites":1})
对象数组选择查询
  • 单元素查询
// 说明: comments是一个数组
db.users.find({"comments":{"author":"lison6", "context": "xxx"}, "commentTime": ISODate("2017-06-06T00:00:00Z")})
  • 查找 lison1 或者 lison12 评论过的 user ($in 查找符)
// 备注:跟数量无关,跟顺序无关;
db.users.find({"comments.author":{"$in":["lison1", "lison2"]}})
  • 查找 lison1 和 lison12 都评论过的 user
// 备注:跟数量有关,跟顺序无关;
db.users.find({"comments.author":{"$all":["lison12","lison1"]}}).pretty()
  • 查找 lison5 评语为包含"早上好"关键字的 user($elemMatch 查找符)
// 备注:数组中对象数据要符合查询对象里面所有的字段,全元素匹配,和顺序无关;
// $elemMatch: 匹配么一个元素
db.users.find({"comments":{"$elemMatch":{"author":"lison5", "content":{"$regex":".*早上好.*"}}}})

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;
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");
}
实例

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);
}

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);
}

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);
}

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);
}

字符串数组

// 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);
}

对象数组查询实例

//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);
}

聚合操作

/**
 * 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);
}

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);
}

10. Spring mongodb 解析

依赖

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

配置文件: applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <context:component-scan base-package="len.hgy">

    </context:component-scan>

    <!-- mongodb连接池配置 -->
    <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>

    <!-- mongodb数据库工厂配置 -->
    <mongo:db-factory dbname="lison" mongo-ref="mongo"/>


    <!-- mongodb模板配置 -->
    <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>

测试用例

@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);

    }

}

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" : <value>, " 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})

创建一个数据

// 原始数据
{
    "_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"))
}

查询

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
}

JAVA 客户端解析

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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());
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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;
}
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());
        }
    }
}

使用 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. 查询 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
        }
    }
]);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
        }
    }
]);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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);
}
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);

}

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"
}

操作符更新 (推荐使用)

  • 性能更好

  • 原子性操作

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

修改语法

db.collection.update( 
    <query>, 
    <update>, 
    { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } 
)

参数说明

  • 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})
更新选择器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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":""}})

更新字段名称示例

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

$each 作用示例

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

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

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

// 不会删除数据, 因为$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"]}}})

向对象数组中插入元素

增加一条

db.users.updateOne({
    "username": "james"
}, {
    "$push": { // 自动添加的是数组
        "comments": { // 如果没有comments字段, 会自动添加字段
            "author": "lison23",
            "content": "ydddyyytttt",
            "commentTime": ISODate("2019-01-06T00:00:00")
        }
    }
})

增加两条: 使用$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")
            }]
        }
    }
})

新增并排序

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
            }
        }
    }
})

删除对象数组中元素示

db.users.update({"username":"james"}, {"$pull":{"comments":{"author":"lison22"}}})

// 多字段删除
db.users.update({"username":"lison"}, {"$pull":{"comments":{"author":"lison5", "content":"ooxx"}}})

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

db.users.updateMany({
    "username": "james",
    "comments.author": "lison23"
}, {
    "$set": {
        "comments.$.content": "xxoo",
        "comments.$.author": "lison10"
    }
})

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

更新的注意点
  • 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});

测试脚本

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;
    }

}
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;
    }

}

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
            }
        }
    }
});

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

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

db.users.find({
    "username": "lison"
}, {
    "comments": {
        "$slice": [0, 3]
    }
}).pretty()

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

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

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

db.users.aggregate([{
    "$match": {
        "username": "lison"
    }
}, {
    "$unwind": "$comments"
}, {
    $sort: {
        "comments.commentTime": 1
    }
}, {
    "$project": {
        "comments": 1
    }
}, {
    "$skip": 0
}, {
    "$limit": 3
}])
代码实现
// 新增评论时,使用$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);
}

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() 
  • 第二种方式
mongod --shutdown -f mongodb.conf (service mongodb start) 

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

数据管理命令

  • 数据备份 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'
    }]
})

服务器启动需要加上 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. 使用刚创建成功的用户登录:db.auth(“boss”,“boss”);

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

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

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

db.createUser({
    'user': 'root',
    'pwd': 'root',
    'roles': [{
        'role': 'root',
        'db': 'admin'
    }]
})
Java 客户端安全认证

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

public static MongoCredential createCredential(final String userName, final String database, final char[] password) {
    return new MongoCredential(null, userName, database, password);
}
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);
    }
}
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>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岁月玲珑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值