万物科技 学,以致用

5. MongoDB教程 -- 更多内容

2017-03-29
Geng

数据库应该有基本的增删改查功能,上一章我们介绍了增和查。这一章,我们重点说下怎么删改。然后再介绍比如过滤,索引等知识。

删除

假设我们通过find()命令,发现我们有如下文档:

> db.people.find()
{ "_id" : ObjectId("58d906c3a45824bc146a0b8d"), "name" : "xiaohong", "height" : 167, "birthday" : ISODate("2000-09-29T16:00:00Z"), "friends" : [ "xiaoming", "xiaowang", "xiaoli" ], "skill" : { "run" : 9, "jump" : 7 } }
{ "_id" : ObjectId("58d906d2a45824bc146a0b8e"), "name" : "xiaoming", "height" : 177, "birthday" : ISODate("2000-09-29T16:00:00Z"), "friends" : [ "xiaoming", "xiaowang", "xiaoli" ], "skill" : { "run" : 9, "jump" : 7 } }
{ "_id" : ObjectId("58d906d7a45824bc146a0b8f"), "name" : "xiaoming", "height" : 177, "birthday" : ISODate("2000-09-29T16:00:00Z"), "friends" : [ "xiaoming", "xiaowang", "xiaoli" ], "skill" : { "run" : 9, "jump" : 0 } }
{ "_id" : ObjectId("58d908f7a45824bc146a0b90"), "name" : "xiaohong", "height" : 177, "birthday" : ISODate("2002-10-29T16:00:00Z"), "friends" : [ "xiaoming", "xiaowang", "xiaoli" ], "skill" : { "run" : 9, "jump" : 7 } }

我们想要删除第一个文档:

> db.people.remove({"height" : 167})
WriteResult({ "nRemoved" : 1 })

再次运行find()命令,可以发现这个文档被删除了。可见,使用集合的remove()命令,可以删除符合条件的文档,其中remove()中的语句,就是查询语句,符合条件的文档就是说符合这个查询语句的所有文档。如果有多个文档都符合条件,那么所有符合条件的文档都会删除。

危险:如果直接db.people.remove({}),将会删除所有文档。

修改

修改属性

假设在录入的时候,我有信息填错了,怎么办呢,我们可以使用update()

> db.people.update({"height": 177},{"$set":{"height": 187}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

这里的语法如下:

> db.集合.update(查询条件,更新数据)

其中使用$set可以说明是更新这个文档的这个属性,如果写成db.people.update({"height": 177},{"height": 187}),那么符合这个条件的文档会被修改为只有{"height": 187},而不是仅仅修改这个属性。

另外需要注意,这句话只能修改第一个符合条件的文档。如果想要修改全部符合条件的文档,需要增加第三个参数{"multi": true}

> db.people.update({"height": 177},{"$set":{"height": 187}}, {"multi": true})

计数

假设我们文档中有一个属性记录个人记录被浏览次数,每次浏览都会加一,我们可以如下操作:

> db.people.update({"name": "xiaohong"},{"$inc":{"count": 1}})

现在没有count这个属性,那么第一次使用会被赋值1。在调用同样语句,看看效果。是不是count变2了。

改增

观察下面代码:

> db.people.update({"name": "xiaoli"},{"$inc":{"count": 1}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })

我们的集合中并没有名字为xiaoli的文档,那么这句就什么都不做了。

但是如果这样写:

> db.people.update({"name": "xiaoli"},{"$inc":{"count": 1}}, {"upsert": true})
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 1,
	"nModified" : 0,
	"_id" : ObjectId("58d91f789ffb01193cc69467")
})

我们会添加一个新的文档。

我们来看下到底发生了什么。 首先,upsert是“updates an existing document or inserts a new one”,简称:“update or insert”。也就是说,如果有,那么更新,方法同没有{"upsert": true}的语句。如果没有,那么添加。

其次,"nMatched" : 0, "nUpserted" : 0, "nModified" 是什么?观察名字,其实就是匹配的数量,改增的数量,修改的数量。

删除属性

我们刚开始都设置了一个生日属性,但是后来发现好像不需要用到,那么,我们怎么可以删除这个属性呢?

> db.people.update({},{"$unset": {"birthday":""}}, {"multi": true})

首先,{}说明我们查询所有文档。其次$unset说明我们要删除某个属性,后面跟属性名,至于属性名的值是什么,没有什么关系,因为我们要把整个属性删除。最后{"multi": true}说明删除所有的,否则只会匹配第一个。

修改属性名

如果我们发现属性名写错了怎么办。我们的技能属性名,应该用复数,但是我在建立的时候用的是单数,如何修改呢:

> db.people.update({},{"$rename": {"skill":"skills"}}, {"multi": true})

可见,使用$rename即可。

修改数组属性

观察我们的数据,会发现朋友属性是一个数组:"friends" : [ "xiaoming", "xiaowang", "xiaoli" ]。如果想要将数组里面某个值做改变呢?我们之前学的方法,都是整体修改属性值,对于数组,可以用下面方法:

db.people.update({"name": "xiaoming"},{"$set": {"friends.1": "change"}} )

可以发现,我们使用了数组索引,但不是不同于一般的friends[1],我们使用点语法:friends.1,可以看到,第二个元素的值会发生变化。

问题又来了,如果我的数组里面有一个值,比如“xiaowang”,想全部改成其他值,比如”haha“,怎么办?我们看下下面代码:

> db.people.update({"friends": "xiaowang"},{"$set": {"friends.$": "111"}}, {"multi": true} )

update()中的第一个大括号是查询条件,第二个大括号中”friends.$”中的“$”是匹配项的占位符,最后的参数我们已经知道了,它使得一次可以改变多个文档。

修改嵌套文档

我们上一章见过这个查询: 查询技能点中的跑的值为9,使用了语法

> db.people.find({"skills.run": 9})
{ "_id" : ObjectId("58d8ed13a45824bc146a0b8b"), "name" : "xiaohong", "height" : 183, "birthday" : ISODate("2000-09-29T16:00:00Z"), "friends" : [ "xiaoming", "xiaowang", "xiaoli" ], "skill" : { "run" : 9, "jump" : 7 } }

那么想一个问题,怎么修改嵌套文档的某个值?是不是很简单,直接用点即可:

> db.people.update({"name": "xiaoming"}, {"$set": {"skill.run": 77}})

投影

观察下面代码:

> db.people.find({"name": "xiaoming"}, {"skills.jump": true})
{ "_id" : ObjectId("58d906d2a45824bc146a0b8e"), "skills" : { "jump" : 7 } }
{ "_id" : ObjectId("58d906d7a45824bc146a0b8f"), "skills" : { "jump" : 0 } }

这段代码中,第一部分是查询条件,第二部分是返回条件。符合查询条件的文档,只会返回符合返回条件的属性(设定为true的那些属性返回,其他属性默认为false)和id。这个其实就是说,只显示我要求显示的。

当然,如果你不想要输出id,也很简单:

> db.people.find({"name": "xiaoming"}, {"skills.jump": true,"_id": false})
{ "skills" : { "jump" : 7 } }
{ "skills" : { "jump" : 0 } }

只要"_id": false即可。

但是:

> db.people.find({"name": "xiaoming"}, {"skills.jump": false})
{ "_id" : ObjectId("58d906d2a45824bc146a0b8e"), "name" : "xiaoming", "height" : 187, "count" : 2, "skills" : { "run" : 9 }, "friends" : [ "xiaoming", "change", "xiaoli" ], "skill" : { "run" : "go" } }
{ "_id" : ObjectId("58d906d7a45824bc146a0b8f"), "name" : "xiaoming", "height" : 187, "skills" : { "run" : 9 }, "friends" : [ "xiaoming", "111", "xiaoli" ] }

注意,这个时候,设定为false的那些属性不返回,其他属性默认为true。这个就是说,不显示不要求显示的。

我们上面看到的find()第二个参数,就是投影

游标

每当我们查询文档的时候,find()方法都会返回一个对象,这个对象是一个游标对象。为什么要用游标呢?可以参考这个回答。简单来说,就是这样做更有效率。其实,我们可以从游标的名字来看,就是读取数值的那个游标,或者是读书的时候眼睛在看的区域,手指的区域。游标做到指哪读哪,不指哪不读哪。

数量

如果查询返回的文档很多,那么每次只会显示20个文档,然后会提示输入it以查看更多:

> db.largeone.find()
.
.
.
Type "it" for more

游标对象可以返回符合条件的文档数目:

> db.largeone.find().count()

因为find()方法返回游标对象,所以我们可以使用游标的count()方法。

排序

排序可以使用sort()方法:

> db.largeone.find().sort({"price": 1})

{"price": 1}说明按照price的升序排序。如果传入-1,那么就是降序排序。

分页

分页就是每次只展示一部分,还必须能够展示下一页和后面的页。我们首先应该确定每页展示多少文档,可以使用limit(数量)。然后要确定展示的这页从哪里开始,也就是前面掠过了多少文档,可以使用skip(数量)

那么,如果第一页,不需要掠过任何文档:

> db.largeone.find().limit(5)

说明显示前5个文档。

如果第三页,那需要掠过 3 * 5 = 15 个文档:

> db.largeone.find().skip(15).limit(5)

数据索引简介

我们前几章使用了内嵌的文档来存储复杂信息,但是考虑存储人员信息的数据库,我们想要添加某人朋友信息,那么其朋友也要有姓名,年龄等信息。但是我们要存储所有人信息,那么我们也会把这个朋友也作为文档存储。这样,这位朋友不仅独立为一个文档存储,也最为内嵌文档嵌入在某人的信息中。同样的信息,多个位置存储,显然不是一个好的方案。鉴于此,我们使用数据索引。

我们来实际操作一下:

original_id = ObjectId()
another_id = ObjectId()

db.places.insert({
    "_id": original_id,
    "name": "Broadway Center",
    "url": "bc.example.net"
})
db.places.insert({     
    "_id": another_id,     
    "name": "Another Center",     
    "url": "ab.example.net" 
})

db.people.insert({
    "name": "Erin",
    "places_id": original_id,
    "url":  "bc.example.net/Erin"
})
db.people.insert({     
    "name": "Mick",     
    "places_id": another_id,   
     "url":  "ab.example.net/Erin" })    

观察id可见,通过索引id就可以建立起索引了。


Comments

你可以请我喝喝茶,聊聊天,鼓励我

Wechat Pay
wechat

Thanks!