一,基础知识
数据存储的json字符串
分片场景 多文档 支持分片 当关系型数据库使用也可以
表 相当于 集合
行 相当于 文档
列 相当于 字段灵活 json 加字段 加集合 敏捷式快速开发
横向扩展能力强 添加分片节点方便
二,命令行操作
服务命令
--fork 后台启动
--bind ip 监听远程ip 允许远程拦截
进入MongoDB安装目录
bin/mongod --port=27017 --dbpath=/opt/mongodb4/data --logpath=/opt/mongodb4/log/mongodb \--bind_ip=0.0.0.0 --fork启动命令
/opt/mongodb4/bin/mongod --port=27017 --dbpath=/opt/mongodb4/data --logpath=/opt/mongodb4/log/mongodb \--bind_ip=0.0.0.0 --fork
数据库操作
//选择使用goods数据库 数据库不存在 则自动创建
use goods//查看所有数据库命令
show dbs 或者 show databases
表操作
//创建集合相当于创建表
//创建goods集合/表 用来存储数据
db.createCollection(goods)
//集合创建:名字不能为空字符串 不能含有空字符 不能以system开头 不能含有符号$
向集合中插入一个文档的时候,如果集合不存在 则自动创建集合
//删除集合
db.goods.drop()
文档操作
文档操作
文档 row 相当于表里的行数据 文档相当于json字符串 键值对 数据存贮
插入添加
//插入:使用insert() 或者 save()方法
//插入单条
db.goods.insert({title:"小米",price:3999})
//批量插入多条数据
db.goods.insertMany([{title:"小米",price:3999},{title:"华为",price:3999},{title:"apple",price:3999}])
//插入数据 相当于插入多个json字符串
//文档 json中键值对是有序的,不能有重复的键,区分大小写,键不能含有空字符
修改数据
//语法
db.goods.update(query,update,options)
//修改title为小米的记录 price修改为4000整数
db.goods.update({title:"小米"},{$set:{price:NumberInt(4000)}})
//更改title为小米的数据 price改为4000 默认值修改第一条数据
db.goods.update({title:"小米"},{$set:{price:NumberInt(4000)}})
//修改所有符合条件的数据 加后面条件 multi:true
db.goods.update({title:"小米"},{$set:{price:NumberInt(4000)}},{multi:true})
删除
//删除数据 ({})中为删除时参数
db.goods.remove({})
//删除title为小米的数据
db.goods.remove({title:"小米"})
查询
//语法
db.goods.find(query,projection)
//查询所有
db.goods.find() 或者 db.goods.find({})
//查询title为小米的数据
db.goods.find({title:"小米"})
//查询指定列数据 title为小米的 数据 显示price name id 三列 id列默认显示 price:1 即为显示
db.goods.find({title:"小米",{price:1,name:1}})
//查询指定列数据 title为小米的 数据 显示price name 两列 id列不显示
db.goods.find({title:"小米",{price:1,name:1,id:0}})
分页
//使用limit()来指定显示多少行数据 skip() 来指定跳过多少行数据
db.goods.find(title:"小米").skip(10).limit(5)
//分页查询title为小米的 跳过10行 显示5行 11-15条数据
排序
db.goods.find().sort({排序方式})
//对查询出来title为小米的 数据 进行price 降序排序 name 进行升序排序 1升序 -1降序
db.goods.find(title:"小米").sort({price:-1,name:1})
模糊查询
//title中包含小米的
db.goods.find({title:"/小米/"})
//title中以小米开头的
db.goods.find({title:"/^小米/"})
比较查询
db.goods.find({price:{<: NumberInt(3000)}})
//查询price小于3000的数据
//$gt; 大于 $gte; 大于等于 $lte; 小于等于 $ne 不等于
//比较字符串$gt; 数值类型 不加;
包含查询
$in:[] 包含 $nin:[] 不包含
//title包含小米和苹果的
db.goods.find({title:{$in:["小米","苹果"]}})
//title包含小米和苹果的
db.goods.find({title:{$nin:["小米","苹果"]}})
条件连接查询
//语法 多查询条件
$and:[{},{},{}]
db.goods.find($and:[{price:{$lt;:NumberInt(4000)}},{price:{$gt;:NumberInt(2000)}}])
//查询 price大于2000 并且小于4000的数据
//or 或者条件查询
$or:[{},{},{}]
查询测试
db.goods.find(
{
$and:[
{price:{$lt:NumberInt(4000)}},
{price:{$gt:NumberInt(2000)}},
{title:"/^小米/"},
],
$or:[
{name:"小米12"},
{name:"小米13"},
],
{id:1,title:1,price:1}
}
)
.skip(10)
.limit(5)
.sort(
{price:-1,name:1}
)
//查询 goods表/集合中title为小米开头的 价格大于2000 小于4000的 name包含在小米12 和小米 13的数据 跳过10行 显示5行 并根据price降序排序 name升序排序
//查询结果显示id,title,price列
聚合查询
//count计数
db.goods.count({条件});
//统计title为小米的数量
db.goods.count({title:"小米"});
//统计所有数 使用estimatedDocumentCount 条件筛选无效
db.goods.estimatedDocumentCount({title:"小米"});
//对title进行去重
db.goods.distinct("title")
//求和
$sum
db.goods.aggregate(
[
{
$group:{
}
}
]
)
三,整合SpringBoot
配置文件
spring:
data:
mongodb:
uri: mongodb://192.168.29.100:27017/user
server:
port: 7443
pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
使用MongoTemplate来进行CRUD
集合创建
//创建集合
public void createCollection(){
boolean b = mongoTemplate.collectionExists(User.class);
if(b){
mongoTemplate.dropCollection(User.class);
}
mongoTemplate.createCollection(User.class);
}
增加
//插入单条数据
@RequestMapping(value = "/insert",method = RequestMethod.POST)
public void insert(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setAge(24);
user.setEmail("18649360869@163.com");
user.setName("翟炳鑫");
user.setCreateDate("2024-02-08 16-25.00");
User insert = mongoTemplate.insert(user);
System.out.println("insert = " + insert);
}
//插入单条数据
@RequestMapping(value = "/insertModel",method = RequestMethod.POST)
public void insert(User user){
User insert = mongoTemplate.insert(user);
System.out.println("insert = " + insert);
}
//插入多条数据
@RequestMapping(value = "/insertAll",method = RequestMethod.POST)
public void insertAll(){
List<User> userList = new ArrayList<>();
for (int i=0;i<10;i++) {
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setAge(20+i);
user.setEmail("18649360869@163.com");
user.setName("翟炳鑫"+i);
user.setCreateDate("2024-02-08 16-25.00");
userList.add(user);
}
mongoTemplate.insertAll(userList);
}
修改
@RequestMapping("update")
public User update(String id){
User user = mongoTemplate.findById(id, User.class);
user.setName("瑶瑶");
user.setAge(25);
user.setEmail("17735469627@163.com");
//查询id字段为user的id的 age进行排序
Query q = new Query(Criteria.where("_id").is(user.getId())).with(Sort.by(Sort.Order.desc("age")));
Update update = new Update();
update.set("name",user.getName());
update.set("age",user.getAge());
update.set("email",user.getEmail());
UpdateResult updateResult = mongoTemplate.updateMulti(q, update, User.class);//修改符合条件的所有数据
UpdateResult updateResult1 = mongoTemplate.updateFirst(q, update, User.class);//修改符合条件的第一条数据
UpdateResult upsert = mongoTemplate.upsert(q, update, User.class);//upsert 进行修改 如果使用query 没有查询出数据 则进行添加一条新数据 并将修改的字段添值
Document updateObject = update.getUpdateObject();
System.out.println("updateObject = " + updateObject);
User byId = mongoTemplate.findById(id, User.class);
return byId;
}
查询
//查询全部
@RequestMapping(value = "/findAll",method = RequestMethod.POST)
public List<User> findAll(){
List<User> all = mongoTemplate.findAll(User.class);
System.out.println("all = " + all);
return all;
}
//id查询
@RequestMapping(value = "/findById",method = RequestMethod.POST)
public User findById(String id){
User byId = mongoTemplate.findById(id, User.class);
System.out.println("byId = " + byId);
return byId;
}
//条件查询
@RequestMapping(value = "/findQuery",method = RequestMethod.POST)
public List<User> findQuery(){
Query query = new Query(
Criteria.where("age").gte(25).lte(30)//age在25-30之间的
.and("name").is("翟炳鑫")//name为翟炳鑫的
);
List<User> userList = mongoTemplate.find(query, User.class);
return userList;
}
//分页条件查询
@RequestMapping(value = "/findQueryPage",method = RequestMethod.POST)
public List<User> findQueryPage(int page,int pageSize){
String name = "小王";
//分页对象设置 设置排序
Pageable pageable = PageRequest.of(page-1,pageSize, Sort.by(Sort.Order.desc("age")));
Criteria criteria = new Criteria();
criteria.and("name").regex(name);//根据name 模糊匹配
criteria.and("age").lte(27).gte(20);//查询age 在24-27的数据
criteria.orOperator(Criteria.where("name").regex("7"),Criteria.where("age").is(26));//or查询 查询name包含7 或者age为26的数据
Query query = new Query(criteria).with(pageable);
query.with(Sort.by(Sort.Order.desc("age")));
long count = mongoTemplate.count(query, User.class);
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println("userList = " + userList);
System.out.println("count = " + count);
return userList;
}
//使用json字符串查询
@RequestMapping(value = "/findQueryJson",method = RequestMethod.POST)
public List<User> findQueryJson(){
String json = "{$or:[{age:{$gte:20}},{name:{翟炳鑫0}}]}";
Query query = new BasicQuery(json);
long count = mongoTemplate.count(query, User.class);
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println("userList = " + userList);
System.out.println("count = " + count);
return userList;
}
四,聚合操作
$project
//使用$project 来给集合中字段为name的列起别名显示 为name1
db.user.aggregate([{$project:{name1:"$name"}}])
//使用$project 来给集合中字段为name1的列起别名显示 为name 并筛选数据name为瑶瑶的数据
db.books.aggregate([{$project:{name:"$name1",name:"瑶瑶"}}])
$match
使用聚合管道时,尽量将$match来放在前面进行筛选
//使用$match来筛选age为25的数据 name包含翟炳鑫的数据
db.user.aggregate([{$match:{age:25,name:/翟炳鑫/}}])
//使用match来筛选数据 并用count来统计筛选后的数据条数
db.user.aggregate( [ { $match:{age:25,name:/翟炳鑫/}}, {$count:"name_count"} ] )
$group
//使用$group进行对id分组 设置为null 意思为不分组 count求数据总条数 pop 求age总和 avg 求age平均值
db.user.aggregate([{$group:{_id:null,count:{$sum:1},pop:{$sum:"$age"},avg:{$avg:"$age"}}}])
{ "_id" : null, "count" : 23, "pop" : 559, "avg" : 24.304347826086957 }
//使用$group对age进行分组 _id指定为age 求分组后的数据条数 age总数 与age平均值
db.user.aggregate([{$group:{_id:"$age",count:{$sum:1},pop:{$sum:"$age"},avg:{$avg:"$age"}}}])
{ "_id" : 23, "count" : 3, "pop" : 69, "avg" : 23 }
{ "_id" : 21, "count" : 2, "pop" : 42, "avg" : 21 }
{ "_id" : 29, "count" : 2, "pop" : 58, "avg" : 29 }
{ "_id" : 27, "count" : 2, "pop" : 54, "avg" : 27 }
{ "_id" : 28, "count" : 2, "pop" : 56, "avg" : 28 }
{ "_id" : 25, "count" : 4, "pop" : 100, "avg" : 25 }
{ "_id" : 24, "count" : 1, "pop" : 24, "avg" : 24 }
{ "_id" : 20, "count" : 3, "pop" : 60, "avg" : 20 }
{ "_id" : 26, "count" : 2, "pop" : 52, "avg" : 26 }
{ "_id" : 22, "count" : 2, "pop" : 44, "avg" : 22 }
//使用$group对age分组 在根据name进行分组 求age总和
db.user.aggregate([{$group:{_id:{age:"$age",name:"$name"},pop:{$sum:"$age"}}}])
$limit
//跳过2条数据
db.user.aggregate([{$limit:2}])
$skip
//跳过2条数据
db.user.aggregate([{$skip:2}])
$sort
//降序排序
db.user.aggregate([{$sort:{_id:-1}}])
举例
//以age进行分组 对分组后求数量 求和 求平均 并通过分组后age进行降序排序 跳过两条数据 显示5条数据
db.user.aggregate([{$group:{_id:"$age",count:{$sum:1},pop:{$sum:"$age"},avg:{$avg:"$age"}}},{$sort:{_id:-1}},{$skip:2},{$limit:5}])
$lookup
//语法
db.class.aggregate(
{$lookup:
{
from:"user",//需要关联的集合名称
localField:"name",//关联列 本集合
foreignField:"name",//需要关联集合 的列
as:"classUser"//临时集合
}
}
)
//class集合与user集合关联查询 class表name列与user集合name列关联 临时新集合classUser
db.class.aggregate({$lookup:{from:"user",localField:"name",foreignField:"name",as:"classUser"}})
{ "_id" : ObjectId("65c5765b413554a1e3570feb"), "name" : "宋含琪", "class" : "语文", "classUser" : [ { "_id" : "b04ead52-5763-4d16-9b3b-07e1abd9c033", "name" : "宋含琪", "age" : 23, "email" : "18649360869@163.com", "createDate" : "2024-02-08 16-25.00", "_class" : "com.example.model.User" } ] }
{ "_id" : ObjectId("65c5765b413554a1e3570fec"), "name" : "翟炳鑫", "class" : "地理", "classUser" : [ { "_id" : "fc6ceab2-8548-479f-96e8-d8ed8bf02d3d", "name" : "翟炳鑫", "age" : 24, "email" : "18649360869@163.com", "createDate" : "2024-02-08 16-25.00", "_class" : "com.example.model.User" } ] }
{ "_id" : ObjectId("65c5765b413554a1e3570fed"), "name" : "苏国亮", "class" : "英语", "classUser" : [ { "_id" : ObjectId("65cac33a52c57d02de844188"), "name" : "苏国亮", "age" : 25, "email" : "43857349.qq.com", "createDate" : "2024-02-13 00:00:00", "_class" : "com.example.model.User" } ] }
多集合关联
db.class.aggregate([{$lookup:{from:"user",localField:"name",foreignField:"name",as:"classUser"},$lookup:{from:"project",localField:"name",foreignField:"name",as:"userClass"}}])
{ "_id" : ObjectId("65c5765b413554a1e3570feb"), "name" : "宋含琪", "class" : "语文", "userClass" : [ { "_id" : ObjectId("65c576c8413554a1e3570fee"), "name" : "宋含琪", "address" : "广胜寺" } ] }
{ "_id" : ObjectId("65c5765b413554a1e3570fec"), "name" : "翟炳鑫", "class" : "地理", "userClass" : [ { "_id" : ObjectId("65c576c8413554a1e3570fef"), "name" : "翟炳鑫", "address" : "庄园" } ] }
{ "_id" : ObjectId("65c5765b413554a1e3570fed"), "name" : "苏国亮", "class" : "英语", "userClass" : [ { "_id" : ObjectId("65c576c8413554a1e3570ff0"), "name" : "苏国亮", "address" : "南营" } ] }
$bucket
//对固定列进行桶范围分组
db.user.aggregate([{
$bucket:{
groupBy:"$age",//分组列
boundaries:[0,22,25,28,30],//分组范围
default:"other",//其他
output:{"count":{$sum:1}}//输出求数量
output:{"avg":{$avg:"$age"}}//输出求平均
}
}])
{ "_id" : 0, "count" : 5 }
{ "_id" : 22, "count" : 6 }
{ "_id" : 25, "count" : 8 }
{ "_id" : 28, "count" : 4 }
{ "_id" : 0, "avg" : 20.4 }
{ "_id" : 22, "avg" : 22.833333333333332 }
{ "_id" : 25, "avg" : 25.75 }
{ "_id" : 28, "avg" : 28.5 }
聚合练习
数据
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" } ,
{ "_id" : "01002", "city" : "CUSHMAN", "loc" : [ -72.51564999999999, 42.377017 ], "pop" : 36963, "state" : "MA" },
{ "_id" : "01005", "city" : "BARRE", "loc" : [ -72.10835400000001, 42.409698 ], "pop" : 4546, "state" : "MA" } ,
{ "_id" : "01007", "city" : "BELCHERTOWN", "loc" : [ -72.41095300000001, 42.275103 ], "pop" : 10579, "state" : "MA" },
{ "_id" : "01008", "city" : "BLANDFORD", "loc" : [ -72.936114, 42.182949 ], "pop" : 1240, "state" : "MA" }
获取人口数大于10000的州
[
{
$group:
{
_id: "$state",//根据州state分组
people: {
$sum: "$pop"//使用sum函数进行求和 对人口字段 pop
}
}
},
{
$match:
{
people: {
$gte: 10000//使用match 进行过滤数据
}
}
}
]
获取每个州下每个城市的平均人口数
[
{
$group:
{
_id: {
state: "$state",//根据州和城市进行分组 获取总和
city: "$city"
},
pop: {
$sum: "$pop"
}
}
},
{
$group:
{
_id: "_id.state",//在根据州分组后进行求平均数
avg: {
$avg: "$pop"
}
}
}
]
获取每个州最大人口和最小人口城市
db.people.aggregate([
{$group:
{
_id:{state:"$state",city:"$city"},//现根据州和城市分组
pop:{$sum:"$pop"}//求每个城市人口数
}
},
{$sort:{pop:-1}},//进行人口数排序
{$group:
{
_id:"$_id.state",//根据城市分组
bigcity:{$first:"$_id.city"},//通过$first 获取$_id 排好序的第一个城市
bigPeople1:{$first:"$_id.pop"}//获取第一个人口数
smallcity:{$last:"$_id.city"},//获取最后一个城市
smallPeople1:{$last:"$_id.pop"}
}
},
{ $project:
{
_id:0,
state:"$_id",//获取人口与城市
bigCity:{name:"$bigcity",pop:"$bigPeople1"},
smallcity:{name:"$smallcity",pop:"$smallPeople1"}
}
}
])
mapReduce
db.user.mapReduce(
function(){emit(this.name.name,this.age)},
function(key,values){return Array.sum(values)},
{
query:{age:25},
out:"userAge"
}
)
五,SpringBoot整合聚合操作
//$group 分组字段 分组后给age字段求平均 起别名age
GroupOperation groupOperation = Aggregation.group("address").avg("age").as("age");
//$match 过滤age小于20的数据
Criteria criteria = Criteria.where("age").gte(20);
MatchOperation matchOperation = Aggregation.match(criteria);
//调用api求数据
TypedAggregation<User> typedAggregation = Aggregation.newAggregation(User.class,groupOperation,matchOperation);
AggregationResults<Map> aggregate = mongoTemplate.aggregate(typedAggregation, Map.class);
List<Map> mappedResults = aggregate.getMappedResults();
for (Map map:mappedResults) {
System.out.println("============");
for (Object key:map.keySet()) {
Object o = map.get(key);
System.out.println(key+"= " + o);
}
}
//$group 分组字段 分组address与name字段 求age和
GroupOperation groupOperation = Aggregation.group("address","name").sum("age").as("age");
SortOperation sortOperation = Aggregation.sort(Sort.by(Sort.Direction.DESC,"age"));
// 在通过address分组
GroupOperation groupOperation1 = Aggregation.group("_id.address")
.first("_id.address").as("firstCity")
.first("age").as("firstAge")
.last("_id.address").as("lastCity")
.last("age").as("lastAge");
//$match 过滤age小于20的数据
// Criteria criteria =Criteria.where("avg").gte(20);
// MatchOperation matchOperation = Aggregation.match(criteria);
ProjectionOperation projectionOperation = Aggregation.project("firstCity","lastCity","address")
.andExclude("_id")
.andExpression("{name:address,age:$firstAge}").as("address")
.andExpression("{name:address,age:$lastCity}").as("address");
//调用api求数据
TypedAggregation<User> typedAggregation = Aggregation.newAggregation(User.class,groupOperation,sortOperation,groupOperation1,projectionOperation);
AggregationResults<Map> aggregate = mongoTemplate.aggregate(typedAggregation, Map.class);
List<Map> mappedResults = aggregate.getMappedResults();
for (Map map:mappedResults) {
System.out.println("============");
for (Object key:map.keySet()) {
Object o = map.get(key);
System.out.println(key+"= " + o);
}
}
//根据address字段进行分组 并通过avg函数对age字段求平均值 并重命名为avgAge
GroupOperation groupOperation = Aggregation.group("address").avg("age").as("avgAge");
//创建筛选条件 对分组后 avgAge 小于18的数据 进行过滤
Criteria criteria = Criteria.where("avgAge").gte(18);
MatchOperation matchOperation = Aggregation.match(criteria);
//对avgAge 字段进行升序排序
SortOperation sortOperation = Aggregation.sort(Sort.by(Sort.Direction.ASC,"avgAge"));
//创建聚合
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("address").avg("age").as("avgAge"),//对address进行分组 求平均 起别名avgAge
Aggregation.match(criteria),//数据筛选
Aggregation.sort(Sort.by(Sort.Direction.ASC,"avgAge")),//进行排序
Aggregation.skip((2-1)*1*1L),//分页 跳过数量
Aggregation.limit(1),
Aggregation.project("_id")//显示指定数据_id字段
.andExclude("_id", "address");//指定排他字段 不显示字段
.andExpression("avgAge").as("age")//指定显示字段并起别名
);
//调用api进行查询 如果为查询结果为实体类 指定显示字段起别名为实体类字段
AggregationResults<User> aggregate = mongoTemplate.aggregate(aggregation, User.class, User.class);
List<User> mappedResults = aggregate.getMappedResults();
for (User u:mappedResults) {
System.out.println("u = " + u);
}