写在前面
前面一篇对MongoDB的基本操作有了一个大致的了解,这一篇中,详细讲解MangoDB的创建、更新、删除文档。
1.插入并保存文档
插入是MongoDB中的最基本的方法,那往一个集合中插入数据,上一篇已经说过:
Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator>E: E:\>cd MongoDB E:\MongoDB>mongo.exe MongoDB shell version: 2.0.2 connecting to: test > use php //选择php数据库 switched to db php > show collections //查看phps数据库中有哪些集合。 blog system.indexes > db.blog.insert({"title":"okokok","content":"123456","time":"2011023023","comments":"23224"}) //选择blog集合。并插入数据。 > db.blog.find() //查看blog集合下所有的文档,已经插入成功。 { "_id" : ObjectId("4f0bc1245a5904bae2b767a8"), "title" : "wowowowo", "content" : "hahahahah", "time" : ISODate("2012-01-10T03:38:45.515Z") } { "_id" : ObjectId("4f0bc11d5a5904bae2b767a7"), "title" : "wowowowo", "content" : "hahahahah", "time" : ISODate("2012-01-10T03:38:45.515Z"), "comments" : "hao a hao a " } { "_id" : ObjectId("4f0cf96bbc8c53013bb90682"), "title" : "okokok", "content" : "123456", "time" : "2011023023", "comments" : "23224" } >上面的文档中,有个“_id”, 本来是要第二篇说的,那这一篇就说说。(引来自:http://www.cnblogs.com/liuhao/archive/2011/12/01/2270649.html)
在一个特定集合内部,需要唯一的标识文档。因此MongoDB中存储的文档都由一个"_id"键,用于完成此功能。这个键的值可以是任意类型的,默认试ObjectId对象。ObjectId对象的生成思路是本文的主题,也是很多分布式系统可以借鉴的思路。
为了考虑分布式,“_id”要求不同的机器都能用全局唯一的同种方法方便的生成它。因此不能使用自增主键(需要多台服务器进行同步,既费时又费力),因此选用了生成ObjectId对象的方法。
ObjectId使用12字节的存储空间,其生成方式如下:
|0|1|2|3|4|5|6 |7|8|9|10|11|
|时间戳 |机器ID|PID|计数器 |
前四个字节时间戳是从标准纪元开始的时间戳,单位为秒,有如下特性:
时间戳的实际值并不重要,不需要对服务器之间的时间进行同步(因为加上机器ID和进程ID已保证此值唯一,唯一性是ObjectId的最终诉求)。
机器ID是服务器主机标识,通常是机器主机名的散列值。
同一台机器上可以运行多个mongod实例,因此也需要加入进程标识符PID。
前9个字节保证了同一秒钟不同机器不同进程产生的ObjectId的唯一性。后三个字节是一个自动增加的计数器(一个mongod进程需要一个全局的计数器),保证同一秒的ObjectId是唯一的。同一秒钟最多允许每个进程拥有(256^3 = 16777216)个不同的ObjectId。
总结一下:时间戳保证秒级唯一,机器ID保证设计时考虑分布式,避免时钟同步,PID保证同一台服务器运行多个mongod实例时的唯一性,最后的计数器保证同一秒内的唯一性(选用几个字节既要考虑存储的经济性,也要考虑并发性能的上限)。"_id"既可以在服务器端生成也可以在客户端生成,在客户端生成可以降低服务器端的压力。
2.更新文档
在MongoDB中更新文档采用db.php.update( )。update有两个参数,第一个参数是查询符合条件的文档,即需要对满足这个条件的文档进行更新。第二个参数是修改器文档,即是修改了的内容。
下面看一个例子:
那么,这个例子我们怎么做呢?
Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator>E: E:\>CD MongoDB E:\MongoDB>mongo.exe MongoDB shell version: 2.0.2 connecting to: test > use php switched to db php > show collections blog system.indexes > db.user.insert({"name":"joe","friends":32,"enemies":2}) //重新建一个user集合,并插入一条数据。 > db.user.findOne() //这就是变化前的数据。那么如何用update()更新达到效果呢? { "_id" : ObjectId("4f0d057b081c6af197458524"), "name" : "joe", "friends" : 32, "enemies" : 2 } > var joe = db.user.findOne({"name":"joe"}); //第一步:用findOne()找出需要修改的文档,即找出user集合中name为joe的文档,命名为joe。 > joe.relationships = {"friend":joe.friends,"enemies":joe.enemies}; //第三步:在joe文档中加入一个新的键relationships,它也是一个文档, 它的键friend的值是joe文档中friends的值,enemies键的值是joe文档中enemies键的值。 { "friend" : 32, "enemies" : 2 } > joe.username = joe.name; //第三步:改name为username,采用的手法是:在joe文档中加入一个新的键username,它的值是joe文档中键name的值。 joe > delete joe.friends; //删除joe文档中friends true > delete joe.enemies;//删除文档中enemies true > delete joe.name; //删除文档中name true > db.user.update({"name":"joe"},joe); //用update更新,导入joe修改器文档。 >db.user.findOne({"name":"joe"}) //查看,更新成功! { "_id" : ObjectId("4f0d057b081c6af197458524"), "relationships" : { "friend" : 32, "enemies" : 2 }, "username" : "joe" } >
3.update()中第二个参数中的几个修改器
前面说到update( )中有两个参数,第二参数是修改器,第2点中,那个例子joe是采用全局覆盖的方法。那么有几个修改器,可以完成更加复杂的更新操作,比如:调整,增加或者删除键,还可能是操作数组或者内嵌文档。
1. "$set"修改器。
"$set"用来修改一个键的值。如果这个键不存在就自动创建,并附上值。下面是一个例子,还是上面的php库中的user集合。
> db.user.update({"username":"joe"},{"$set":{"sex":"nan"}}) //采用"$set"修改器,修改sex键的值为"nan",但是sex键不存在,就自动创建一个,并赋值为"nan"。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "relationships" : { "friend" : 32, "enemies" : 2 }, "sex" : "nan", //新添加上去了。 "username" : "joe" } >
> db.user.update({"username":"joe"},{"$set":{"sex":"nv"}}) //再次修改sex键的值为nv,因为已经存在,所以就修改。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "relationships" : { "friend" : 32, "enemies" : 2 }, "sex" : "nv", //sex键修改为"nv"了。 "username" : "joe" } >“$set” 也可以修改键的数据类型,如想吧sex变成一个数组[“nan”,"nv","no"],哈哈,一会男,一会女!那我们也可以做。
> db.user.update({"username":"joe"},{"$set":{"sex":["nv","nan","no"]}}) //将sex键变成数组。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "relationships" : { "friend" : 32, "enemies" : 2 }, "sex" : [ //修改成功。 "nv", "nan", "no" ], "username" : "joe" } >“$set” 同样可以修改内嵌的文档,好比user集合中键relationships是一个文档,那么如何修改这个内嵌文档中friend的值呢。也用"$set" 。
> db.user.update({"username":"joe"},{"$set":{"relationships.friend":1000}}) //采用relationships.friend二级菜单的方式修改,成1000 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "relationships" : { "friend" : 1000, //修改成功。 "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >
“$inc”修改器是增加键的值(等同于 +=),不存在这个键就自动增加一个。只能用于(int )整数,长整数,双精度浮点数 键的叠加。比如age =2 1,用$inc增加 2岁,就变成23了。
> db.user.update({"username":"joe"},{"$inc":{"age":20}}) //将user文档中username为joe的age增加20岁。由于age不存在,就自动创建并赋值为20. > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 20, //新增age键,值为20。 "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" }
> db.user.update({"username":"joe"},{"$inc":{"age":2}}) //将age的值增加2岁,由于已经存在,所以会累加 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, //加上2,变成22。 "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >3.数组修改器(“$push”、“$pop”、“$pull”、“$addToSet”、“$each”)
当涉及到键为数组的时候,用上面的“$set”就不好使了,就不方便了。那么可以用$push往数组中添加值,用$pop删除前或后的值,用$pull删除数组中指定的值,用$addToSet增加非重复值。
> db.user.update({"username":"joe"},{"$set":{"email":["[email protected]","[email protected]","[email protected]"]}}) //由于user集合中没有一个文档有email键。所以用$set添加一个数组。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ //添加数组键成功。 "[email protected]", "[email protected]", "[email protected]" ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } > db.user.update({"username":"joe"},{"$push":{"email":"[email protected]"}}) //往sex数组键中加入一条新的email。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]", "[email protected]", "[email protected]", "[email protected]" //成功。 ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >那么我们要删除数组键中的一个值,怎么办呢?用$pop。
> db.user.update({"username":"joe"},{"$pop":{"email": 1}}) //$pop为1的时候,表示从数组尾部删除一个元素。为-1表示从数组头部删除一个元素 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]", "[email protected]", "[email protected]" //[email protected]已经删除。 ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } > db.user.update({"username":"joe"},{"$pop":{"email":-1}}) //$pop为-1,从头部删除一个元素 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]", //[email protected]已删除。 "[email protected]" ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >那,如果我想删除数组键中指定的某一个键,怎么办。可以用$pull 修改器。
> db.user.update({"username":"joe"},{"$pull":{"email":"[email protected]"}}) //删除email为[email protected]这个元素 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]" //已删除。 ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >那,添加的时候遇到重复的怎么办。$push是不能去掉重复的,只有你加入,就堆入数组尾部。那么可以用$addToSet来添加数组时候,重复的数据不会添加。
> db.user.update({"username":"joe"},{"$addToSet":{"email":"[email protected]"}}) //增加[email protected]。不会重复 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]", "[email protected]" //添加成功! ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } > db.user.update({"username":"joe"},{"$addToSet":{"email":"[email protected]"}}) //再次添加[email protected]。重复了,就不会添加了。 > db.user.findOne() { "_id" : ObjectId("4f0d057b081c6af197458524"), "age" : 22, "email" : [ "[email protected]", "[email protected]" //没添加成功。 ], "relationships" : { "friend" : 1000, "enemies" : 2 }, "sex" : [ "nv", "nan", "no" ], "username" : "joe" } >
3.删除文档
mongoDB中删除文档较为简单 db.user.remove(条件),就可以了。