好好学习,天天向上

MongoDB初识之增删改

插入

1
2
3
4
5
# insert操作会给文档增加一个“_id”键,然后将其保存到MongoDB中
> db.foo.insert({"bar":"baz"})
WriteResult({ "nInserted" : 1 })
> db.foo.find()
{ "_id" : ObjectId("5643e996b477ef42bea500b1"), "bar" : "baz" }
  1. 批量插入:当要插入多个文档时,使用批量插入会提高插入的速度。批量插入也可以用于导入数据。使用批量插入的时候,传递一个由文档构成的数组给数据库即可。
  2. 插入原理:插入时,驱动程序会将数据转换成BSON形式再送到数据库。数据库解析BSON,检验是否包含“_id键并且文档不超过4MB后直接将文档原样存入数据库。
  • 坏处:不检查数据,因此允许插入无效的数据
  • 好处:更加安全,因为不执行代码,因此远离注入式攻击

删除

1
2
3
4
5
# 删除foo中所有文档,但不会删除集合本身,原有的索引也会保留
> db.foo.remove({})
WriteResult({ "nRemoved" : 1 })
# 提供一个查询文档,则只有符合条件的文档才被删除
> db.mailing.list.remove({"opt-out":true})
注意:当想删除集合中所有文档时,不传递参数会得到一个` Error: remove needs a query`的错误。可以参考[remove needs a query at src/mongo/shell/collection.js](http://stackoverflow.com/questions/25104095/remove-needs-a-query-at-src-mongo-shell-collection-js)
  1. 删除是永久的,不能撤销,不能恢复
  2. 如果要清除整个集合,直接删除集合(然后重建索引)会更快。因为remove()操作会挨个删除文档并且更新索引,而drop()操作会直接删除集合的所有文档和索引。但也要注意,不能使用任何限制条件,而且因为整个集合都被删除了,所有的索引都会不见的。

更新

使用update方法: update( query , obj , upsert , multi ) 第一个参数是查询文档,用来找出要更新的文档;第二个参数是修改器文档,描述对找到的文档做哪些修改。第三个参数表示是否为upsert。第四个参数表示是否修改所有匹配到的项。 更新操作是原子的:若两个更新同时发生,先到服务器的先执行,接着执行另外一个。

文档替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 完全用一个新文档替代匹配的文档。适用于模式结构发生了较大变化的时候。
> var joe = db.users.findOne({"name":"joe"})
> joe.relationships = {"friends":joe.friends,"enemies":joe.enemies};
{ "friends" : 32, "enemies" : 2 }
> joe.username = joe.name
joe
> delete joe.friends
true
> delete joe.enemies
true
> delete joe.name
true
> db.users.update({"name":"joe"},joe)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  • 常见错误是查询条件匹配了多个文档,然后更新的时候由于第二个参数的存在就产生重复的"_id"值。数据库会报错,不做任何修改。因此,最好确保更新总是指定唯一文档。
  • 当文档只有一部分要更新时,使用原子弹更新修改器可以使得更新更为有效。

使用修改器

更新修改器是特殊的键,用来指定复杂的更新操作,例如调整、增加或者删除键,还可能是操作数组或者内嵌文档。 使用修改器时,"_id"的值不能改变。其他键值,包括其他唯一索引的键,都是可以更改的。 ### "\(set"修改器 用来指定一个键的值,若这个键不存在,则创建它。可以用于更新模式或者增加用户定义键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# "$set":不存在则创建
> db.users.update({"_id":ObjectId("5643f109b477ef42bea500b2")},
... {"$set":{"favorite book":"war and peace"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
"_id" : ObjectId("5643f109b477ef42bea500b2"),
"relationships" : {
"friends" : 32,
"enemies" : 2
},
"username" : "joe",
"favorite book" : "war and peace"
}
# "$set":存在则修改
> db.users.update({"_id":ObjectId("5643f109b477ef42bea500b2")}, {"$set":{"favorite book":"green eggs and ham"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne()
{
"_id" : ObjectId("5643f109b477ef42bea500b2"),
"relationships" : {
"friends" : 32,
"enemies" : 2
},
"username" : "joe",
"favorite book" : "green eggs and ham"
}
# "$set":修改键的数据类型
> db.users.update({"username":"joe"}, {"$set":{"favorite book":["cat's cradle","foundation trilogy","ender's game"]}})
# "$unset":完全删除键
> db.users.update({"username":"joe"},
... {"$unset":{"favorite book":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
* 增加、修改或者删除键的时候,应该适用\)修改器。

增加和减少

"$inc"修改器用来增加已有键的值,或在键不存在时创建一个键。 * 只能用于整数、长整数或双精度浮点数。 * \(inc键的值必须为数字,不能使用字符串、数组或其他非数字的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
> db.games.insert({"game":"pinball","user":"joe"})
WriteResult({ "nInserted" : 1 })
# "$inc":不存在则创建
> db.games.update({"game":"pinball","user":"joe"},
... {"$inc":{"score":50}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.find({"user":"joe"})
{ "_id" : ObjectId("5643fb09b477ef42bea500b4"), "game" : "pinball", "user" : "joe", "score" : 50 }
# "$inc":存在则增加
> db.games.update({"game":"pinball","user":"joe"}, {"$inc":{"score":10000}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.find({"user":"joe"})
{ "_id" : ObjectId("5643fb09b477ef42bea500b4"), "game" : "pinball", "user" : "joe", "score" : 10050 }
### 数组修改器 只能用在值为数组的键上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> db.blog.posts.find()
{ "_id" : ObjectId("5643fd1ab477ef42bea500b5"), "title" : "A blog post", "content" : "this is a test" }
# "$push":若指定的键已存在,会向已有的属组末尾加入一个元素,要是没有则创建一个新数组
> db.blog.posts.update({"title":"A blog post"},{$push:{"comments":
... {"name":"joe","email":"joe@exmple.com","content":"nice post."}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.posts.find()
{ "_id" : ObjectId("5643fd1ab477ef42bea500b5"), "title" : "A blog post", "content" : "this is a test", "comments" : [ { "name" : "joe", "email" : "joe@exmple.com", "content" : "nice post." } ] }
# "$ne"
> db.papers.update({"authors cited":{"$ne":"Richie"}},
... {$push:{"authors cited":"Richie"}})
# "$addToSet":避免重复
> db.users.update({"username":"joe"},
... {"$addToSet":{"emails":"joe@gmail.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
# "$addToSet""$each"组合起来,可以添加多个不同的值
> db.users.update({"_id":ObjectId("5643f109b477ef42bea500b2")}, {"$addToSet": {"emails":{"$each":["joe@php.net","joe@example.com","joe@python.org"]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
# "$pop":从数组任何一端删除元素。{"$pop":{"key":1}}从数组末尾删除一个元素;{"$pop":{"key":-1}}从数组头部删除一个元素
> db.blog.posts.update({"_id": ObjectId("5643fd1ab477ef42bea500b5")}, {"$pop":{"comments":-1}})
# "$pull":基于特定条件删除元素
### 数组的定位修改器 有两种方法可以操作数组中的值: * 通过位置:可以将下标(以0开始)直接作为键来选择元素
1
> db.blog.posts.update({"_id": ObjectId("5643fd1ab477ef42bea500b5")}, {"$inc":{"comments.0.votes":1}})
* 使用定位操作符"\)"
1
2
3
# 定位查询文档已经匹配的元素,并进行更新。
# 注意,定位符只更新第一个匹配的元素。
> db.blog.posts.update({"comments.author":"John"},{"$set":{"comments.$.author":"Jim"}})
### 修改器速度 不同的修改器根据它们的作用而速度不一。

upsert

一种特殊的更新。若没有文档符合更新条件,则以这个条件和更新文档为基础创建一个新的文档。若找到了匹配的文档,则正常更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
# update的第3个参数表示这是个upsert
> db.analytics.update({"url":"/blog"},{"$inc":{"visits":1}},true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("56442f86d00ed0231039a956")
})
# 创建新文档会将条件文档作为基础,然后将修改器文档应用于其上。
> db.math.remove({})
> db.math.update({"count":25},{"$inc":{"count":3}},true)
> db.math.findOne()
{ "_id" : ObjectId("56443012d00ed0231039a957"), "count" : 28 }
save是一个shell函数,可以在文档不存在时插入,存在时更新。

1
2
3
4
> var x = db.foo.findOne()
> x.num = 42
# save只有一个参数:文档
> db.foo.save(x)

更新多个文档

update默认只更新符合匹配条件的第一个文档。要使所有匹配到的文档都得到更新,可以设置update的第4个参数为true。

1
> db.users.update({birthday:"10/13/1978"}, {"$set":{"gift":"Happy Birthday!"}}, false, true)
## 返回已更新的文档 可以使用getLastError获得更新文档的数目:> db.runCommand({getLastError:1}) 可以使用findAndModify返回已更新的文档。
1
2
3
4
5
6
7
> db.runCommand({"findAndModify": "processes", # 字符串,集合名。
... "query": {"status":"READY"}, # 查询文档,用来检索文档的条件
... "sort": {"priority":-1}, # 排序结果的条件
... #"update": {"status": "RUNNING"}, # 修改器文档,对所找到的文档执行的更新
... "remove": true, # 布尔类型,表示是否删除文档
... "new": true # 布尔类型,表示返回的是更新前的文档还是更新后的文档。默认是更新前的文档
... )
* "update"和"remove"必须有一个,也只能有一个 * 若匹配不到,则返回一个错误 * 一次只能处理一个文档,且只能更新已有文档 * 速度相对普通更新慢一点

瞬间完成

上面提到的三个操作(插入、删除和更新)都是瞬间完成的,即不需要等待数据库响应(不是异步操作)。 ## 安全操作 上面三种操作都有“安全”版本,执行时检查到了错误还可以重来。安全的版本在执行完了操作后立即运行getLastError命令。驱动程序也会等待数据库响应。一般呢,会抛出一个可以被捕获的异常。 1. 如果不考虑安全,就只用默认的瞬间完成 2. 若需要活得稍长一点,就把重要的用户数据用安全的方式操作,其余的数据采用瞬间完成。 3. 用来捕获“常规”错误,是一种调试数据库“奇怪”行为的好方法。

请求和连接

数据库会为每一个MongoDB数据库连接创建一个队列,用于存放这个连接的请求。队列采用先来先处理的方式。 使用Ruby, Python和Java驱动程序时要特别注意,因为它们的驱动程序都使用了连接池。

请言小午吃个甜筒~~