1.shell基本操作
shell是一个功能完备的JavaScript解释器,可以运行任何JavaScript程序。
通过mongo.exe运行MongoDB shell后,shell会连到MongoDB服务器的test服务器,并将这个数据库连接赋值给全局变量db。
切换数据库
use DB_NAME
查看帮助文档
help
2.数据库的创建和删除
通过切换数据库的命令来创建数据库,不过此时该数据库尚未真正创建起来,只有当数据库中创建了集合后,该数据才会真正建立起来。
use micromsg
此时使用查看数据库的命令是看不到micromsg的
show dbs
删除当前数据库
db.dropDatabase();
3.集合的创建和删除
1)通过函数创建
db.createCollection(name, { size : ..., capped : ..., max : ... } ); e.g. db.createCollection("user");
2)通过直接往不存在的集合里添加文档创建,此时MongoDB会自动创建该集合
var user = { "id": "100100100", "nickname": "路人甲" }; db.user.insert(user);
3)删除集合
db.collections.drop(); e.g. db.user.drop();
4.文档的插入,读取,删除,更新
1)插入,文档大小不能超过4MB
db.collectios.insert(obj); e.g. var user = { "id": "100100100", "nickname": "路人甲" }; db.user.insert(user);
可以通过JS代码插入
for (var i = 0; i < 10; i++) { var user = { "id": 100100100 + i + "", "nickname": "路人甲" + i }; db.user.insert(user); }
批量插入:传递一个由文档构成的数组给数据库。
一次批量插入只是单个的TCP请求,避免了许多零碎请求所带来的开销;加上无需处理大量的消息头,批量插入会快一些。
注:批量插入的数量受限于Mongo消息的长度,目前为16MB。
var users = []; for (var i = 0; i < 10; i++) { var user = { "id": 100100100 + i + "", "nickname": "路人甲" + i }; users[i] = user; } db.user.insert(users);
插入的原理
驱动程序将数据装换为BSON的形式,然后将其送入数据库。数据库解析BSON,检验是否包含"_id"键并且文档不能超过4MB,除此之外,不做别的数据验证,就只是简单地将文档原样存入数据库中。
可以使用--objcheck选项使服务器在插入之前检查文档结构的有效性。
2)读取
这里进介绍简单的两个读取方式
--返回集合里面所有的文档
db.collectios.find(); e.g. db.user.find();
--返回集合里的第一个文档
db.collection.findOne(); e.g. db.user.findOne();
3)删除
db.collectios.remove(query)
query为查询文档,只有符合条件的文档才被删除
//删除"nickname"="路人甲0"的文档 db.user.remove({ "nickname": "路人甲0" }); //删除所有的文档 db.user.remove();
删除数据是永久性的,不能撤销,也不能恢复。
4)更新
更新操作是原子的:若两个更新同时发生,严格按照先后顺序执行。
db.collections.update(query, object[, upsert_bool, multi_bool])
query:查询文档,找出要更新的文档
object:修改器文档,描述对文档做哪些更改
(1)完全替换
db.user.update({"id": "100100100"}, { "id": "100100100", "nickname": "路人甲" });
(2)$inc
累加修改器,对指定的键进行累加操作。
只能用于数字类型。
键不存在时创建一个键,并将其初始化为0
//init + add db.user.update({ "id": "100100110" }, {$inc: {"age": 1}}); //add db.user.update({ "id": "100100110" }, {$inc: {"age": 1}});(3)$set
用来指定一个键的值,如果这个键不存在,则创建它。
//add db.user.update({ "id": "100100109" }, {$set: {"age": 18}}); //update db.user.update({ "id": "100100110" }, {$set: {"age": 17}});
还可以用来操作内嵌文档的键
db.user.update({ "id": "100100110" }, {$set: {"favorite_book": {"name": "三国"}}}); //add db.user.update({ "id": "100100110" }, {$set: {"favorite_book.author": "罗"}}); //update db.user.update({ "id": "100100110" }, {$set: {"favorite_book.author": "罗贯中"}}); //add nest doc + key db.user.update({ "id": "100100109" }, {$set: {"favorite_book.author": "罗"}});(4)$unset
删除文档中的指定键
db.user.update({ "id": "100100109" }, {$unset: {"age": 1}});同理,也可以用来操作内嵌文档的键
db.user.update({ "id": "100100110" }, {$unset: {"favorite_book.author": 1}});(5)$push 向已有的数组的末尾加入一个元素。要是没有就会创建一个数组。
//init + push db.user.update({ "id": "100100101" }, {$push: {"favorite_books": "三国"}}); //push db.user.update({ "id": "100100101" }, {$push: {"favorite_books": "三国"}}); //have added 2 elements可以和$each组合,一次向数组添加多个元素
db.user.update({ "id": "100100101" }, {$push: {"favorite_books": {$each: ["三国", "水浒"]}}});(6)$addToSet 和$push功能一直,但是添加时可以避免重复。
//init + push db.user.update({ "id": "100100102" }, {$addToSet: {"favorite_books": "三国"}}); //push db.user.update({ "id": "100100102" }, {$addToSet: {"favorite_books": "三国"}}); //have added only 1 element和$each组合起来时,可以添加多个不同的值。
db.user.update({ "id": "100100102" }, {$addToSet: {"favorite_books": {$each: ["三国", "水浒", "西游"]}}});(7)$pop 移除数组index位置处的元素,index>0,从尾部开始,index<0从头部开始,index=0和index=1效果一样,值最后一个元素
db.user.update({ "id": "100100102" }, {$addToSet: {"favorite_books": {$each: ["三国", "水浒", "西游", "红楼", "聊斋"]}}}); db.user.update({ "id": "100100102" }, {$pop: {"favorite_books": 0}}); db.user.update({ "id": "100100102" }, {$pop: {"favorite_books": 1}}); db.user.update({ "id": "100100102" }, {$pop: {"favorite_books": -1}}); db.user.update({ "id": "100100102" }, {$pop: {"favorite_books": 6}});(8)$pull 基于特定条件来删除元素,而不是紧紧依靠位置
db.user.update({ "id": "100100102" }, {$push: {"favorite_books": {$each: ["三国", "水浒", "西游", "三国", "三国"]}}}); db.user.update({ "id": "100100102" }, {$pull: {"favorite_books": "三国"}});(9)数组的定位修改器 --通过数组下标(从0开始)定位元素
db.user.update({ "id": "100100104" }, {$addToSet: {"favorite_books": {$each: ["三国", "水浒", "西游", "红楼", "聊斋"]}}}); db.user.update({ "id": "100100104" }, {$set: {"favorite_books.0": "三国1"}});--通过$定位符来定位查询文档已经匹配的元素(只更新第一个匹配的元素)
db.user.update({ "id": "100100105" }, {$push: {"favorite_books": {"name": "三国", author: "罗"}}}); db.user.update({ "id": "100100105" }, {$push: {"favorite_books": {"name": "三国", author: "罗"}}}); //只修改了第一个元素对应的键值 db.user.update({ "favorite_books.author": "罗"}, {$set: {"favorite_books.$.author": "罗贯中"}});(10)upsert 一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新文档。
db.user.update({ "id": "100100111" }, {$set: {"nickname" : "路人甲11", "age": 22}}, true);(11)save 在文档不存在时插入,存在时更新。
db.collections.save(obj)要是该文档有_id键,则调用upsert。否则,会调用插入
// upsert db.user.save({ "_id" : ObjectId("53037ae4564524c4f605e0af"), "age" : 24, "id" : "100100112", "nickname" : "路人甲12" }); // insert db.user.save({ "age" : 24, "id" : "100100112", "nickname" : "路人甲12" });(12)更新多个文档 默认情况下,更新只能对符合匹配条件的第一个文档进行更改。要是多个文档符合条件,其余的文档就没有变化。当需要更新匹配的所有文档时,需要显式说明。
// update 1 doc db.user.update({"id" : "100100112"}, {$set: {"age" : 26}}); // update 2 docs db.user.update({"id" : "100100112"}, {$set: {"age" : 25}}, false, true);通过getLastError命令查看更新的文档数
db.user.update({"id" : "100100112"}, {$set: {"age" : 25}}, false, true); db.runCommand({getLastError: 1});(13)findAndModify命令
相当于 一次find + 一次update/remove + 一次getLastError命令 的原子操作
db.runCommand({ "findAndModify": "user", "query": {"id" : "100100112"}, "sort": {"_id": 1}, "update": {$set: {"nickname": "路人甲"}} });
命令中每个键的对应值如下所示
findAndModify:集合名
query:查询文档
sort:排序结果的条件
update:修改器文档 / remove:boolean类型,表示是否删除文档 (update和remove必须有且仅有一个)
new:boolean类型,表示返回的是更新前的文档还是更新后的文档,默认为更新前的文档。
5.瞬间完成
对文档的插入,删除,更新都是瞬间完成的,它们都不等待数据库响应。
不等于异步操作,客户端将文档发送给服务器后就立刻干别的了。
有点:速度快,缺点:可能会丢失数据
6.安全操作
安全操作在执行了操作之后立即运行getLastError命令,来检查是否执行成功。
驱动程序会等待数据库响应,然后适当地处理错误,一般会抛出一个可被捕获的异常。
7.请求和连接
MongoDB会为每一个数据库连接创建一个队列,存放这个连接的请求。
客户端发送的请求将会被放到队列的末尾,请求严格按照队列的顺序进行执行,因此单个连接总是能读到自己写得东西。
注:每个连接都有独立的队列,要是打开两个Shell,就有两个数据库连接。
在一个Shell中插入,之后在另一个Shell中查询不一定能得到插入的文档,可能会存在延时。