1、前言
前面我们已经介绍过缓存k-v数据库Redis,华为的OpenGauss关系型数据库,今天我们继续介绍一款NoSQL数据库MongoDB。
MongoDB是一款NoSQL(Not Only SQL)数据库。
其拥有很多的优点:
- 扩展性强
- 高性能
- 分布式(HA)
但是其也拥有一些缺点:
- 事务能力弱
- 占用空间大
- 不支持热重启
即便如此,也不妨碍它是一款强大,优秀的文档型数据库的客观事实。
2、快速开始
为了方便,我们这里使用windows版本的MongoDB来做学习演示。
2.1、下载MongoDB_Windows版本
从以下链接下载MongoDB社区程序.zip
:
- 在版本下拉列表中,选择要下载的MongoDB版本。
- 在平台下拉菜单中,选择Windows。
- 在Package下拉列表中,选择zip。
- 点击下载。
以上步骤摘自:MongoDB中文社区
以上步骤很是清楚,但是我的网络无法完成下载,慢的和蜗牛一样。索性我选择了XX下载之家不到5分钟就完成了下载。
下载完成后,我们将MongoDB的zip包解压,然后把解压后的MongoDB文件中的bin
路径添加到环境变量即可。
2.2、启动MongoDB
2.2.1、启动服务端
PS E:\demo> mkdir db
PS E:\demo> mongod --dbpath db
在windows terminal中执行上述命令,出现waiting for connections on port 27017
即证明MongoDB服务端启动成功。
2.2.2、使用客户端连接
PS E:\> mongo
MongoDB shell version v4.0.3
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("c752f19d-cfde-4b8f-85ca-48372e273eed") }
MongoDB server version: 4.0.3
...省略...
>
没错,就是这么简单,MongoDB连接完成。
2.3、基础命令
2.3.1、查看数据库
> db # 查看当前所在的数据库
test
> show dbs # 查看所有数据库
admin 0.000GB
config 0.000GB
local 0.000GB
>
如上,你发现了什么问题没有?
为什么当前所在的数据库是test,但是所有数据库中没有test库。经过验证,当数据库中没有数据的时候就会不展示。
那我们向其中插入一些数据看看效果?
2.3.2、数据插入和查询
> db.test.insert({"name":"phyger","age":18}) # 数据插入
WriteResult({ "nInserted" : 1 })
> db.test.find() # 数据查询
{ "_id" : ObjectId("62bac83d6e89996fc64680e6"), "name" : "phyger", "age" : 18 }
> db.test.find({"name":"phyger"}) # 根据name查询
{ "_id" : ObjectId("62bac83d6e89996fc64680e6"), "name" : "phyger", "age" : 18 }
> db.test.find({"age":18}) # 根据年龄查询
{ "_id" : ObjectId("62bac83d6e89996fc64680e6"), "name" : "phyger", "age" : 18 }
> db.test.find().pretty() # 查询结果美化pretty
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger",
"age" : 18
}
>
save方法,插入:
> db.test.find().pretty()
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{ "_id" : ObjectId("62bad0b8b63fec7fa1ee0c4a"), "name" : "phyger-2" }
> db.test.insert({"_id":ObjectId("62bad0b8b63fec7fa1ee0c4a"),"name":"phyger-3"}) # 使用重复的id插入,会保存
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.test index: _id_ dup key: { : ObjectId('62bad0b8b63fec7fa1ee0c4a') }"
}
})
> db.test.save({"_id":ObjectId("62bad0b8b63fec7fa1ee0c4a"),"name":"phyger-3"}) # 使用重复的id保存,会更新
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
insert和save的区别:
insert在id重复的时候,不允许插入;save在id重复的时候,会更新。
2.3.3、更新数据
更新数据的语法格式:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
justOne:默认为false,会删除匹配到的所有对象;如果指定为true或者1,则只会删除匹配到的第一个对象。
writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
> db.test.update({"name":"phyger"},{$set:{"name":"phyger-0"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find().pretty()
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger-0",
"age" : 18
}
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-1",
"age" : 19,
"home" : "china"
}
>
如上,使用了update{query,update}
语句将查询到的对象进行了修改。
$set:{update}
中的$set:
的作用就是指定字段修改,如果不带$set:
,那么被查询到的对象将会被update
覆盖,即会丢失其他字段。
就像这样:
> db.test.update({"name":"phyger-0"},{"name":"phyger-00"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find().pretty()
{ "_id" : ObjectId("62bac83d6e89996fc64680e6"), "name" : "phyger-00" }
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-1",
"age" : 19,
"home" : "china"
}
>
Oops,phyger-0
对象原有的age
字段被更新丢了。即全量更新了。
当我们查询到有多个数据的时候,怎么更新呢?
> db.test.find().pretty()
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger-0",
"age" : 19
}
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-1",
"age" : 19,
"home" : "china"
}
> db.test.update({"age":19},{$swt:{"name":"phyger-19"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 9,
"errmsg" : "Unknown modifier: $swt"
}
})
> db.test.update({"age":19},{$set:{"name":"phyger-19"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find().pretty()
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger-19",
"age" : 19
}
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-1",
"age" : 19,
"home" : "china"
}
>
如上,可以看到,直接update只能更新第一条数据,怎么修改查询到的多个数据呢?
> db.test.find({"age":19}).pretty()
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger-19",
"age" : 19
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-1",
"age" : 19,
"home" : "china"
}
> db.test.update({"age":19},{$set:{"name":"phyger-19a"}},{multi:true})
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.test.find({"age":19}).pretty()
{
"_id" : ObjectId("62bac83d6e89996fc64680e6"),
"name" : "phyger-19a",
"age" : 19
}
{
"_id" : ObjectId("62bac98bb63fec7fa1ee0c48"),
"name" : "phyger-19a",
"age" : 19,
"home" : "china"
}
>
如上,在更新的时候加上{multi:true}
参数,即可实现批量的修改。
upsert参数
如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
> db.test.update({"age":19},{$set:{"name":"phyger-19a"}},{multi:true,upsert:1})
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("62bad325c008bae5b3cdce60")
})
> db.test.find().pretty()
{
"_id" : ObjectId("62bac979b63fec7fa1ee0c47"),
"name" : "phyger-2",
"age" : 20
}
{ "_id" : ObjectId("62bad0b8b63fec7fa1ee0c4a"), "name" : "phyger-3" }
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
>
如上,使用{multi:true,upsert:1}
参数后,,因为没有匹配到数据,无法更新,所以直接插入了数据,但是我们的update是$set:
指定了参数name
,没有指定age
,但是更新插入后却将原有name为phyger-19a
的整个对象恢复了出来(恢复的是最近的状态,比如一个对象被改了很多次然后删掉了,我们在update不存在的对象的时候指定upsert为1,query的对象匹配到了被删除的对象,则只会恢复被删除对象最后一次的数据)。
证明了:MongoDB的删除只是标记,实际未做删除,在下次更新插入的时候会将数据恢复。
2.3.4、删除数据
删除操作的语法格式:
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
> db.test.remove({"age":19},{justOne:1})
WriteResult({ "nRemoved" : 1 })
> db.test.remove({"age":19},{justOne:1})
WriteResult({ "nRemoved" : 1 })
> db.test.remove({"age":19},{justOne:1})
WriteResult({ "nRemoved" : 0 })
如上,携带{justOne:1}
参数,一次只删除一个,如果不带,则会一次全部删除匹配到的结果。
2.4、逻辑语句
2.4.1、AND
> db.test.find({"name":"phyger-2","age":18}).pretty()
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger-2",
"age" : 18
}
>
以上,在query条件中多个,
隔开的条件之间就是ADN
关系。
2.4.2、OR
> db.test.find({$or:[{"name":"phyger-2"},{"age":19}]}).pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{ "_id" : ObjectId("62bad4ddc008bae5b3cdce85"), "name" : "phyger-2" }
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger-2",
"age" : 18
}
{
"_id" : ObjectId("62bad64cc008bae5b3cdce99"),
"name" : "phyger-2",
"age" : 19
}
>
如上,语句{$or:[{"name":"phyger-2"},{"age":19}]}
中的{"name":"phyger-2"}
和{"age":19}
之间的关系就是OR
。
2.5、条件语句
当我们想要根据某个字段的条件来进行查询的时候需要使用到MongoDB的条件语句。
- (>) 大于 :
$gt
- (<) 小于 :
$lt
- (>=) 大于等于 :
$gte
- (<= ) 小于等于 :
$lte
> db.test.find({age:{$gt:18}}).pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad64cc008bae5b3cdce99"),
"name" : "phyger-2",
"age" : 19
}
>
> db.test.find({age:{$gte:18}}).pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger-2",
"age" : 18
}
{
"_id" : ObjectId("62bad64cc008bae5b3cdce99"),
"name" : "phyger-2",
"age" : 19
}
>
以上为大于和大于等于,小于和小于等于类似。
3、高级功能
3.1、$type
用于获取指定字段的指定类型数据库。
比如我想获取,name字段全部为string的数据。
> db.test.find().pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
{
"_id" : ObjectId("62bad64cc008bae5b3cdce99"),
"name" : 321222,
"age" : 19
}
> db.test.find({"name": {$type:"string"}}).pretty() # 指定类型为:string
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
> db.test.find({"name": {$type:2}}).pretty() # 指定类型为:2 相当于string
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
>
3.2、Limit和Skip
limit用于限制查询到的数据数量。
> db.test.find({"name": {$type:2}}).pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
> db.test.find({"name": {$type:2}}).pretty().limit(1)
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
>
skip一般用于和limit结合,用于跳过指定数量的数据。
> db.test.find({"name": {$type:2}}).pretty().limit(2)
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
> db.test.find({"name": {$type:2}}).pretty().limit(2).skip(1)
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
>
如上,limit出来的两条数据,使用skip(1)会跳过第一条。
3.3、排序
> db.test.find({"name": {$type:2}}).pretty()
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
>
> db.test.find({"name": {$type:2}}).pretty().sort({"age":1})
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
> db.test.find({"name": {$type:2}}).pretty().sort({"age":-1})
{
"_id" : ObjectId("62bad325c008bae5b3cdce60"),
"age" : 19,
"name" : "phyger-19a"
}
{
"_id" : ObjectId("62bad63fc008bae5b3cdce94"),
"name" : "phyger",
"age" : 18
}
>
如上,我们使用sort({"age":1}
就可以根据age进行升序排序,使用sort({"age":-1}
就可以根据age进行降序排序。
更多功能请移步官方文档中文版。
评论区