MongoDB 学习之基本操作
这章我们学习数据操作。
Inserting and Saving Documents
上一章我们已经简单介绍了数据插入,如:
db.foo.insert({"bar":"baz"})
那么,假如你碰到需要插入多条 documents 的时候情况怎么办呢?
只要传入数组就行
db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])
插入验证
MongoDB 在插入数据的时候仅做最少的检查。它检查 document 的基础结构并且 _id 不存在时帮你自动添加。
一种数据结构的检查是所有的 document 都必须小于 16M。
Removing Documents
假如我们数据库中有数据,让我们来删除它:
db.foo.remove()
这是我们会删除 foo 中所有的 documents。实际上它并不会删除 foo 这个 collection。
remove 这个方法支持一个查询 document 的参数,当我们传入这个参数时,只有匹配这个查询参数的 document 才会被删除。
当我们执行 remove 时,它就永久被删除,没有任何方法可以恢复。
Remove Speed
删除文档是一个相当快的操作,但是你想要清除整个 collection,最快的是 drop 方法
Updating Documents
当 document 存储在数据库中时,我们可以使用 update 方法更改它。
update 有两个参数,一个用来定位我们需要更新的 document,另一个是我们需要修改的 document。
更新 document 是原子性的:假如有两个 update 动作发生在同一时间,哪个操作先到达服务器端将会先接收,第二个操作会后接收。
Using Modifiers
通常我们只要更新 document 中的某些部分。你可以使用 update modifiers 来更新特殊字段。 Update Modifiers 是用于指定复杂更新操作的特殊 key 值,例如 altering,adding,或者 removing keys。
假设我们要分析网站访问量,我们可以使用 update modifiers 来做这个自动增量,例如原来的数据:
{
"_id":ObjectId("..."),
"url": "www.example.com",
"pageviews":1
}
每次访问页面,我们可以根据它的url查找并且使用 $inc modifier 来增加 pageviews 的值
db.foo.update({"url":"www.example.com"},{"$inc":{"pageviews":1}})
Getting started with the "$set" modifier
$set
可以设置字段值。假如字段不存在,它会被创建。
举个例子,假设你有个用户简况存在数据库中:
{
"_id":ObjectId("4XXXX"),
"name":"joe",
"age":30,
"sex":"male",
"location":"Wisconsin"
}
这是最基本的信息,假如用户想要存储他最喜欢的书,你可以使用 $set:
db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":"War and Peace"}})
$set 甚至可以更改它的类型,假如用户实际上喜欢多本书,我们可以将 favorite book 这个值存入数组:
db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":["War and Peace","Cat's Cradle"]}})
当用户发现他实际上不喜欢阅读时,他也可以使用 $unset 来移除 key
db.users.update({"_id":ObjectId("4XXXX")},{"$unset":{"favorite book":1}})
Array modifers
$push
可以添加数据到类型为 Array 的字段。假如字段不存在,它会被创建。
例如原有数据:
{
"_id":ObjectId("4XXX"),
"title":"A blog post"
}
添加 cmments 的数组字段:
db.blog.update({"title":"A blog post"},{"$push":{"comments":{"name":"xc","content":"nice post"}}})
再来看下数据,这时变为:
{
"_id":ObjectId("4XXX"),
"title":"A blog post",
"comments":[
{
"name":"xc",
"content":"nice post"
}
]
}
这只是简单的对数组进行 push,可是我们要插入多条数组数据怎么做呢?答案是使用 $eac:
db.blog.update({"title":"A blog post"},{"$push":{"comments":{"$each":[{"name":"xc","content":"nice post"},{"name":"cc","content":"test"}]}}})
Using arrays as sets
如果你只想添加数组中没有的元素,可以使用 $ne:
db.papers.update({"authors":{"$ne":"Richie"}},{"$push":{"authors":"Richie"}})
你也可以通过 $addToSet 完成这个工作:
db.blog.update({"title":"A blog post"},{"$addToSet":{"comments":{"name":"xc","content":"nice post"}}})
Removing elements
移除数组最后一个元素,用
{"$pop":{"key":1}}
移除数组第一个元素,用
{"$pop":{"key":-1}}
根据条件匹配删除数据,用 $pull,例:
db.list.update({},{"$pull":{"todo":"laundry"}})
Positional array modifications
当我们在一个数组中有多个值并且想要修改它们时,数组操作变的很机智。
我们可以基于数组的索引来选取它们。
{
"_id" : ObjectId("55ef9fdcb01a89febec546be"),
"comments":
[
{"comment":"x","author":"John","votes":0},
{"comment":"y","author":"Claire","votes":3},
{"comment":"z","author":"Alice","votes":-1}
]
}
我们想要使第一条 comments 的 votes 加 1,我们可以这样做(comments.0.votes 代表第一条 comments 的 votes 字):
db.foo.update({},{"$inc":{"comments.0.votes":1}})
在许多情况下,我们不知数组的索引来查找并修改数组。结果 MongoDB 出现了一个定位操作,$ 可以指出数组中匹配的元素并进行跟新。
例如,我们有个用户交 John,并且我们想把它的名字变为 Jim,我们就可以这样写:
db.foo.update({"comments.author":"John"},{"$set":{"comments.$.author":"Jim"}})
Upserts
upsert 是更新的一种特殊类型(其实就是 update 的第三个参数)。假如我们通过更新匹配条件没有找到 document,它会结合条件创建并更新 document。假如找到了,它则正常更新。
举个例子:
//check if we have an entry for this page
blog = db.analytics.findOne({url:"/blog"})
//if we do,add one to the number of views and save
if(blog){
blog.pageviews++;
db.analytics.save(blog);
}
//otherwise,create a new document for this page
else{
db.analytics.save({url:"/blog",pageviews:1})
}
我们用 upsert 改下
db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)
upsert 如果不加或者是 false。如果查不到匹配的 document 时则什么都不会发生。
在 upsert 的基础上,有些时间我们只需要在创建 document 的时候对某个字段赋值,并不想更新它的值,可以用 $setOnInsert:
db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
我们可以看到数据为
{ "_id" : ObjectId("55efc4a3de70c83a19f2d9b9"), "createdAt" : ISODate("2015-09-09T05:34:07.791Z") }
这时候继续在打命令
db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
我们发现 createAt 字段的值还是没有变化。
Updating Multiple Documents
update 方法默认的只会根据匹配条件更新第一条数据。假如有更多的匹配出的 document,则不会更新。
如果需要全部更新,你只要指定第四个参数为 true 就可以了。
例如我们想为所有生日在某一天的用户添加一个 gift 字段,可以这样写:
db.users.update({"birthday":"10/13/1978"},{"$set":{"gift":"Happy Birthday!"}},false,true);
Returning Updated Documents
我们执行 update 操作时,不会返回更新的数据,这时,你需要 findAndModify 命令。并且它是原子性的。
假设我们有些订单需要跑,数据结构为
{
"id":ObjectId(),
"status":state,
"priority":N
}
status 可以是 READY,RUNNING 或者 DONE。我们有个任务,需要根据 READY 的优先级顺序,跑 process 方法,并且更新其状态为 DONE。
我们需要根据排序优先级查找出 document 准备跑 porcess 方法,状态标记为 RUNNING,当我们跑完方法时,更新状态为 DONE,看上去像是这样:
var cursor = db.processes.find({"status":"READY"});
ps = cursor.sort({"priority":-1}).limit(1).next();
db.processes.update({"_id":ps._id},{"$set":{"status":"RUNNING"}});
do_something(ps);
db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});
这种算法并不好。假设我们有两个线程在跑。假如线程一和线程二同时检索到同一条数据,那么将跑同样的 process。
我们可以做状态检查来避免这种情况,但是这将会变的复杂:
var cursor = db.processes.find({"status":"READY"});
cursor.sort({"priority":-1}).limit(1);
while((ps=cursor.next())!=null){
ps.update({"_id":ps._id,"status":"READY"},{"$set":{"status":"RUNNING"}});
var lastOp = db.runCommand({getlasterror:1});
if(lastOp.n == 1){
do_something(ps);
db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});
break;
}
cursor = db.process.find({"status":"READY"});
cursor.sort({"priority":-1}).limit(1);
}
另外,一个线程可能会做完所有的工作,而另外一个线程一直处于等待失败的状态中。
类似这种情况,findAndModify 完美解决。findAndModify 能够在一个操作中同时返回并更新数据。
ps = db.runCommand(
{
"findAndModify":"processes",
"query":{"status":"READY"},
"sort":{"priority":-1},
"update":{"$set":{"status":"RUNNING"}}
}
)
返回值:
{
"ok":1,
"value":{
"_id":ObjectId("4XXXX"),
"priority":1,
"status":"READY"
}
}
这里返回的状态是 READY,为更新前的状态数据。假如你这时查找数据库,会发现状态已经更新了。
findAndModify 除了有 udpate 这个key,也有 remove 这个 key。 想必这个不用解释了吧。
ps = db.runCommand(
{
"findAndModify":"processes",
"query":{"status":"READY"},
"sort":{"priority":-1},
"remove":true}
}
)
这章介绍了 MongoDB 的创建、删除、修改操作,下章将介绍查询操作。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: MongoDB 学习之查询操作
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论