「MongoDB」基础操作
本文主要会介绍一些关于MongoDB数据库的基本操作:
- 数据库相关
- 数据的导入、导出
- 集合操作
- 文档操作
- 关于游标
- 管道聚合操作
[数据库操作]
装好数据库之后,如果你在Linux环境下,就可以用下面两条命令查看mongodb的服务是否启动以及连接到mongo。但是注意如果你是win10的话,并且安装的mongodb版本是最新版本的,需要下载mongoshell才可以进到交互式的环境,至于打开服务的话就到任务管理器-》服务->找到Mongod就行(也许叫MongoDB,一下忘了)
pgrep mongo -l # 查看mongodb是否已经启动 mongo # 连接mongo
本文版本是MongoDB shell version v4.0.0
数据库的基本操作:(创建、删除、查看、插入数据,应有尽有)
use Testdb # 创建数据库 db.Testdb.insert({_id:1,name:"王小明"}) # 插入数据 # 循环向集合items插入数据 for(var i = 0; i < 10000; i++) db.items.insert({_id:i, text:"Hello" + i}) db # 查看当前数据库 show dbs # 查看所有数据库 # 删除数据库 -- MongoDB 删除数据库需要先切换到该数据库中,然后再执行删除命令. use Testdb db.dropDatabase()
use Testdb
之后show dbs
是显示不出Testdb
数据库的,因为使用use命令创建的Testdb
存储在内存中,且数据库中没有数据,执行insert
语句之后再show dbs
就能看到Testdb
了。
MongoDB中默认包含数据库admin
、config
、local
、test
,但是test
数据库存储在内存中,也无任何数据,所以执行show dbs
也是看不到的。
因为 mongodb 底层是 javascript 引擎,所以我们可以使用 js 的语法来插入数据。
[数据导入、导出]
mongoimport
将数据(csv/ json等)导入mongodb数据,用到的命令是mongoimport
, 具体语法如下:
# 导入- mongoimport mongoimport -d Testdb1 -c score --type csv --headerline --ignoreBlanks --file test.csv
-d Testdb1
:指定将数据导入到 Testdb1 数据库;
-c score
:将数据导入到集合 score ,如果这个集合之前不存在,会自动创建一个(如果省略 --collection 这个参数,那么会自动新建一个以 CSV 文件名为名的集合);
--type csv
:文件类型,这里是 CSV;
--headerline
:这个参数很重要,加上这个参数后创建完成后的内容会以 CSV 文件第一行的内容为字段名(导入json文件不需要这个参数);
--ignoreBlanks
:这个参数可以忽略掉 CSV 文件中的空缺值(导入json文件不需要这个参数);
--file test.csv
:这里就是 CSV 文件的路径了,需要使用绝对路径。
mongoexport
数据导出-mongoexport
具体语法如下:
# 导出为csv -f :当输出格式为 csv 时,必须指定输出的字段名。 mongoexport -d Testdb1 -c score -o /file.json --type csv -f "_id,name,age,sex,major" # 导出为json mongoexport -d Testdb1 -c score -o /file.json --type json
-o /file.json
:输出的文件路径/(根目录下)和文件名;
--type json
:输出的格式,默认为 json。
[集合操作]
基本操作:CRUD
# 在 Testdb 数据库中创建创建固定集合 test ,整个集合空间大小512000KB,文档最大个数为1000个。 use Testdb # 查看当前数据库所有集合 show collections # 显式创建集合 db.createCollection("test", { capped : true, autoIndexId : true, size : 512000, max : 1000 } ) # capped :是一个布尔类型,true 时创建固定集合,必须指定 size。 # 固定集合指有固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。默认为 false; # autoIndexId :也是一个布尔类型,如为 true,自动在_id 字段创建索引。默认为 false ; # size :为固定集合指定一个最大值(以字节 KB 计); # max :指定固定集合中包含文档的最大数量。 # 隐式创建集合 -- db.集合名.insert() db.mytest2.insert([{"name" : "王小明","sex":"男"}, {"name" : "李小红","sex":"女"}]) db.集合名.find() # 查询集合 db.集合名.drop() # 删除集合
和 MySQL 不同的是,在 MongoDB 中,你不一定需要先创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
一个🌰:
[文档操作]
前面是对集合这个对象进行操作,接下来,就需要对集合中的数据进行操作了~
增删改
所有存储在集合中的数据都是 BSON 格式。BSON 是一种类 JSON 的一种二进制形式的存储格式,简称: Binary JSON 。
document=({_id:1, name: '王小明', sex: '男',hobbies: ['乒乓球','羽毛球'], birthday: '1996-02-14'}); # 插入 db.集合名.insert(document) # document可以是一个变量 db.collection.insertMany([{},{},...]) # 更新 db.person2.update({birthday:"1996-02-14"},{$set:{birthday:"1996"}}) # update中两个参数,都是对象 # 查看 db.person2.find().pretty() # pretty() 方法的作用是使文档整齐的输出 # 替换 save() 适用修改整条数据 db.person3.save({ "_id" :1, "name" : "李小红", "sex" : "女", "hobbies" : [ "画画", "唱歌", "跳舞" ],"birthday" : "1996-06-14"}) # 删除 db.collection.remove({"name":"www"}) # 删除所有数据,但是集合不会删除 db.remove({}) # 删除指定集合里的所有数据 db.stu2.remove({});
tips:
save()
指定了_id,则对文档进行更新;未指定_id则会执行插入功能,MongoDB 默认自动生成一个不重复的_id。
update()
criteria-查询条件,
objNew-更新后的,
upsert-默认false,在不存在更新文档的情况下,是否插入objNew,
multi-默认false,只更新找到的第一个)
文档查询
在MySQL中,我们时常会对数据进行一定的筛选,就像大部分的人都喜欢漂亮小姐姐和帅气小哥哥一样, 在MongoDB中,一定也不能少了这么重要的操作!
一些基本的运算操作感觉和LaTeX的语法有几分相似~
比较运算
操作 | 格式 | 案例 | 关系数据库中类似的语句 |
等于 | {:} |
db.stu1.find({"name":"李小红"}).pretty() | where name = '李小红' |
小于 | {:{$lt:}} | db.stu1.find({"age":{$lt:18}}).pretty() | where age < 18 |
小于或等于 | {:{$lte:}} | db.stu1.find({"age":{$lte:18}}).pretty() | where age <= 18 |
大于 | {:{$gt:}} | db.stu1.find({"age":{$gt:18}}).pretty() | where age > 18 |
大于或等于 | {:{$gte:}} | db.stu1.find({"age":{$gte:18}}).pretty() | where age >= 18 |
不等于 | {:{$ne:}} | db.stu1.find({"age":{$ne:18}}).pretty() | where age != 18 |
条件查询
关于条件,这里只介绍了and
, or
, in
, nin
,具体语法如下:
# and db.col.find({$and:[{k1:v1},{k2:v2}]}) db.stu1.find({"age":20, "sex":"男"}).pretty() # 查询年龄为20且性别为男的文档 # or db.col.find({$or:[{k1:v1},{k2:v2}]}) # in db.col.find({k:{$in:[k1,k2]}}) # nin db.col.find({k:{$nin:[k1,k2]}})
高级查询
符号 | 含义 |
举例 |
$all | 匹配所有:$all 会查询满足方括号中所有条件的文档 |
查询其中所有喜欢“唱歌”和“羽毛球”的人db.hobbies.find({hobbies:{$all:["唱歌","羽毛球"]}) |
$exists | {$exists:true} 存在{$exists:false} 不存在 |
查询 hobbies 集合中存在 age 字段的文档db.test.find({age:{$exists:true}}) |
$in/ $nin | $in 包含 $nin 不包含 |
查询 age =17 或 age =20的文档db.test.find({age:{$in:[17,20]}}) 查询 age !=17 且 age !=20 的文档db.test.find({age:{$nin:[17,20]}}) |
$size | 数组元素个数 | 查询有两个爱好的文档db.hobbies.find({hobbies:{$size:2}}) |
$mod | 取模运算 | 查询 age 取模7等于4的文档db.hobbies.find({age:{$mod:[7,4]}}) |
sort() | 1: 升序 -1:降序 | 升序db . collection . find (). sort ({ _id : 1 }) |
$or | 或查询 | 查询性别 sex 为 男 或年龄 age 为18的文档信息db.student.find({$or:[{sex:"男"},{age:18}]}) |
$and | 和 | 查询18 < age < 21之间的文档db.student.find({$and:[{age:{$gt:18}},{age:{$lt:21}}]}) |
$not | 取反 | 查询年龄小于20岁的文档db.student.find({age:{$not:{$gte:20}}}) |
count() | 返回结果集总数 | db.student.find({major:{$not:/^计.*/}}).count() |
特定类型查询
# 查询名字不是以韩开头的 db.test.find({name:{$not:/^计.*/}}}) # 查询字段为null的 db.test.find({k:null})
[游标]
游标不是查询结果,而是查询的返回资源,或者接口。通过这个接口,你可以逐条读取。就像fopen
打开文件,得到一个资源一样,通过资源,可以一行一行的读文件。
# 声明游标 var cursor = db.items.find({_id:{$lte:5}}) # find _id <= 5 的查询结果赋值给cursor # 打印游标当中的数据信息-4种方式 # 1. printjson(cursor.next()) # 打印下一条数据 当所有数据都取完后再执行该语句就会报错 # 2. for(var cursor=db.test.find({_id:{$lte:5}}); cursor.hasNext(); ) { printjson(cursor.next()); } # 3. while(cursor.hasNext()) { printjson(cursor.next()); }; # 4. cursor.forEach(function(obj) {printjson(obj)}); # 分页打印 # skip() 跳过多少条数据 limit 显示多少条数据 var cursor = db.test.find().skip(7000).limit(10); # 显示id为7000-7009 因为_id从0开始 cursor.forEach(function(obj) {printjson(obj);}); # 取第一条文档 printjson(cursor.toArray()[0])
[管道聚合]
MongoDB聚合操作包括聚合管道操作和Map-Reduce操作等。
其中聚合管道操作是将文档在一个管道处理完之后,把处理的结果传递给下一个管道进行再次处理;Map-Reduce操作是将集合中的批量文档进行分解处理,然后将处理后的各个结果进行合并输出。
管道操作符
操作符 | 作用 |
$project | 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档 |
$match | 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作 |
$limit | 用来限制MongoDB聚合管道返回的文档数 |
$skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档 |
$unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值 |
$group | 将集合中的文档分组,可用于统计结果 |
$sort | 将输入文档排序后输出 |
管道表达式
$sum | 计算总和 |
$avg | 计算平均值 |
$min | 获取集合中所有文档对应值的最小值 |
$max | 获取集合中所有文档对应值的最大值 |
$push | 在结果文档中插入值到一个数组中 |
$addToSet | 在结果文档中插入值到一个数组中,但不创建副本 |
$first | 根据资源文档的排序获取第一个文档数据 |
$last | 根据资源文档的排序获取最后一个文档数据 |
注意:以上操作不会修改集合的内容,只是将集合以指定形式输出。
举例
$project 修改文档结构输出
场景:有时候只需要输出文档中的几列,使用$project
进行操作,还可以使用该聚合符对列名重命名
# 只输出作者 author 和学习人数 learning_num 信息 _id默认显示,这里设置为0不显示 非0为显示 db.educoder.aggregate({$project:{_id:0,author:1,learning_num:1}}) # 重命名为num db.educoder.aggregate({$project:{course:1,authoe:1,tags:1,num:'$learning_num'}})
注意这里除了_id:0
可以设置为0外,其他的只需要设置需要的列为1即可,不出现的不用设置为0,不然会报错。
$match 筛选文档输出
# 只输出作者为“李暾”的文档 db.educoder.aggregate({$match:{author:'李暾'}})
$limit
db.product.aggregate({$limit:3}).pretty() # 显示前3个文档
$skip
# 跳过前4个文档,展示集合 _id之后为4的文档 db.product.aggregate({$skip:4}).pretty() #跳过第一条,显示前两条,也就是显示第2-3条文档 db.educoder.aggregate([{$skip:1},{$limit:2}]) #显示前两条,跳过第一条,也就是显示第2条文档 db.educoder.aggregate([{$limit:2},{$skip:1}])
$unwind
将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
# 把tags数组里面的值拆开来 db.educoder.aggregate({$unwind:'$tags'}) # 统计每个tags中出现的次数 db.educoder.aggregate([{$unwind:'$tags'},{$group:{_id:'$tags',course_num:{$sum:1}}}])
$group
# 按作者分组,可以统计出当前集合有多少个作者 db.test.aggregate({$group:{_id:'$author'}}) # 按照type类分组,并分别求不同type组的price总额 db.product.aggregate([{$group:{"_id":"$type","price":{$sum:"$price"}}}])
$sort
# 1 升序 -1 降序 db.product.aggregate([{$sort:{"price":-1}}]).pretty() # 按照price降序输出
$sum
# 获取每个作者拥有的实训数量 db.educoder.aggregate([{$group:{_id:'$author',num_course:{$sum:1}}}]) # 每个作者的实训学习总人数learning_sum db.educoder.aggregate([{$group:{_id:'$author',learning_sum:{$sum:'$learning_num'}}}])
$sum:1
表示前面的情况出现一次就加1
,$sum:2
前面条件每满足一次就加2
$avg
# 每个type的平均价格 db.product.aggregate([{$group:{"_id":"$type","price":{$avg:"$price"}}}])
$min
# 不同type之中的最小值 db.product.aggregate([{$group:{"_id":"$type","price":{$min:"$price"}}}])
$push
# 按type分类并得到每个type中的所有name db.product.aggregate([{$group:{"_id":"$type","name":{$push:"$name"}}}])
$first \ $last
# 每个type的第一个 db.product.aggregate([{$group:{"_id":"$type","name":{$first:"$name"}}}]).pretty() # 每个type的最后一个 db.product.aggregate([{$group:{"_id":"$type","name":{$last:"$name"}}}]).pretty()
map-reduce操作
Map-Reduce操作先将集合中满足query
的文档查询出来,然后将这些满足条件的文档输入到Map阶段中,并按照key进行分组,将key相同的文档的value存放到一个数组中,输出到Reduce阶段进行聚合处理。
Map-Reduce具体操作语法如下:
db.collection.mapReduce( function() {emit(key,value);}, //map 函数 function(key,values) {return reduceFunction}, //reduce 函数 { out: collection, // 输出集合的名字 query: document, // 筛选条件,符合条件的才会调用map函数 sort: document, // 发往map函数前给文档排序,可以优化分组机制 limit: number // 发往map函数的文档数量的上限 } )
map函数调用emit(k,v)
方法,遍历集合中的所有文档,返回k-v
键值对,并将key
和value
输出到reduce函数中;reduce主要是将key-values
变为key-value
。
query
、sort
、limit
都是在送入到map之前进行的操作。
案例:在集合orders中查找status:"A"
的数据,,并根据cust_id
分组,计算amount
的和。
db.orders.mapReduce( function() {emit(this.cust_id, this.amount);}, # map阶段 function(key, value) {return Array.sum(value)}, # reduce阶段 { query:{status:"A"}, # query:条件 out:{"order_totals"}, # output:输出集合名字 } )
关于mongodb的有关基础操作就先总结到这儿啦~