当前版本的mongodb消息最大长度是16MB,所以使用批量插入时还是有限制的。
插入:原理和作用
文档大小不能超过4MB。
插入形式:db.foo.insert({"bar": "baz"})
mongodb在插入时候并不执行代码,所以这块没有注入式攻击的可能性。
删除文档
db.users.remove() 上述命令会删除users集合中的所有文档。但不会删除集合本身,原有的索引也会保留。
假设要删除mailing.list集合中“output"为true的人:db.mailing.list.remove({"opt-out": true})
删除数据是永久的,不能恢复。
删除文档通常会很快,但是要是直接删除集合(然后重建索引)会更快:db.drop_collection("bar") (php中使用dropCollection方法)
更新文档
更新文档是原子操作,若是两个更新同时发生,先到达服务器的先执行,接着执行另一个。
更新简单的情形是用一个新的文档替换匹配的文档。这适用于模式结构发生了较大变化的时候:
var joe = db.users.findOne({"name": "joe"});
joe.replationships = {"friends": joe.friends, "enemies": joe.enemies};
joe.username = joe.name;
delete joe.friends;
delete joe.enemies;
delete joe.name;
db.users.update({"name": "joe"}, joe}
使用修改器
通常文档只会有一部分要更新。利用原子的更新修改器,更新修改器是种特殊的键,用来指定复杂的更新操作。
$inc修改器:db.analytics.update({"url": "www.example.com"}, {"$inc": {"pageviews": 1}}),接着执行一个find操作,会发现"pageviews"的值增加了1。
使用修改器时,"_id"的值不能改变。其他键值,包括其他唯一索引的键,都是可以改变的。
"$set"修改器入门:
"$set"用来指定一个键的值。如果这个键不存在,则创建它。
db.user.update({"name": "joe"}, {"$set": {"favorite book": ["cat' cradle", "foundation trilogy"]}})
如果用户突然发现自己不爱读书,可以使用"$unset"将键完全删除:
db.user.update({"name": "joe"}, {"$unset": {"favorite book": 1}})
也可以用"$set"修改内嵌文档:
db.blog.posts.findOne()
{
"_id": ObjectId("53438fb9127a528b218b4567"),
"title": "A Blog Post",
"content": "...",
"author": {
"name": "joe",
"email": "joe@example.com"
}
}
db.blog.posts.update({"author.name": "joe"}, {"$set": {"author.name": "joe schmoe"}})
增加和减少:
"$inc"修改器用来增加已有键的值,或者在键不存在时创建一个键。
db.games.update({"name": "pingball", "user": "joe"}, {"$inc": {"score": 50}})
分数键原来也许并不存在,所以"$inc"创建了这个键,并把值设定成增量:50。
"$inc"只能用于整数,长整数或双精度浮点数。
数组修改器:
数组是常用且非常有用的数据结构:他们不仅是可通过索引进行引用的列表,而且还可以作为集合来用。
如果指定的键已经存在,"$push"会向已有的数组末尾加入一个元素,要是没有就会创建一个新的数组。
假设要存储博客文章,要添加一个包含一个数组的"comments"键。可以向还不存在的"comments"数组push一个评论,这个数组会被自动创建,并加入评论:
db.blog.posts.update({"title": "A blog post"}, {$push: {"comments": {"name": "joe", "email": "joe@example.com", "content": "nice post"}}})
要是还想添加一条评论,可以接着使用"$push":
db.blog.posts.update({"title": "A blog post"}, {$push: {"comments": {"name": "bob", "email": "bob@example.com", "content": "good post"}}})
经常会有这种情况,如果一个值不在数组里面就把它加进去可以用"$addToSet"(要是没有就会创建一个新的数组)来实现:
db.blog.users.findOne({"_id": ObjectId("53438fb9127a528b218b4567")})
{
"_id": ObjectId("53438fb9127a528b218b4567"),
"username": "joe",
"emails":
[
"joe@example.com",
"joe@gmial.com",
"joe@yahoo.com"
]
}
当添加新的地址时,用"$addToSet"可以避免重复:
db.users.update({"_id": ObjectId("53438fb9127a528b218b4567")}, {"$addToSet": {"emails": "joe@gmail.com"}}) 不会进行添加
db.users.update({"_id": ObjectId("53438fb9127a528b218b4567")}, {"$addToSet": {"emails": "joe@hotmail.com"}}) 会添加到数组末尾
将"$addToSet"和"$each"组合起来,可以添加多个不同的值,例如一次想添加多个邮件地址,就可以使用这些修改器:
db.users.update({"_id": ObjectId("53438fb9127a528b218b4567")},
{"$addToSet": {"email": {"$each": ["joe@php.net", "joe@example.com", "joe@python.org"] } } })
删除数组元素: {$pop: {key: 1}} 从数组末尾删除一个元素, {$pop: {key: -1}}则从头部删除。
基于条件删除元素:
db.lists.insert({"todo": ["dishes", "laundry", "dry cleaning"] }) 现在要删除"laundry":db.lists.update({}, {"$pull": {"todo": "laundry"}})
"$pull"会将所有匹配的部分删除。
数组定位修改器:
数组都是以0开头的,可以直接将下标作为键来选择元素。例如这里有个文档,其中包含由内嵌文档组成的数组,比如包含评论的博客文章:
db.blog.posts.findOne()
{
"_id": ObjectId("53438fb9127a528b218b4567"),
"content": "...",
"comments": [
{
"comment": "good post",
"author": "john",
"votes": 0
},
{
"comment": "i thought...",
"author": "Claire",
"votes": 3
}
]
}
如果想增加第一个评论的投票数,可以这么做:
db.blog.update({"post": post_id}, {"$inc": {"comment.0.votes": 1}})
很多情况下,不预先查询文档就不能知道要修改数组的下标。为了克服这个困难,MongoDB提供了定位操作符"$",用来定位查询文档已匹配的元素,并进行更新。例如,要是用户john把名字改成jim,就可以用定位符替换评论中的名字:
db.blog.update({"comments.author": "john"}, {"$set" : {"comments.$.author": "jim"}}) 定位符只更新第一个匹配的元素。所以如果john的评论有不止一个评论,那么只有他的第一个评论中的名字会被改变。
修改器的速度
$inc能就地修改,因为不需要改变文档的大小,只需要将键的值修改一下,所以非常快。而数组修改器可能更改了文档的大小,就会慢一些。MongoDB预留了些补白文档,来适应大小变化,但是要超出了原来的空间,最后还是要分配一些新的空间。空间分配会减慢速度。
Upsert
upsert是一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。如果找到匹配的文档,则正常更新。
db.analytics.update({"url": "/blog"}, {"$inc": {"visits": 1}, true})创建新文档会将条件文档作为基础(先将条件作为数据创建文档),然后将修改器文档应用其基础上。
更新多个文档
默认情况下,更新只能对符合匹配条件的第一个文档执行操作。要是有多个文档符合条件,其余的文档就没有变化。要是所有匹配的文档都能得到更新,可以设置update的第4个参数为ture。
返回已更新的文档
findAndModify的调用方式和普通的更新略有不同,还有点慢,这是因为他要等待数据库的相应。这对于操作查询以及执行其他需要取值和赋值风格的原子性操作来说是十分方便的。findAndModify可以在一个操作中返回结果并更新:
db.runCommand({
"findAndModify": "processess",
"query": {"status": "READY"},
"sort": {"property": -1},
"update": {"$set": {"status": "running"}}
})
注意文档中的状态仍然为"READY"。先返回结果然后更新。
findAndModify既有"update"键也有"remove"键。"remove"键表示将匹配到的文档从集合里面删除:
db.runCommand({
"query": {"status": "READY"},
"sort": {"property": -1},
"remove": true
})
PHP的一个演示:
$db -> command
(
array
(
"findAndModify" => "test", // 集合名
// "query" => array() 查询条件
"sort" => array("age" => -1),
"update" => array('$set' => array("name" => 'ss')),
"new" => true, // 是否返回更新后的数据
"options" => array("j" => true)
)
);
"update"和"remove"必须有一个,也只能有一个。要是匹配不到的文档,这个命令会返回一个错误。这个命令一次只能处理一个文档,也不能执行upsert操作。
瞬间完成
插入、删除、更新都是瞬间完成的,客户端将文档发送给服务器后就立刻干别的了。客户端永远不会收到”好了“,"之道了”或者“有问题” 这类的响应。在没有服务器的时候,客户端还是会发送写操作到服务器的,完全不理会到底有没有服务器。
安全版本在执行完了操作之后立即运行getLastEorror命令,检查是否执行成功。驱动程序会等待数据库响应。
请求和连接
数据库会为每个MongoDB数据库连接创建一个队列,存放这个连接的请求,当客户端发送一个请求,会被放到队列的末尾。只有队列的请求都执行完毕,后续的请求才会执行。注意:每个请求都有独立的队列,要是打开两个shell,就有两个数据库连接。在一个shell中执行插入,之后在另一个shell中进行查询不一定能得到插入的文档。然而在同一个shell中,插入后在进行查询一定能查到的。当开发者用一个线程插入数据,用另一个线程检查是否插入成功时,会遇到这样的问题。有那么一两秒的时间,好像根本没有插入数据,但随后又突然冒出来。
浙公网安备 33010602011771号