增删查改超详细介绍
前言
从库至今数据库的作用其实主要的就是存放数据,而对数据的操作也就是增删查改,MongoDB也一样,虽然其有自己的一套操作语法,但是根本思想还是与SQL一样,只是可能没有SQL那么健壮,但是大部分的业务需求还是能够满足的,如果真不能满足我相信你也就不会选择MongoDB作为你的存储媒介了。
好,闲言碎语不要讲,我们开始学习如何对MongoDB里的数据进行操作
1.插入
1.1 表有如下两种方式创建:
db.tb1.insert({_id:1,name:"Kobe"}) //隐式创建,insert时自动创建新表
db.createCollection("tb1") //显式创建,创建一个空表
隐式创建没什么好说的,主要说两句
createCollection
,当你执行此命令后,会在数据文件目录下创建一个.wt的数据文件,伴随着也会有一个索引文件存在(_id主键索引树文件)默认4096bytes;
> db.createCollection("tb2")
$ ls -l
-rw------- 1 mongo mongo 4096 Jul 27 10:14 0-1144972512418703480.wt
也可以通过如下命令格式创建capped collection(固定集合)
db.createCollection("cap", { capped : true, size : 5242880, max : 5000 } )
1.2 插入数据提供了多种方法:
- insertOne() 插入单一文档
- insertMany() 插入多个文档,返回的是插入文档的ObjectID
-
insert() 插入单一或多个文档
其实虽然提供了很多种方法,但是主要用insert()就够了> db.tb1.insert({ "_id" : 1, "x" : 1, "j" : 1 }) //普通插入 WriteResult({ "nInserted" : 1 }) //返回结果,nInserted表示插入的文档数量 > db.tb1.insert([ //以数组的形式插入多个document ... { "_id" : 2, "x" : 4, "j" : 2 }, ... { "_id" : 3, "x" : 2, "j" : 3 } ... ]) BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, //结果返回的时插入成功的行数 "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] })
> db.tb1.insertMany([ ... { "_id" : 4, "x" : 4, "j" : 4 }, ... { "_id" : 5, "x" : 3 } ... ]) { "acknowledged" : true, "insertedIds" : [ 4, 5 ] } //结果返回的时插入行的id
注:
上述通过的方法虽然都能将数据插入,但是返回结果有所不同,所以在做开发时请注意查看对应的API方法,使用正确的调用方法。1.3 基于表插入
就是从其他表中查询的数据插入表中,类似SQL:insert into tb2 select * from tb1
>db.tb1.find({_id:1}).forEach( function(myDoc) { var a=myDoc; db.tb2.insert(a); } ) >db.tb2.find() { "_id" : 1, "x" : 1, "j" : 1 }
1.4 批量插入测试数据
> for(var i=1;i<10;i++) db.tb2.insert({userid:i,name:"johnny"});
2 查询
语法结构:db.collection.find( {query filter}, {projection} ) //query filter: 个大括号{ }, 指定查询条件 //projection: 第二个大括号{ },指定哪些字段需显示,从而限制结果集的输出大小
> db.tb1.find() //直接查询,类似select * from tb,类似于select * from tb1 order by j desc; > db.tb1.find().sort({j:-1}) //根据字段j排序,"1"表示升序,“-1”表示降序,类似于select * from tb1 order by j desc; > db.tb1.find({j:{$gt:2,$lte:4}}) //查询j<4 and j>2的记录,类似select * from tb1 where j<4 and j>2; $gt:大于 $lt:小于 $gte:大于等于 $lte:小于等于 $ne:不等于 > db.tb1.find({j:{$in:[1,4]}}) //查询包含j=1或j=4的记录,类似select * from tb1 where j in (1,4); 不包括的话可以用$nin { "_id" : 1, "x" : 1, "j" : 1 } { "_id" : 4, "x" : 4, "j" : 4 } > db.tb1.find({x:2,j:{$gt:1}}) //查询x=1 and j>1条件记录,类似select * from tb1 where x=1 and j>1; >db.tb1.find({$or:[{x:4},{j:{$lte:2}}]}) //查询x=4 or j<=2的条件记录 >db.tb1.find({x:1,$or:[{j:{$lt:2}},{j:3}]}) //查询x=1 and (j<2 or j=3) > db.tb1.find({x:1},{j:1}) //类似select j, _id from tb1 where x=1; 默认显示主键 { "_id" : 1, "j" : 1 } > db.tb1.find({x:1},{j:1,_id:0}) //只返回某一字段 { "j" : 1 }
注:
在生产上查询数据时,好只返回需要的字段,不要使用类似select * 的方式
不存在行的查询> db.tb1.insert({_id:6,x:6,j:null}); //插入j:null的行记录 > db.tb1.find({j:{$exists:false}}) //查询不存在j字段的行,相反就是$exists:true { "_id" : 5, "x" : 3 } > db.tb1.find({j:null}) //不存在列也被查询到,是因为这样的查询方式会返回name=null且不存在name字段的记录 { "_id" : 5, "x" : 3 } { "_id" : 6, "x" : 6, "j" : null } > db.tb1.find({j:{$type:10}}) //可以使用$type,10其实就代表了null这个数据类型 { "_id" : 6, "x" : 6, "j" : null }
Type | Number | Alias |
---|---|---|
Double | 1 | “double” |
String | 2 | “string” |
Object | 3 | “object” |
Array | 4 | “array” |
Binary data | 5 | “binData” |
ObjectId | 7 | “objectId” |
Boolean | 8 | “bool” |
Date | 9 | “date” |
Null | 10 | “null” |
Timestamp | 17 | “timestamp” |
注:
更多type类型请参考
type类型
数组查询
> db.tb2.insertMany([ //插入测试数据
... { "_id" : 1, "userId" : 2, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ],info:{city:"TJ",age:22}},
... { "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ],info:{city:"BJ",age:33}}
... ])
> db.tb2.find({Course:"English"}) //查询数组Course中包含元素“English”的行记录
{ "_id" : 1, "userId" : 2, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 } }
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({Course:["Math","English"]}) //只会查询到一行,而另外一笔数据虽然包含的元素一样,但是元素顺序不一致
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({Course:{$all:["English","Math"]}}) //只需包含指定元素,不关心元素的顺序位置
{ "_id" : 1, "userId" : 2, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 } }
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({"Course.1":"English"}) //查询数组中第2个元素是“English”的行记录,以为数组的下标从0开始
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({finished:{$elemMatch:{$gt:15,$lt:20}}}) //需要数组中至少有一个元素满足所有条件
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({finished:{$gt:25,$lt:20}}) //数组中各个元素只满足一部分筛选条件,但是组合起来可以满足所有条件,则显示
{ "_id" : 1, "userId" : 2, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 } }
> db.tb2.find({Course:{$size:2}}) //根据数组中元素个数查询
{ "_id" : 1, "userId" : 2, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 } }
{ "_id" : 2, "userId" : 3, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } }
> db.tb2.find({_id:2},{_id:0,finished:0,info:0,Course:{$slice:-1}}) //根据查询条件,返回数组Course中后一个元素
{ "userId" : 3, "Course" : [ "English" ] }
获取数组字段的数组长度
> db.tb2.find({_id:1}).forEach(function(x){
... var course=x.Course;
... print(course.length);
... })
2 //返回结果是2
嵌套子文档查询
> db.tb2.find({info:{city:"TJ",age:22}}) //根据子文档中的字段进行查询
或
>db.tb2.find({"info.city":"TJ","info.age":22})
> db.tb2.find({_id:1},{"info.city":1}) //返回子文档中的某个字段
{ "_id" : 1, "info" : { "city" : "TJ" } }
正则表达式
>db.tb3.insertMany([
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" },
{ "_id" : 2, "name" : "Tom", "addr" : "beijing" },
{ "_id" : 3, "name" : "Nick J" }
])
> db.tb3.find({name:/^J/}) //查询以J开头的记录
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" }
> db.tb3.find({name:{$not:/^J/}}) //查询不以J开头的记录
{ "_id" : 2, "name" : "Tom", "addr" : "beijing" }
{ "_id" : 3, "name" : "Nick J" }
> db.tb3.find({name:/J/}) //查询name重包含J的记录
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" }
{ "_id" : 3, "name" : "Nick J" }
迭代查询
find()查询之后返回的是一个cursor游标,在mongo shell默认情况下迭代20次以显示前20个文档记录,如果使用变量的话:
> var a=db.tb3.find()
> while(a.hasNext()){
printjson(a.next());
}
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" }
{ "_id" : 2, "name" : "Tom", "addr" : "beijing" }
{ "_id" : 3, "name" : "Nick J" }
或者使用forEach( )
> var a=db.tb3.find()
> a.forEach(printjson)
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" }
{ "_id" : 2, "name" : "Tom", "addr" : "beijing" }
{ "_id" : 3, "name" : "Nick J" }
或者使用toArry( )逐一查询结果集
> var a=db.tb3.find() ---定义变量
> var doc=a.toArray() ---使用toArray( )生成数组结果集
> doc[0] ---查询结果集中个文档
{ "_id" : 1, "name" : "Jack", "addr" : "tianjin" }
去重查询
> db.tb3.distinct("name",{_id:{$gte:2}})
[ "Tom", "Nick J" ]
注:
distinct返回的记录不能超过BSON文档大限制(16M),否则会报错
更新
更新其实也有很多种方法:
- db.collection.updateOne() —-3.2版本的新功能,只更新匹配查询条件的document记录
- db.collection.updateMany() —-3.2版本的新功能,更新匹配查询条件的所有document记录,其余功能和update()基本一致
- db.collection.update()
-
db.collection.replaceOne() —-3.2版本的新功能,只更新替换匹配查询条件的个document记录,并且是替换整个
默认情况是update只更新一条记录
语法格式:db.collection.update(
, //以{}形式填写查询条件,与find()方法一样 , ---更新动作,默认是会用此区域的值覆盖原数据,仅更新某个字段请用$set,以设置字段的新值 { upsert: , ---可选,当query的document不存在时,直接insert一个文档 multi: , ---update()默认多只更新一条记录,multi:true可以多文档更新 writeConcern: ---一个完成返回模式,详细可查看官方文档 }) 其实update也没什么好说的,在生产环境时小心使用即可;
举例:> db.tb2.update({_id:1},{$set:{userId:11}}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.tb2.update({_id:1},{$set:{name:"Kobe"}}); //name字段不存在时自动添加新字段 WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs1:PRIMARY> db.tb2.find({_id:1}) { "_id" : 1, "userId" : 11, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 }, "name" : "Kobe" } rs1:PRIMARY>
重命名字段
> db.tb2.update({},{$rename:{"userId":"u_id"}},{multi:true}) WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 }) > db.tb2.find() { "_id" : 1, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 }, "name" : "Kobe", "u_id" : 11 } { "_id" : 2, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 }, "u_id" : 3 }
删除字段
> db.tb2.update({},{$unset:{u_id:0}},{multi:true}) //u_id字段之后的值不重要,写多少都可以,依然可以删除此字段 WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 }) > db.tb2.find() { "_id" : 1, "Course" : [ "English", "Math" ], "finished" : [ 1, 26 ], "info" : { "city" : "TJ", "age" : 22 }, "name" : "Kobe" } { "_id" : 2, "Course" : [ "Math", "English" ], "finished" : [ 18, 12 ], "info" : { "city" : "BJ", "age" : 33 } } >
注:
如果删除的字段或者嵌套文档在数组中,可考虑使用$pull
判断性更新(存在即update,不存在则insert)> db.tb3.update({name:"Kobe"},{$set:{addr:"TJ"}},{upsert:true}) //upsert参数即实现此功能 WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : ObjectId("5d3c0cfed33748cc8d4e0e67") }) rs1:PRIMARY> db.tb3.find() { "_id" : 1, "name" : "Jack", "addr" : "tianjin" } { "_id" : 2, "name" : "Tom", "addr" : "beijing" } { "_id" : 3, "name" : "Nick J" } { "_id" : ObjectId("5d3c0cfed33748cc8d4e0e67"), "name" : "Kobe", "addr" : "TJ" }
注:
此功能其实也可以使用save( ) 实现;
数组更新>db.tb4.insert([ { "_id" : 1, "age" : [ 3, 4, 5, 5, 9 ] } { "_id" : 2, "age" : [ 3, 5 ] } ]) > db.tb4.update({age:5},{$set:{"age.$":55}}) //更新匹配到的个元素 { "_id" : 1, "age" : [ 3, 4, 55, 5, 9 ] } { "_id" : 2, "age" : [ 3, 5 ] }
更新数组中的嵌套子文档的字段值
举例,如更新如下文档{ _id: 4, grades: [ { grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 85, mean: 85, std: 8 } ]}
更新语句如下:
>db.tb5.updateOne( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } })
数组中添加一个元素
> db.tb4.update({_id:2},{$addToSet:{age:100}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.tb4.find() { "_id" : 1, "age" : [ 3, 4, 55, 5, 9 ] } { "_id" : 2, "age" : [ 3, 5, 100 ] }
当然还有一个特殊方法findAndModify
语法格式:db.collection.findAndModify({ query:
, //查询条件 sort: , //对结果集排序 remove: , //是否移除查询到的文档,只删除一个 update: , //更新一条数据 new: , //true后,返回修改后的文档内容 fields: , //返回结果中显示哪些字段(列) upsert: , //true时,未匹配到则插入一条 bypassDocumentValidation: , //绕过当前集合的文档约束 writeConcern: , //强制写关注 collation: , //指定语言规则 arrayFilters: [ , ... ] //针对数组操作的参数设定 }); 删除
删除其实也没有什么可说的,就是删除你查询到的行数据,虽然也提供了很多种方法,但是一般使用remove就够了;
> db.tb4.remove({_id:1}) WriteResult({ "nRemoved" : 1 }) >db.tb4.remove({}) ---传入空文档{ },则表示删除集合中的所有文档
注:
虽然删除了集合上的所有文档记录,但索引定义还是存在的,不会随之而删除
如果想要删除一个集合,好使用drop( )
, 如db.tb4.drop( )批量操作
上面讲了一堆增删改查的操作,不过都是单一英雄操作,如果我们想批量操作怎么办,那就拍一个《复仇者联盟》就好了呀——BulkWrite()
官方的介绍是:MongoDB提供给客户端可以批量执行写操作的能力,不过只能影响一个collection
BulkWrite()支持一下写操作:
insertOne
updateOne
updateMany
replaceOne
deleteOne
deleteMany
语法格式:db.collection.bulkWrite( [
, , ... ], { writeConcern : , ordered : }) operation1、operation2
都是一个个的写操作,以文档的形式放在[ ]数组中
ordered
:默认是true,即按照写操作的顺序执行,如果过程中某一操作报错,则剩余的操作将中断,false时,则是平行执行,一个操作的报错异常,不会影响到其余操作,但官方一般不建议,因为没有保证性
实验:> db.t4.find() > db.t4.bulkWrite([ ... {insertOne:{"document":{_id:1,a:1,name:"James"}}}, ... {insertOne:{"document":{_id:2,a:2,name:"Johnny"}}}, ... {updateOne:{"filter":{a:1},"update":{$set:{name:"Jack"}}}}, ... {deleteOne:{"filter":{a:2}}}]) { "acknowledged" : true, "deletedCount" : 1, "insertedCount" : 2, "matchedCount" : 1, "upsertedCount" : 0, "insertedIds" : { "0" : 1, "1" : 2 }, "upsertedIds" : { } } > db.t4.find() { "_id" : 1, "a" : 1, "name" : "Jack" }
实验结果:
插入一条、更新其中一条、删除其中一条