前言
从库至今数据库的作用其实最主要的就是存放数据,而对数据的操作也就是增删查改,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" : [ ]
})
虽然insert和insertMany都能一次插入多条document记录,但是返回结果不一样,不信你看
> 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: 第二个大括号{ },指定哪些字段需显示,从而限制结果集的输出大小
基于上面创建的tb1集合进行条件查询
> 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类型列表如下:
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(
<query>, //以{}形式填写查询条件,与find()方法一样
<update>, ---更新动作,默认是会用此区域的值覆盖原数据,仅更新某个字段请用$set,以设置字段的新值
{
upsert: <boolean>, ---可选,当query的document不存在时,直接insert一个文档
multi: <boolean>, ---update()默认最多只更新一条记录,multi:true可以多文档更新
writeConcern: <document> ---一个完成返回模式,详细可查看官方文档
})
其实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: <document>, //查询条件
sort: <document>, //对结果集排序
remove: <boolean>, //是否移除查询到的文档,只删除一个
update: <document>, //更新一条数据
new: <boolean>, //true后,返回修改后的文档内容
fields: <document>, //返回结果中显示哪些字段(列)
upsert: <boolean>, //true时,未匹配到则插入一条
bypassDocumentValidation: <boolean>, //绕过当前集合的文档约束
writeConcern: <document>, //强制写关注
collation: <document>, //指定语言规则
arrayFilters: [ <filterdocument1>, ... ] //针对数组操作的参数设定
});
删除
删除其实也没有什么可说的,就是删除你查询到的行数据,虽然也提供了很多种方法,但是一般使用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(
[ <operation 1>, <operation 2>, ... ],
{
writeConcern : <document>,
ordered : <boolean>
})
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" }
实验结果:
插入一条、更新其中一条、删除其中一条
网友评论