MongoDB数据库
1. 聚合(aggregate)
-
聚合是基于数据处理的聚合管道,每个文档通过由一个或多个阶段(stage)组成的管道,可以对每个阶段的文档集进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
db.集合名称.aggregate({管道:{字段:{表达式}}})
-
常用表达式
处理输⼊⽂档并输出,语法:字段:{表达式:'$列名'}
表达式 | 作用 |
---|---|
$sum | 计算总和, $sum:1 表示以⼀倍计数 |
$avg | 计算平均值 |
$min | 获取最⼩值 |
$max | 获取最⼤值 |
$push | 在结果⽂档中插⼊值到⼀个数组中 |
$first | 根据资源⽂档的排序获取第⼀个⽂档数据 |
$last | 根据资源⽂档的排序获取最后⼀个⽂档数据 |
- 常用管道:
⽂档处理完毕后, 通过管道进⾏下⼀次处理
管道 | 作用 |
---|---|
$group | 将集合中的⽂档分组, 可⽤于统计结果 |
$match | 过滤数据, 只输出符合条件的⽂档 |
$project | 修改输⼊⽂档的结构, 如重命名、 增加、 删除字段、 创建计算结果 |
$sort | 将输⼊⽂档排序后输出 |
$limit | 限制聚合管道返回的⽂档数 |
$skip | 跳过指定数量的⽂档, 并返回余下的⽂档 |
$unwind | 将数组类型的字段进⾏拆分 |
1.1 $group
- 将集合中的文档分组,可用于统计结果,_id表示分组的依据,使用某个字段的格式为
'$字段'
例如:统计男生女生的总人数
> db.stu.aggregate({$group:{_id:'$gender', counter:{$sum:1}}})
{ "_id" : 1, "counter" : 1 }
{ "_id" : 2, "counter" : 4 }
- Group by null:将集合中所有文档分为一组
例如:求学生的总人数、平均年龄
> db.stu.aggregate({$group:{_id:null, counter:{$sum:1}, avgAge:{$avg:'$age'}}})
{ "_id" : null, "counter" : 5, "avgAge" : 23.2 }
- 透视数据
插⼊值到结果集的数组中
统计不同性别的学生姓名
> db.stu.aggregate({$group:{_id:'$gender', name:{$push:'$name'}}})
{ "_id" : 1, "name" : [ "yh" ] }
{ "_id" : 2, "name" : [ "hx", "my", "xy", "xz" ] }
使用 $$ROOT
可以将文档内容加入到结果集的数组中
> db.stu.aggregate({$group:{_id:'$gender', name:{$push:'$$ROOT'}}})
{ "_id" : 1, "name" : [ { "_id" : ObjectId("5dac0e04f29e62ebc28cc5f7"), "name" : "yh", "age" : 23, "gender" : 1, "hometown" : "qd" } ] }
{ "_id" : 2, "name" : [ { "_id" : ObjectId("5dabdf0cf29e62ebc28cc5f2"), "name" : "hx", "age" : 22, "gender" : 2, "hometown" : "qd" }, { "_id" : ObjectId("5dabdf0ff29e62ebc28cc5f3"), "name" : "my", "age" : 24, "gender" : 2, "hometown" : "wf" }, { "_id" : ObjectId("5dabdf80f29e62ebc28cc5f4"), "name" : "xy", "age" : 24, "gender" : 2, "hometown" : "qd" }, { "_id" : ObjectId("5dac0dddf29e62ebc28cc5f6"), "name" : "xz", "age" : 23, "gender" : 2, "hometown" : "rz" } ] }
1.2 $match
- 用于过滤数据,只输出符合条件的文档
用法同标准查询操作
例如:
1.查询年龄大于23的学生
> db.stu.aggregate({$match:{age:{$gt:23}}})
{ "_id" : ObjectId("5dabdf0ff29e62ebc28cc5f3"), "name" : "my", "age" : 24, "gender" : 2, "hometown" : "wf" }
{ "_id" : ObjectId("5dabdf80f29e62ebc28cc5f4"), "name" : "xy", "age" : 24, "gender" : 2, "hometown" : "qd" }
2.查询年龄大于22的男生、女生人数
> db.stu.aggregate({$match:{age:{$gt:22}}}, {$group:{_id:'$gender', counter:{$sum:1}}})
{ "_id" : 1, "counter" : 1 }
{ "_id" : 2, "counter" : 3 }
1.3 $project
- 修改输入文档的结构,如重命名、增加、删除字段,创建计算结果
例如:
1.查询学生的姓名、年龄
> db.stu.aggregate({$project:{_id:0, name:1, age:1}})
{ "name" : "hx", "age" : 22 }
{ "name" : "my", "age" : 24 }
{ "name" : "xy", "age" : 24 }
{ "name" : "xz", "age" : 23 }
{ "name" : "yh", "age" : 23 }
2.查询男生、女生人数,只输出人数
> db.stu.aggregate({$group:{_id:'$gender', counter:{$sum:1}}}, {$project:{_id:0, counter:1}})
{ "counter" : 1 }
{ "counter" : 4 }
1.4 $sort
- 将输入文档排序后输出
例如:
1.查询学生信息,按年龄升序
> db.stu.aggregate({$sort:{age:1}})
{ "_id" : ObjectId("5dabdf0cf29e62ebc28cc5f2"), "name" : "hx", "age" : 22, "gender" : 2, "hometown" : "qd" }
{ "_id" : ObjectId("5dac0dddf29e62ebc28cc5f6"), "name" : "xz", "age" : 23, "gender" : 2, "hometown" : "rz" }
{ "_id" : ObjectId("5dac0e04f29e62ebc28cc5f7"), "name" : "yh", "age" : 23, "gender" : 1, "hometown" : "qd" }
{ "_id" : ObjectId("5dabdf0ff29e62ebc28cc5f3"), "name" : "my", "age" : 24, "gender" : 2, "hometown" : "wf" }
{ "_id" : ObjectId("5dabdf80f29e62ebc28cc5f4"), "name" : "xy", "age" : 24, "gender" : 2, "hometown" : "qd" }
2.查询男生、女生人数,按人数降序
> db.stu.aggregate({$group:{_id:'$gender', counter:{$sum:1}}}, {$sort:{counter:-1}})
{ "_id" : 2, "counter" : 4 }
{ "_id" : 1, "counter" : 1 }
1.5 $limit 和 $skip
- $limit:限制聚合管道返回的文档数
- $skip:跳过指定数量的文档,并返回余下文档
例如:
1.查询2条学生信息
> db.stu.aggregate({$limit:2})
{ "_id" : ObjectId("5dabdf0cf29e62ebc28cc5f2"), "name" : "hx", "age" : 22, "gender" : 2, "hometown" : "qd" }
{ "_id" : ObjectId("5dabdf0ff29e62ebc28cc5f3"), "name" : "my", "age" : 24, "gender" : 2, "hometown" : "wf" }
2.查询从第三条开始的学生信息
> db.stu.aggregate({$skip:2})
{ "_id" : ObjectId("5dabdf80f29e62ebc28cc5f4"), "name" : "xy", "age" : 24, "gender" : 2, "hometown" : "qd" }
{ "_id" : ObjectId("5dac0dddf29e62ebc28cc5f6"), "name" : "xz", "age" : 23, "gender" : 2, "hometown" : "rz" }
{ "_id" : ObjectId("5dac0e04f29e62ebc28cc5f7"), "name" : "yh", "age" : 23, "gender" : 1, "hometown" : "qd" }
1.6 $unwind
- 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
例如:
> db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L']})
WriteResult({ "nInserted" : 1 })
> db.t2.aggregate({$unwind:'$size'})
{ "_id" : 1, "item" : "t-shirt", "size" : "S" }
{ "_id" : 1, "item" : "t-shirt", "size" : "M" }
{ "_id" : 1, "item" : "t-shirt", "size" : "L" }
若现在有这样一个集合:
> db.t3.find()
{ "_id" : 1, "item" : "a", "size" : [ "S", "M", "L" ] }
{ "_id" : 2, "item" : "b", "size" : [ ] }
{ "_id" : 3, "item" : "c", "size" : "M" }
{ "_id" : 4, "item" : "d" }
{ "_id" : 5, "item" : "e", "size" : null }
id = 2, id = 3, id = 5的文档size值为空或者null,此时使用 $unwind 会导致属性值为空的文档被丢失:
> db.t3.aggregate({$unwind:'$size'})
{ "_id" : 1, "item" : "a", "size" : "S" }
{ "_id" : 1, "item" : "a", "size" : "M" }
{ "_id" : 1, "item" : "a", "size" : "L" }
{ "_id" : 3, "item" : "c", "size" : "M" }
此时,若想保留id = 2, id = 3, id = 5的文档,需在 $unwind 中添加 preserveNullAndEmptyArrays 属性:
> db.t3.aggregate({$unwind:{path:'$size', preserveNullAndEmptyArrays:true}})
{ "_id" : 1, "item" : "a", "size" : "S" }
{ "_id" : 1, "item" : "a", "size" : "M" }
{ "_id" : 1, "item" : "a", "size" : "L" }
{ "_id" : 2, "item" : "b" }
{ "_id" : 3, "item" : "c", "size" : "M" }
{ "_id" : 4, "item" : "d" }
{ "_id" : 5, "item" : "e", "size" : null }
2. 索引
2.1 创建索引
- 语法:
db.集合名.ensureIndex({属性:1})
,1表示升序, -1表示降序 - 创建唯一索引
db.集合名.ensureIndex({"name":1},{"unique":true})
- 创建唯一索引并消除重复
db.集合名.ensureIndex({"name":1},{"unique":true,"dropDups":true})
- 创建联合索引
db.t1.ensureIndex({name:1,age:1})
—— 通过多个字段判断文档的唯一性
首先在数据库中创建一个集合,包含100000条数据
> for(i=0;i<100000;i++){db.t12.insert({name:'test'+i,age:i})}
通过一下命令查看执行时间,结果如下:
> db.t12.find({name:'test10000'}).explain('executionStats')
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test1.t12",
"indexFilterSet" : false,
"parsedQuery" : {
"name" : {
"$eq" : "test10000"
}
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"name" : {
"$eq" : "test10000"
}
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 74,
"totalKeysExamined" : 0,
"totalDocsExamined" : 100000,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"name" : {
"$eq" : "test10000"
}
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 39,
"works" : 100002,
"advanced" : 1,
"needTime" : 100000,
"needYield" : 0,
"saveState" : 782,
"restoreState" : 782,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 100000
}
},
"serverInfo" : {
"host" : "VM_0_10_centos",
"port" : 27017,
"version" : "4.0.13",
"gitVersion" : "bda366f0b0e432ca143bc41da54d8732bd8d03c0"
},
"ok" : 1
}
可以看到"executionTimeMillis" : 74,
执行时间为74ms。
现根据name创建索引:
> db.t12.ensureIndex({name:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
在此之前,_id为默认索引,因此"numIndexesBefore" : 1, "numIndexesAfter" : 2,
。
再次执行查询命令:
> db.t12.find({name:'test10000'}).explain('executionStats')
{
"queryPlanner" : {
"plannerVersion" : 1,
……
……
……
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 1,
……
……
……
"version" : "4.0.13",
"gitVersion" : "bda366f0b0e432ca143bc41da54d8732bd8d03c0"
},
"ok" : 1
}
可以看到"executionTimeMillis" : 0,
执行时间为0ms。
2.2 查看索引
- 通过
db.集合名.getIndexes()
查看已经建立的索引
> db.t12.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test1.t12"
},
{
"v" : 2,
"key" : {
"name" : 1
},
"name" : "name_1",
"ns" : "test1.t12"
}
]
2.3 删除索引
- 通过
db.集合名.dropIndex('索引名')
删除已经建立的索引
3. 数据的备份和恢复
- 备份语法:
mongodump -h dbhost -d dbname -o dbdirectory
-h: 服务器地址, 也可以指定端⼝号
-d: 需要备份的数据库名称
-o: 备份的数据存放位置, 此⽬录中存放着备份出来的数据 - 恢复语法:
mongorestore -h dbhost -d dbname --dir dbdirectory
-h: 服务器地址
-d: 需要恢复的数据库实例
–dir: 备份数据所在位置