插入是向MongoDB中添加数据的基本方法。对目标集使用insert方法,插入一个文档:
> db.foo.insert({"bar" : "baz"}) WriteResult({ "nInserted" : 1 }) > db.foo.find() { "_id" : ObjectId("55062bcad94d123625069ca4"), "bar" : "baz" }这个操作会给文档增加一个“_id”键(要是原来没有的话),然后将其保存到MongoDB中。
优势:一次批量插入只是单个TCP请求,避免了许多零碎的请求所带来的开销。
1. 插入时,驱动程序会将数据转换为BSON的形式。
2. 数据库解析BSON,只检验是否包含"_id"键并且文档不超过4MB。
副作用: 允许插入无效的数据
好处: 让数据库更加安全,远离注入式攻击
使用remove进行永久性的删除。
> db.foo.find() { "_id" : ObjectId("550631a5d94d123625069ca8"), "bar" : "foo" } { "_id" : ObjectId("550631b0d94d123625069ca9"), "bar" : "foo" } { "_id" : ObjectId("550631b3d94d123625069caa"), "bar" : "foo1" } { "_id" : ObjectId("550631b7d94d123625069cab"), "bar" : "foo2" } > db.foo.remove({"bar" : "foo"}) WriteResult({ "nRemoved" : 2 }) > db.foo.remove({}) WriteResult({ "nRemoved" : 2 }) > db.foo.find() >
删除速度:
test.py:
import pymongo import time connection = pymongo.Connection("localhost", 27017) db = connection.test_database for i in range(1000000): db.test_collection.insert({"foo" : i}) start = time.time() db.test_collection.remove({}) total = time.time() - start print "%d seconds" % total删除时间为0s.
文档存入数据库以后,就可以使用update方法来修改它。update有两个参数,一个是查询文档,用来找出要更新的文档,另一个是修改器文档,描述对找到的文档做哪些修改。
整个文档的替换过程中,我们要找到唯一的键值作为筛选的标准。假设我们要将:
变成:
则我们需要这样做:
> db.users.insert({"name" : "joe", "friends" : 32, "enemies" : 2}) WriteResult({ "nInserted" : 1 }) > 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.find() { "_id" : ObjectId("550646922a18d2707db456a1"), "name" : "joe", "friends" : 32, "enemies" : 2 } > joe { "_id" : ObjectId("550646922a18d2707db456a1"), "relationships" : { "friends" : 32, "enemies" : 2 }, "username" : "joe" } > db.users.update({"name" : "joe"}, joe) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.find() { "_id" : ObjectId("550646922a18d2707db456a1"), "relationships" : { "friends" : 32, "enemies" : 2 }, "username" : "joe" }但是,假设有多个{“name”:"joe"}就麻烦了:
> db.people.find() { "_id" : ObjectId("5506482d2a18d2707db456a3"), "name" : "joe", "age" : 65 } { "_id" : ObjectId("550648302a18d2707db456a4"), "name" : "joe", "age" : 20 } { "_id" : ObjectId("550648352a18d2707db456a5"), "name" : "joe", "age" : 49 } > joe = db.people.findOne({"name" : "joe", "age" : 20}) { "_id" : ObjectId("550648302a18d2707db456a4"), "name" : "joe", "age" : 20 } > joe.age++ 20 > db.people.update({"name" : "joe", "age" : 20}, joe) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.people.find() { "_id" : ObjectId("5506482d2a18d2707db456a3"), "name" : "joe", "age" : 65 } { "_id" : ObjectId("550648302a18d2707db456a4"), "name" : "joe", "age" : 21 } { "_id" : ObjectId("550648352a18d2707db456a5"), "name" : "joe", "age" : 49 }这里,我们必须编写:
> db.people.update({"name" : "joe", "age" : 20}, joe)而不能草率的写成:
> db.people.update({"name" : "joe"}, joe)因为这样会找到age为65的joe,但是由于id不一致,导致会update失败。
通常文档只有一部分要更新。利用原子的更新修改器,可以使得这种部分更新极为高效。更新修改器是种特殊的键,用来指定复杂的更新操作。
假设我们访问一个网址要递增访问次数:
> db.analytics.find() { "_id" : ObjectId("55064a722a18d2707db456a6"), "url" : "www.example.com", "pageviews" : 52 } > db.analytics.update({"url" : "www.example.com"}, {"$inc": {"pageviews" : 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.analytics.find() { "_id" : ObjectId("55064a722a18d2707db456a6"), "url" : "www.example.com", "pageviews" : 53 }
$set用来指定一个键的值。如果这个键不存在,则创建它。例如,用户资料存储在下面这样的文档里:
> db.users.findOne() { "_id" : ObjectId("55064d5c2a18d2707db456a7"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "wisconsin" }假设我们向把喜欢的书籍添加进去,则可以用$set:
> db.users.update({"_id" : ObjectId("55064d5c2a18d2707db456a7")}, {$set : {"favorite book" : "war and peace"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.findOne() { "_id" : ObjectId("55064d5c2a18d2707db456a7"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "wisconsin", "favorite book" : "war and peace" }因为_id具有唯一的标志,所以建议使用_id而非使用{“name” : "joe"}。
假设我们喜欢的实际上是另一本书,那么我们依旧可以使用$set:
> db.users.update({"_id" : ObjectId("55064d5c2a18d2707db456a7")}, {$set : {"favorite book" : "green eggs and ham"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.findOne() { "_id" : ObjectId("55064d5c2a18d2707db456a7"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "wisconsin", "favorite book" : "green eggs and ham" }而如果我们喜欢的一堆书,那么可以将$set的值变成列表(通常使用$push进行列表的插入,后面介绍)
也可以使用"$set"修改内嵌文档:
> db.blog.posts.insert({"title" : "A Blog Post", "content" : "...", "author" : {"name" : "joe", "email" : "[email protected]"}}) WriteResult({ "nInserted" : 1 }) > db.blog.posts.update({"author.name" : "joe"}, {"$set" : {"author.name" : "joe schmoe"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blog.posts.find() { "_id" : ObjectId("550668e2c0e0d2cfc19edb82"), "title" : "A Blog Post", "content" : "...", "author" : { "name" : "joe schmoe", "email" : "[email protected]" } }
“$inc”修改器用来增加已有键的值,或者在键不存在时创建一个键。
> db.foo.insert({"url" : "www.example.com", "pageviews" : 1}) WriteResult({ "nInserted" : 1 }) > db.foo.update({"url" : "www.example.com"}, {"$inc" : {"pageviews" : 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.foo.find() { "_id" : ObjectId("55066b30c0e0d2cfc19edb89"), "url" : "www.example.com", "pageviews" : 2 }
如果指定的键已经存在,"$push"会向已有的数组末尾加入一个元素,要是没有就会创建一个新的数组。例如向博客文章添加评论:
> db.blog.posts.insert({"title" : "A blog post", "content" : "..."}) WriteResult({ "nInserted" : 1 }) > db.blog.posts.update({"title" : "A blog post"}, {$push : {"comments" : {"name" : "joe", "email" : "[email protected]", "content" : "nice post."}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blog.posts.update({"title" : "A blog post"}, {$push : {"comments" : {"name" : "bob", "email" : "[email protected]", "content" : "good post."}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blog.posts.findOne() { "_id" : ObjectId("55066dbbc0e0d2cfc19edb8b"), "title" : "A blog post", "content" : "...", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post." }, { "name" : "bob", "email" : "[email protected]", "content" : "good post." } ] }而使用$addToSet可以插入不重复的数据:
> db.users.insert({"username" : "joe", "emails" : ["[email protected]", "[email protected]", "[email protected]"]}) WriteResult({ "nInserted" : 1 }) > db.users.update({"username" : "joe"}, {$addToSet : {"emails" : "[email protected]"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 }) > db.users.update({"username" : "joe"}, {$addToSet : {"emails" : "[email protected]"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.findOne() { "_id" : ObjectId("55066eb1c0e0d2cfc19edb8c"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]", "[email protected]" ] }假设一次添加多个邮箱,则将“$addToSet”和"$each"结合起来即可:
> db.users.update({"username" : "joe"}, {$addToSet : {"emails" : {$each : ["[email protected]", "[email protected]"]}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.findOne() { "_id" : ObjectId("55066eb1c0e0d2cfc19edb8c"), "username" : "joe", "emails" : [ "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]" ] }我们可以使用$pop这个修改器从数组任何一端删除元素。{$pop : {key : 1}}从数组末尾删除一个元素,{$pop : {key : -1}}则从头部删除。
> db.users.remove({}) WriteResult({ "nRemoved" : 1 }) > db.users.insert({"name" : "joe", "num" : [1, 2, 3]}) WriteResult({ "nInserted" : 1 }) > db.users.update({"name" : "joe"}, {$pop : {"num" : 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.find() { "_id" : ObjectId("550670d5c0e0d2cfc19edb8d"), "name" : "joe", "num" : [ 1, 2 ] } > db.users.update({"name" : "joe"}, {$pop : {"num" : -1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.find() { "_id" : ObjectId("550670d5c0e0d2cfc19edb8d"), "name" : "joe", "num" : [ 2 ] } >而$pull会将所有匹配的部分全部删除:
> db.users.remove({}) WriteResult({ "nRemoved" : 1 }) > db.users.insert({"name" : "joe", "num" : [1, 2, 3, 1, 1]}) WriteResult({ "nInserted" : 1 }) > db.users.update({"name" : "joe"}, {$pull : {"num" : 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.users.find() { "_id" : ObjectId("55067174c0e0d2cfc19edb8e"), "name" : "joe", "num" : [ 2, 3 ] }
若是数组有多个值,而我们只想对其中的一部分进行操作,则:通过位置或者定位操作符。
> db.blog.posts.findOne() { "_id" : ObjectId("55066dbbc0e0d2cfc19edb8b"), "title" : "A blog post", "content" : "...", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post.", "votes" : 0 }, { "name" : "bob", "email" : "[email protected]", "content" : "good post.", "votes" : 0 } ] } > db.blog.posts.update({"title" : "A blog post"}, {$inc : {"comments.0.votes" : 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blog.posts.findOne() { "_id" : ObjectId("55066dbbc0e0d2cfc19edb8b"), "title" : "A blog post", "content" : "...", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post.", "votes" : 1 }, { "name" : "bob", "email" : "[email protected]", "content" : "good post.", "votes" : 0 } ] }这里comments.0.votes代表数组的第一个元素,0为索引。
而$的作用是:将定位已经匹配的元素:
> db.blog.posts.update({"comments.name" : "bob"}, {$set : {"comments.$.name" : "Bob"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blog.posts.findOne() { "_id" : ObjectId("55066dbbc0e0d2cfc19edb8b"), "title" : "A blog post", "content" : "...", "comments" : [ { "name" : "joe", "email" : "[email protected]", "content" : "nice post.", "votes" : 1 }, { "name" : "Bob", "email" : "[email protected]", "content" : "good post.", "votes" : 0 } ] }
upsert是一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。(第三个参数为true则代表upsert)
> db.math.remove({}) WriteResult({ "nRemoved" : 0 }) > db.math.update({"count" : 25}, {"$inc" : {"count" : 3}}, true) WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : ObjectId("550677f5312f793834187ab9") }) > db.math.findOne() { "_id" : ObjectId("550677f5312f793834187ab9"), "count" : 28 }save可以在文档不存在时插入,存在时更新。它只有一个参数:文档。要是这个文档含有“_id”键,save会调用upsert。否则,会调用插入。
> var x = db.foo.findOne() > x { "_id" : ObjectId("55066b30c0e0d2cfc19edb89"), "url" : "www.example.com", "pageviews" : 2 } > x.num = 42 42 > db.foo.save(x) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > x { "_id" : ObjectId("55066b30c0e0d2cfc19edb89"), "url" : "www.example.com", "pageviews" : 2, "num" : 42 }
默认情况下,更新只能对符合匹配条件的第一个文档执行操作。要是多个文档符合条件,其余的文档就没有变化。要使得所有匹配到的文档都得到更新,可以设置update的第四个参数为true。
> db.foo.find() { "_id" : ObjectId("55067bd1c0e0d2cfc19edb93"), "name" : "joe", "age" : 1 } { "_id" : ObjectId("55067bd4c0e0d2cfc19edb94"), "name" : "joe", "age" : 2 } { "_id" : ObjectId("55067bd7c0e0d2cfc19edb95"), "name" : "joe", "age" : 3 } > db.foo.update({"name" : "joe"}, {$set : {"wish" : "hello world"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.foo.find() { "_id" : ObjectId("55067bd1c0e0d2cfc19edb93"), "name" : "joe", "age" : 1, "wish" : "hello world" } { "_id" : ObjectId("55067bd4c0e0d2cfc19edb94"), "name" : "joe", "age" : 2 } { "_id" : ObjectId("55067bd7c0e0d2cfc19edb95"), "name" : "joe", "age" : 3 } > db.foo.update({"name" : "joe"}, {$set : {"wish" : "hello world"}}, false, true) WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 2 }) > db.foo.find() { "_id" : ObjectId("55067bd1c0e0d2cfc19edb93"), "name" : "joe", "age" : 1, "wish" : "hello world" } { "_id" : ObjectId("55067bd4c0e0d2cfc19edb94"), "name" : "joe", "age" : 2, "wish" : "hello world" } { "_id" : ObjectId("55067bd7c0e0d2cfc19edb95"), "name" : "joe", "age" : 3, "wish" : "hello world" }