2021年 mongodb 基础语法

连接MongoDB(bin目录下)

 

./mongo

如果觉得shell里空空的可以输入help,在刷屏的同时大致了解下有哪些方法

help

现在咱们还没有数据库,咱们创建一个,任性起名:template

use template

咱们确认下,数据库有没有创建成功

show dbs

template 0.078GB

如果存在template,就进入,如果没有,在最后保存的时候就会创建template

###insert###

发现已经创建成功,继续走,现在咱们创建一个集合,任性起名:room,再插入点数据

db.room.insert({"desk":1,"bed":1,"window":2})

db.room.find()

{ "_id" : ObjectId("55081e42591555a6c35dd695"), "desk" : 1, "bed" : 1, "window" : 2 }

####知识点####

insert会给文档增加一个id(如果原来没有的话),这个id是文档的唯一标示,然后保存到数据库中

###remove###

 

现在咱们不想要这个room文档了,那么来删除它

db.room.remove({})

注意,在remove() 里传入了空对象{},意为删除全部数据,除了这样删除,我们还可以这样删除

db.room.remove({"desk":1})

####Tips####

db.dropDatabase()

删除数据库

db.collection.drop()

删除集合

###update###

update方法很强大,它有两个参数,一是查询文档,用来找出需要更新的文档,另一个是修改器(modifier)文档,描述对找到的文档做哪些修改。

####亮点#### 更新操作是原子的,若两个更新同时发生,先到达服务器的现执行,接着执行另一个。所以,互相冲突的更新可以火速传递,并不会互相干扰,虽然这是一个拼速度的年代,但是后更新的会取得“胜利”(后发制人!)

因为使用原子的 更新修改器 进行更新操作极为高效,所以,就决定用它了!

 

###$inc###

例如:你需要存储一个网站的访问次数

db.web.insert({"url":"www.example","count":2})

{ "_id" : ObjectId("55082435591555a6c35dd697"), "url" : "www.example", "count" : 2 }

这时,可以用$inc来原子的增加(减少)count的值

db.web.update({"url":"www.example"},{$inc:{"count":1}})

{ "_id" : ObjectId("55082435591555a6c35dd697"), "url" : "www.example", "count" : 3 }

$inc指定是对数字的增减操作,count指定操作对象,1指定步长(正增负减)

###$set & $unset###

用$set指定一个键的值,如果不存在,就创建它。这对更新模式或者增加用户定义很有帮助。

db.user.insert({"name":"qianjiahao"})

此用户现在只有姓名信息,现在需要给他添加email

db.user.update({"name":"qianjiahao"},{"$set":{"email":"example@example.com"}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com" }

比如现在他又要添加他的个人爱好,他可能有很多的爱好,所以爱好应该是一个数组

db.user.update({"name":"qianjiahao"},{"$set":{"hobby":["swimming","running","reading"]}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com", "hobby" : [ "swimming", "running", "reading" ] }

假如他现在又没有爱好了...

db.user.update({"name":"qianjiahao"},{"$unset":{"hobby":1}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com" }

爱好就消失了...

###$push###

数组修改器,既然名字都这样叫了,那么这个修改器就只能对数组进行操作啦。

db.user.update({"name":"qianjiahao"},{"$push":{"hobby":"sleeping"}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com", "hobby" : [ "swimming", "running", "reading", "sleeping" ] }

但是这里有个问题,万一你不确定hobby里面是否有一个值,如“singing”,那么也许下面这个方法更适合

 

###$addToSet & $each###

用$addToSet更新可以避免重复,将它与$each组合起来,可以一次性添加多条(就算后添加的值已存在也没有关系)

db.user.update({"name":"qianjiahao"},{"$addToSet":{"hobby":{"$each":["singing","eating","dancing"]}}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com", "hobby" : [ "swimming", "running", "reading", "sleeping", "singing", "eating", "dancing" ] }

 

###$pop###

如果将数组看做队列,可以用$pop方法删除第一个或者最后一个元素 {$pop:{"key":-1}},{$pop:{"key":1}}

###$pull###

它可以删除所匹配的值,如果[1,1,2,1] 执行pull 1 后,只剩下[2]

###定位修改器### 如果要操作数组中的值,可以用值在数组中的位置当做参数来删除

db.user.update({"name":"qianjiahao"},{"$set":{"hobby.0":"crying"}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com", "hobby" : [ "crying", "running", "reading", "sleeping", "singing", "eating", "dancing" ] }

现在哭成了主要爱好了,这不行,咱得改了,但是如果不事先去查,我们并不知道哭是这个hobby数组的第几个值,MongoDB为我们考虑到了这点,使用$来代替位置。

db.user.update({"hobby":"crying"},{"$set":{"hobby.$":"smiling"}})

{ "_id" : ObjectId("55082691591555a6c35dd698"), "name" : "qianjiahao", "email" : "example@example.com", "hobby" : [ "smiling", "running", "reading", "sleeping", "singing", "eating", "dancing" ] }

####upsert##

upsert是一个选项,它是update的第三个参数,并不是一个方法。它是一种特殊的更新,要是没有文档符合匹配,那么它就会根据条件和更新文档为基础,创建新的文档,如有匹配,则正常更新。咱们之前见到的所有update操作,都是建立在有文档的基础之上的。upsert非常方便,不必预制集合,同一套代码既可以创建又可以更新。

超市需要修改商品的价格,比如将苹果的价格上调0.5元,但是店主不确定自己是否有购进苹果(偶尔会犯糊涂),那么他可以这样

db.supermarket.update({"name":"apple"},{$set:{"price":5.5}},True)

如果MongoDB之前有苹果的记录,那么就会update苹果的价格,如果没有就会创建新的文档

{ "_id" : ObjectId("55083db0720f2a27156f66ed"), "name" : "apple", "price" : 5.5 }

 

###save###

save是一个shell函数,调用它,可以在文档不存在时插入,存在时更新,它只有一个参数:文档。如果文档有 _id 这个 键,那么save会调用upsert,否则会调用insert,非常方便。

###多文档更新###

当一次更新一个文档无法满足我们的脚步时,我们可以选择一次更新多个文档,及在update的第四个参数的位置添上true,及做多文档更新,建议就算不做多文档更新也显式的在第四个参数上置false,这样明确易懂,也可以在默认参数变化时从容应对。

运行getLastError命令可以帮助我们获取反馈信息

db.count.update({"x":3},{$inc:{"x":2}},false,true)

db.runCommand({getLastError:1})

{ "connectionId" : 199, "updatedExisting" : true, "n" : 1, "syncMillis" : 0, "writtenTo" : null, "err" : null, "ok" : 1 }

###安全VS性能### insert、remove、update这些操作都是瞬间的,他们不需要等待数据库响应,可以理解为子弹从枪膛里射出但不关心射没射中!并不是说这些操作是异步的,而是说后面的事你不用担心了,说白了,子弹射出去后怎么飞、怎么击中目标那都是子弹的事了,咱也管不了了。

这个特点是速度快,快通常挺好,但是万一遇到点什么岔子,小到踢断网线,中到停电淹水,大到火山爆发。借用周星驰电影里的一句话:“世事难预料”,可能有些情况还能接受,但是如果是安全性要求很高的应用场景,那就不能接受了。

所以,安全性要求高的解决方案是:执行完操作,立刻返回getLastError,如果出错,一般都会抛出一个可捕获的异常,好及时处理,如果成功了,会给出额外的信息作为反馈。

但这样一来,性能方面肯定就不如之前了,因为用户会额外等待数据库做出响应。

至于具体是对安全性要求高,还是对性能要求高,要视具体情况而定。

 

终于到了CRUD的大哥:find方法了。

####第一个参数#### MongoDB使用find来进行查询,查询呢,就是返回一个集合中文档的子集,子集合的范围从0个文档到整个集合。

find的第一个参数决定了要返回那些文档,其形式也就一个文档,说明了要执行的查询细节。

通常呢,大家看到的find查询基本都长这个样子

db.user.find({})

db.user.find({"name":"qianjiahao"})

db.user.find({"name":"qianjiahao","email":"example@example.com"})

####第二个参数#### 但是,有的时候,我们并不希望将文档中的所有键/值对都返回,这时,我们可以在find方法的第二个参数上指明我们希望返回的信息。

db.user.find({},{"name":1,"email":1})

上面的语句意思是:我们只想得到name和email,其他的不关心。像这样指明返回信息的做法肯定是有好处的,它可以帮助我们节省传输的数据量,又能节省客户端解码文档的时间和内存消耗。 比如,现在有这两条数据

{ "_id" : ObjectId("5509087e08fa61313b5a8230"), "name" : "william", "email" : "example@example.com" }

{ "_id" : ObjectId("5509088b08fa61313b5a8231"), "name" : "jack", "email" : "example@example1.com" }

我们只想得到name,连 _id 都不想要,那么可以这样

db.user.find({},{"name":1,"_id":0})

{ "name" : "william" }

{ "name" : "jack" }

####注意#### 数据库关心的查询文档的值,必须是常量(在你自己的代码里可以是正常的变量),换句话说,不可以引用文档中其他键的值!

###查询条件###

####$lt $lte $gt $gte####

以上四个分别表示为:< 、 <= 、 > 、 >= 。 通常的做法是将他们组合起来,以便查找一个范围。 比如,查询年龄在18到25岁(含)的人,我们可以这样

db.user.find({"age":{"$gte":18,"$lte":25}})

这样的范围查询对查询日期特别有用 比如,查询在2015年1月1日后注册的用户

start = new Date("01/01/2015") db.user.find({"register":{"$gte":start}})

####注意#### 不要去匹配精确的日期,而是用范围来对日期进行查询

####$ne#### $ne表示不相等

db.user.find({"name":{"$ne":"william"}})

 

###OR查询### ####$in####

$in可以查询一个键的多个值 举例,每个人有爱好,假定为一个,数据太多,咱们用第二个参数来过滤一下

db.user.find({},{"_id":0})

{ "hobby" : "swimming", "gender" : "female" }

{ "hobby" : "dancing", "gender" : "male" }

{ "hobby" : "singing", "gender" : "male" }

我们想查询喜欢dancing和swimming和的人,可以得到如下结果

db.user.find({"hobby":{"$in":["dancing","swimming"]}},{"_id":0})

{ "hobby" : "swimming", "gender" : "female" }

{ "hobby" : "dancing", "gender" : "male" } 若只查询会跳舞的人

db.user.find({"hobby":{"$in":["dancing"]}},{"_id":0})

{ "hobby" : "dancing", "gender" : "male" }

既然$in,那么与之相对的就$nin,可以查询到不包括指明信息的文档

 

###$or####

我们再添加一个游泳的人,并用$in查询游泳的人

db.user.find({"hobby":{"$in":["swimming"]}},{"_id":0})

{ "hobby" : "swimming", "gender" : "female" }

{ "hobby" : "swimming", "gender" : "male" }

$in 是对单个键进行的查询,用$or查询可以匹配多个键

db.user.find({"$or":[{"hobby":"swimming"},{"gender":"female"}]},{"_id":0})

{ "hobby" : "swimming", "gender" : "female" }

{ "hobby" : "swimming", "gender" : "male" } 现在,我们把查询条件的female改成male

db.user.find({"$or":[{"hobby":"swimming"},{"gender":"male"}]},{"_id":0})

{ "hobby" : "swimming", "gender" : "female" }

{ "hobby" : "dancing", "gender" : "male" }

{ "hobby" : "singing", "gender" : "male" }

{ "hobby" : "singing", "gender" : "male" }

{ "hobby" : "dancing", "gender" : "male" }

{ "hobby" : "swimming", "gender" : "male" }

现在我们可以得出结论,OR查询($in 和 $or)是尽可能的获取更多的匹配项。 OR查询其实是取并集,满足其中一条及以上,即可被查询到。

 

####$not####

not 是元条件句,可以用于任何条件之上,意为取反

####注意#### 一个键不能对应多个更新修改器 但是可以对应多个条件查询句 比如,可以这样

db.user.find({"age":{"$gt":18,"$lt":30}})

但是,不可以这样

{"$inc":{"age":1},"$set":{"age":20}}

因为他们修改了两次age

###查询数组###

 

####$all####

利用all来查询所以满足的匹配项,已知数据库有这些数据

db.user.find({},{"_id":0})

{ "fruit" : [ "apple", "banana", "cherry" ] }

{ "fruit" : [ "apple", "banana", "pear" ] }

{ "fruit" : [ "apple", "watermelon", "pear" ] }

需要查询出有apple和cherry的文档

db.user.find({"fruit":{$all:["apple","cherry"]}},{"_id":0})

{ "fruit" : [ "apple", "banana", "cherry" ] }

与之前的OR查询($or $in)相比,$all的查询模式更像是去取交集,即同时满足查询条件才能被查询出来。

 

####$size####

size可以获得指定数组长度的文档

db.user.find({"fruit":{"$size":3}},{"_id":0})

{ "fruit" : [ "apple", "banana", "cherry" ] }

{ "fruit" : [ "apple", "banana", "pear" ] }

{ "fruit" : [ "apple", "watermelon", "pear" ] }

###游标### 数据库使用游标来控制find的执行结果。

客户端对游标的实现通常能够对最终结果进行有效控制。

可以限制结果的数量,略过部分结果,对任意方向任意键的组合对结果进行排序,或者去执行一些功能强大的操作。

我们来看一例

var cursor = db.foo.find().sort({"x":1}).limit(3).skip(5)

上面代码的意思是:对foo集合查询文档,参照x键的值升序排序,仅显示跳过前五个后的前三个文档。

在接触过Jquery后,感觉这样的链式函数调用很眼熟。

其实他们的原理是一样的,因为几乎所有的游标对象的方法都会返回游标本身,

所以才可以使用如此优雅的方式去调用函数。

####注意#### 在调用find的时候,shell并不立即查询数据库,

而是等待正要获得结果的时候才发送查询,这样在执行之前可以给查询附加额外的选项。

####limit、sort和skip####

limit指的是上限 sort里 1 为升序,-1为降序,如果sort里有多个值,那就按照多个键的顺序,依次排序 skip...就是skip(额) 用这三个方法可以通常可以用来制作分页。

####辩证的看待游标#### 在服务器端,游标消耗内存和其他资源。

游标遍历尽了结果以后,或者客户端发来消息要求终止,数据库将会释放这些资源,释放掉的资源可以被数据库换作他用。

这非常有必要,所以尽量保证尽快释放资源(在合理的前提下)

还有一些情况导致游标终止

首先,当游标完成了匹配结果的迭代时,它会清除自身。 另外,当游标在客户端已不在作用域内时,驱动会向服务器发送专门的消息,让其销毁。 最后,即便用户没有迭代完所有的结果,并且游标还在作用域内,10分钟不使用,数据库游标也会自动销毁。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值