MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
在高负载的情况下,添加更多的节点,可以保证服务器性能。
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
主要特点
- MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
- 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName=“Sameer”,Address=“8 Gandhi Road”)来实现更快的排序。
- 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
- 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
- Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
- Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
- Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
- Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
- MongoDB安装简单。
windows安装
官网下载安装文件或者压缩包即可
安装有两种方式:
方式一:
在安装路径的bin同级目录下新建两级文件夹:data/db
然后进入bin目录打开命令窗口,输入命令
mongod --dbpath ..\data\db
方式二:
首先在bin同级目录下新建config文件夹中新建mongod.conf配置文件,编辑
storage:
dbPath: D:\environment\MongoDB\data\db
然后到bin目录下打开命令窗口,输入以下命令行启动
mongod -f ..\config\mongod.conf
完成启动,发现mongodb的默认端口号是27017
开启了mongodb服务之后,该怎么连接呢,连接也有两种方式
方式一:
打开bin目录下的mongo.exe,直接输入以下命令就可以连接(甚至自动连接)
mongo
测试是否连接成功
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
方式二:
可通过可视化工具来连接,需要下载compass,在官网下载解压即用,运行compass.exe文件,然后conect即可
Linux安装
官网下载linux版本的mongodb
将压缩包放到linux系统中的usr/local/mongodb文件夹中,解压
cd /usr/local/mongodb
tar -xvf mongodb-linux-x86_64-4.0.10.tgz
新家几个目录,分别用来存储数据、日志和配置
mkdir -p /usr/local/mongodb/data
mkdir -p /usr/local/mongodb/log
在bin目录下新建并修改配置文件
vim /usr/local/mongodbmongodb-linux-x86_64-4.0.19/bin/mongod.conf
内容如下:
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
destination: file
path: "/usr/local/mongodb/log/mongod.log"
# 当mongoos或mongod实例重新启动时,会将新条目附加到先有日志文件的末尾
logAppend: true
storage:
#mongod实例存储其数据的目录
dbPath: "/usr/local/mongodb/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
net:
#服务实例绑定的IP,默认是localhost
bindIp: localhost,172.16.0.17
port: 27017
启动MongoDB服务
./mongod -f ./mongod.conf
启动成功
测试是否启动成功:
ps -ef|grep mongod
最后通过可视化界面来检测我们的mongodb,这时候不再是localhost了,而是要连接到我们的远程服务器上,需要填写远程服务器的主机名
连接成功
最后看下怎么关闭服务
方式一:
kill -2 进程id
方式二:
mongo --port 27017
//#切换到admin库
use admin
//关闭服务
db.shutdownServer()
数据库的创建和删除
存放文章评论的数据存放到MongoDB中,数据结构参考如下:
数据库:articledb
选择和创建数据库
如果数据库不存在则自动创建
use articledb
查看数据库
查看有权限查看的所有的数据库命令
show dbs
注意:如果是空的db是不会显示出来的,例如刚刚创建的articledb,因为刚创建还没数据,也就是还没持久化到磁盘中,暂存在内存中
查看正在使用的数据库
db
删除数据库
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
提示:主要用来删除已经持久化的数据库
集合
创建集合
集合,类似关系型数据库中的表。 可以显示的创建,也可以隐式的创建
显式创建、查看、删除
> db.createCollection("my")
{ "ok" : 1 }
> show collections # 查看集合
my
> db.my.drop()
true
> show collections
>
集合的命名规范:
- 集合名不能是空字符串""。
- 集合名不能含有
- 集合名不能以"system."开头,这是为系统集合保留的前缀。 字符(空字符),这个字符表示集合名的结尾。
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除 非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
隐式创建
文档操作
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合,通常我们使用隐式创建文档即可
插入文档
文档(document)的数据结构和 JSON 基本一样。 所有存储在集合中的数据都是 BSON 格式
单条插入
跟mysql一样既可以单条插入,也可以多条执行
db.collection.insert(
<document or array of documents>,
{
writeConcern: <document>,
ordered: <boolean>
}
)
使用insert() 或 save() 方法向集合中插入文档,语法如下:
> db.comment.insert({
... "articleid": "100000",
... "content": "今天天气真好,阳光明 媚",
... "userid": "1001",
... "nickname": "Rose",
... "createdatetime": new Date(),
... "likenum": NumberInt(10),
... "state": null
... })
WriteResult({ "nInserted" : 1 }) # 表明插入成功
> show collections
comment
>
参数:
示例:
要想comment的集合中插入一条测试数据(隐式插入)
提示:
- mongo中的数字,默认double类型,如要存整型,必须使用函数NumberInt,否则取出来就有问题了。
- 插入当前日期使用 new Date()
- 插入的数据没有指定 _id ,会自动生成主键值
- 如果某字段没值,可以赋值为null,或不写该字段。
- 文档中的键/值对是有序的。
注意:
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- MongoDB区分类型和大小写。
- MongoDB的文档不能有重复的键。
- 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
- 键不能含有
- .和$有特别的意义,只有在特定环境下才能使用。 (空字符)。这个字符用来表示键的结尾。
文档键命名规范:
- 以下划线"_"开头的键是保留的(不是严格要求的)
批量插入
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{ writeConcern: <document>, ordered: <boolean> }
)
> db.comment.insertMany([{
... "_id": "1",
... "articleid": "100001",
... "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。",
... "userid": "1002",
... "nickname": "相忘于江湖",
... "createdatetime": new Date("2019-08- 05T22:08:15.522Z"),
... "likenum": NumberInt(1000),
... "state": "1"
... },
... {
... "_id": "2",
... "articleid": "100001",
... "content": "我夏天空腹喝凉开水,冬天喝温开水",
... "userid": "1005",
... "nickname": "伊人憔 悴",
... "createdatetime": new Date("2019-08-05T23:58:51.485Z"),
... "likenum": NumberInt(888),
... "state": "1"
... },
... {
... "_id": "3",
... "articleid": "100001",
... "content": "我一直喝凉开水,冬天夏天都喝。",
... "userid": "1004",
... "nickname": "杰克船 长",
... "createdatetime": new Date("2019-08-06T01:05:06.321Z"),
... "likenum": NumberInt(666),
... "state": "1"
... },
... {
... "_id": "4",
... "articleid": "100001",
... "content": "专家说不能空腹吃饭,影响健康。",
... "userid": "1003",
... "nickname": "凯 撒",
... "createdatetime": new Date("2019-08-06T08:18:35.288Z"),
... "likenum": NumberInt(2000),
... "state": "1"
... },
... {
... "_id": "5",
... "articleid": "100001",
... "content": "研究表明,刚烧开的水千万不能喝,因为烫 嘴。",
... "userid": "1003",
... "nickname": "凯撒",
... "createdatetime": new Date("2019-08- 06T11:01:02.521Z"),
... "likenum": NumberInt(3000),
... "state": "1"
... }]);
{
"acknowledged" : true,
"insertedIds" : [
"1",
"2",
"3",
"4",
"5"
]
}
语法:
try {
db.comment.insertMany([{
"_id": "1",
"articleid": "100001",
"content": "我们不应该把清晨浪费在手机上,健康很重要",
"userid": "1002",
"nickname": "相忘于江湖",
"createdatetime": new Date("2019-08- 05T22:08:15.522Z"),
"likenum": NumberInt(1000),
"state": "1"
},
{
"_id": "2",
"articleid": "100001",
"content": "我夏天空腹喝凉开水,冬天喝温开水",
"userid": "1005",
"nickname": "伊人憔 悴",
"createdatetime": new Date("2019-08-05T23:58:51.485Z"),
"likenum": NumberInt(888),
"state": "1"
},
{
"_id": "3",
"articleid": "100001",
"content": "我一直喝凉开水,冬天夏天都喝。",
"userid": "1004",
"nickname": "杰克船 长",
"createdatetime": new Date("2019-08-06T01:05:06.321Z"),
"likenum": NumberInt(666),
"state": "1"
},
{
"_id": "4",
"articleid": "100001",
"content": "专家说不能空腹吃饭,影响健康。",
"userid": "1003",
"nickname": "凯 撒",
"createdatetime": new Date("2019-08-06T08:18:35.288Z"),
"likenum": NumberInt(2000),
"state": "1"
},
{
"_id": "5",
"articleid": "100001",
"content": "研究表明,刚烧开的水千万不能喝,因为烫 嘴。",
"userid": "1003",
"nickname": "凯撒",
"createdatetime": new Date("2019-08- 06T11:01:02.521Z"),
"likenum": NumberInt(3000),
"state": "1"
}]);
} catch (e) {
print (e);
}
示例:
查询文档
提示:
插入时指定了 _id ,则主键就是该值。
如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。
因为批量插入由于数据较多容易出现失败,因此,可以使用try catch进行异常捕捉处理,测试的时候可以不处理。
db.collection.find(<query>, [projection])
查询数据的语法格式如下:
Type
参数:
Description | 查询所有 |
|
query | document | 可选。使用查询运算符指定选择筛选器。若要返回集合中的所有文档,请省略此参数或传递空文档 ({}) |
projection | document | 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段, |
示例:
> db.comment.find({userid:"1003"})
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
如果我们要查询集合的所有文档,我们输入以下命令
查询单条
这里你会发现每条文档会有一个叫_id的字段,这个相当于我们原来关系数据库中表的主键,当你在插入文档记录时没有指定该字段, MongoDB会自动创建,其类型是ObjectID类型。 如果我们在插入文档记录时指定该字段也可以,其类型可以是ObjectID类型,也可以是MongoDB支持的任意类型.
如果我想按一定条件来查询,比如我想查询userid为1003的记录,怎么办?很简单!只 要在fifind()中添加参数即可,参数也是json格式,如 下:
> db.comment.findOne({userid:"1003"})
{
"_id" : "4",
"articleid" : "100001",
"content" : "专家说不能空腹吃饭,影响健康。",
"userid" : "1003",
"nickname" : "凯 撒",
"createdatetime" : ISODate("2019-08-06T08:18:35.288Z"),
"likenum" : 2000,
"state" : "1"
}
>
投影查询
如果你只需要返回符合条件的第一条数据,我们可以使用fifindOne命令来实现,语法和fifind一样。
如:查询用户编号是1003的记录,但只最多返回符合条件的第一条记录:
> db.comment.find({userid:"1003"},{userid:1,nickname:1})
{ "_id" : "4", "userid" : "1003", "nickname" : "凯 撒" }
{ "_id" : "5", "userid" : "1003", "nickname" : "凯撒" }
> db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0})
{ "userid" : "1003", "nickname" : "凯 撒" }
{ "userid" : "1003", "nickname" : "凯撒" }
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)。
如:查询结果只显示 _id、userid、nickname :
db.comment.find({},{userid:1,nickname:1})
如:查询结果只显示 、userid、nickname ,不显示 _id :
统计查询
再例如:查询所有数据,但只显示 _id、userid、nickname :
db.collection.count(query, options)
语法:
Type
参数:
Description |
| 分页查询 |
query | document | 查询选择条件。 |
options | document | 可选。用于修改计数的额外选项。 |
示例:
统计comment集合的所有的记录数
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
> db.comment.find().limit(3)
{ "_id" : ObjectId("5eee1393b9ba7b116ffa7823"), "articleid" : "100000", "content" : "今天天气真好,阳光明 媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2020-06-20T13:48:03.665Z"), "likenum" : 10, "state" : null }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
>
可以使用limit()方法来读取指定数量的数据,使用skip()方法来跳过指定数量的数据
基本语法如下所示:
> db.comment.find().skip(3)
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船 长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
如果你想返回指定条数的记录,可以在fifind方法后调用limit来返回结果(TopN),默认值20,例如
排序查询
skip方法同样接受一个数字参数作为跳过的记录条数。(前N个不要),默认值是0
db.COLLECTION_NAME.find().sort({KEY:1})
> db.comment.find().sort({userid:-1,likenum:1})
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船 长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : ObjectId("5eee1393b9ba7b116ffa7823"), "articleid" : "100000", "content" : "今天天气真好,阳光明 媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2020-06-20T13:48:03.665Z"), "likenum" : 10, "state" : null }
>
sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用 于降序排列
正则查询
例如:
对userid降序排列,并对访问量进行升序排列
正则表达式
提示:
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关。
db.collection.find({field:/正则表达式/})
MongoDB的模糊查询是通过> db.comment.find({content:/空腹/})
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
> db.comment.find({content:/^我们/})
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
>
的方式实现的。格式为:
比较查询
提示:正则表达式是js的语法,直接量的写法。
例如,我要查询评论内容包含“开水”的所有文档,代码如下:
db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value
db.集合名称.find({ "field" : { $lt: value }}) // 小于: field < value
db.集合名称.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ "field" : { $ne: value }}) // 不等于: field != value
> db.comment.find({likenum:{$gt:NumberInt(700)}})
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
>
<, <=, >, >= 这个操作符也是很常用的,格式如下:
包含查询
示例:查询评论点赞数量大于700的记录
> db.comment.find({userid:{$in:["1003","1004"]}})
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船 长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
>
条件连接查询
包含使用$in操作符。 示例:查询评论的集合中userid字段包含1003或1004的文档
$and:[ { },{ },{ } ]
> db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
>
我们如果需要查询同时满足两个以上条件,需要使用$and操作符将条件进行关联。 格式为:
$or:[ { },{ },{ } ]
示例:查询评论集合中likenum大于等于700 并且小于2000的文档:
修改文档
如果两个以上条件之间是或者的关系,我们使用 操作符进行关联,与前面 and的使用方式相同 格式为:
db.collection.update(query, update, options)
或
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2
}
)
覆盖修改
> db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.comment.findOne({_id:"1"})
{ "_id" : "1", "likenum" : 1001 }
>
局部修改
如果我们想修改_id为1的记录,点赞量为1001,输入以下语句:
> db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.comment.findOne({_id:"2"})
{
"_id" : "2",
"articleid" : "100001",
"content" : "我夏天空腹喝凉开水,冬天喝温开水",
"userid" : "1005",
"nickname" : "伊人憔 悴",
"createdatetime" : ISODate("2019-08-05T23:58:51.485Z"),
"likenum" : 889,
"state" : "1"
}
>
执行后,我们会发现,这条文档除了likenum字段其它字段都不见了
批量修改
为了解决这个问题,我们需要使用修改器$set来实现,命令如下:
我们想修改_id为2的记录,浏览量为889,输入以下语句:
> db.comment.update({userid:"1003"},{$set:{nickname:"凯撒2"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.comment.find({userid:"1003"})
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯撒2", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
> db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.comment.find({userid:"1003"})
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯撒大帝", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒大帝", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
>
列值增长的修改
更新所有用户为 1003 的用户的昵称为 凯撒大帝 。
> db.comment.update({_id:"3"},{$inc:{likenum:Number(1)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
提示:如果不加后面的参数,则只更新符合条件的第一条记录
删除文档
如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用 $inc 运算符来实现。
需求:对3号数据的点赞数,每次递增1
db.集合名称.remove(条件)
db.comment.remove({})
删除文档的语法结构:
> db.comment.remove({_id:"1"})
WriteResult({ "nRemoved" : 1 })
>
以下语句可以将数据全部删除,请慎用
常用命令小结
如果删除_id=1的记录,输入以下语句
use articledb
db.comment.insert({bson数据})
选择切换数据库:
db.comment.find()
插入数据:
db.comment.find({条件})
查询所有数据:
db.comment.findOne({条件})
条件查询数据:
db.comment.find({条件}).limit(条数)
查询符合条件的第一条记录:
db.comment.find({条件}).skip(条数)
查询符合条件的前几条记录:
db.comment.update({条件},{修改后的数据})
或
db.comment.update({条件},{$set:{要修改部分的字段:数据})
查询符合条件的跳过的记录:
db.comment.update({条件},{$inc:{自增的字段:步进值}})
修改数据:
db.comment.remove({条件})
修改数据并自增某字段值:
db.comment.count({条件})
删除数据:
db.comment.find({字段名:/正则表达式/})
统计查询:
db.comment.find({字段名:{$gt:值}})
模糊查询:
db.comment.find({字段名:{$in:[值1,值2]}})
或
db.comment.find({字段名:{$nin:[值1,值2]}})
条件比较运算:
db.comment.find({$and:[{条件1},{条件2}]})
或
db.comment.find({$or:[{条件1},{条件2}]})
包含查询:
索引
条件连接查询:
索引的类型
单字段索引
索引支持在MongoDB中高效地执行查询。如果没有索引,MongoDB必须执行全集合扫描,即扫描集合中的每个文档,以选择与查询语句匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。
官网文档:https://docs.mongodb.com/manual/indexes/
MongoDB索引使用B树数据结构(确切的说是B-Tree,MySQL是B+Tree)
复合索引
其他索引
MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引(Single Field Index)。
对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。
地理空间索引
MongoDB还支持多个字段的用户定义索引,即复合索引(Compound Index)。
复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由 { userid: 1, score: -1 } 组成,则索引首先按userid正序排序,然后在每个userid的值内,再在按score倒序排序
文本索引
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)。
哈希索引
为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面 几何的二维球面索引。
索引管理
MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如“the”、“a”、“or”),而将集合中的词作为词干,只存储根词。
查询索引
为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支 持相等匹配,不支持基于范围的查询。
> db.comment.getIndexes()
[
{
"v" : 2, # 索引引擎版本
"key" : {
"_id" : 1 # 索引对应的字段:1代表升序
},
"name" : "_id_", # 索引名称
"ns" : "test.comment"
}
]
>
id返回一个集合中的所有索引的数组
创建索引
结果中显示的是默认 _id 索引。
默认_id索引:
MongoDB在创建集合的过程中,在 _id 字段上创建一个唯一的索引,默认名字为 db.collection.createIndex(keys, options)
,该索引可防止客户端插入两个具有相同值的文 档,您不能在_id字段上删除此索引。
注意:该索引是唯一索引,因此值不能重复,即 _id 值不能重复的。在分片集群中,通常使用 _id 作为片键
db.collection.createIndex(keys, options)
在集合上创建索引。
db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
在集合上创建索引。
db.comment.createIndex({userid:1,nickname:-1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
参数:
options(更多选项)列表:
提示:
注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex() ,之后的版本使用了 db.collection.createIndex() 方法, ensureIndex() 还能用,但只是createIndex() 的别名。
【示例】
(1)单字段索引示例:对 userid 字段建立索引:
索引的移除
(2)复合索引:对 userid 和 nickname 同时建立复合(Compound)索引:
db.collection.dropIndex(index)
db.collection.dropIndexes()
可以移除指定的索引,或移除所有索引
移除指定索引:
索引的使用
移除所有索引:
注意:_id的索引是不会被删除的
执行计划
db.collection.find(query,options).explain(options)
> db.comment.find({userid:"1003"}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.comment",
"indexFilterSet" : false,
"parsedQuery" : {
"userid" : {
"$eq" : "1003"
}
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"userid" : {
"$eq" : "1003"
}
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "ryan",
"port" : 27017,
"version" : "4.0.19",
"gitVersion" : "7e28f4296a04d858a2e3dd84a1e79c9ba59a9568"
},
"ok" : 1
}
分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。
那么,通常,我们想知道,建立的索引是否有效,效果如何,都需要通过执行计划查看
> db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
示例:
查看根据userid查询数据的情况:
涵盖的查询
关键点看: “stage” : “COLLSCAN”, 表示全集合扫描,也就是全部5条数据都要查一遍,再选择符合条件的,这个查询方式就比较低了
我们尝试给userid加上索引,再查一下看看
springboot整合MongoDB
再次查看执行计划,关键点看: “stage” : “IXSCAN” ,基于索引的扫描,然后抓取fetch
这时候会先去索引库中扫描数据,再抓取符合条件的数据,总共才查了2条数据,对比没有索引的,效率肯定是提高了,如果是大数据,就会非常明显
1、创建项目,导入依赖
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存,这些覆盖的查询非常高效
示例:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ryan</groupId>
<artifactId>mongodb-study</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- this is needed or IntelliJ gives junit.jar or junit-platform-launcher:1.3.2 not found errors -->
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
主要结合springboot-mangodb对数据的操作
表结构分析
技术选型
SpringDataMongoDB
实战:
2、配置yml文件
spring:
# 数据源配置
data:
mongodb:
# 主机地址
host: 203.195.160.231
# 数据库
database: test
# 默认端口号:27017
port: 27017
3、编写主启动类
@SpringBootApplication
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}
}
4、编写实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "comment")//指定对应的集合
@CompoundIndex(def = "{'userid':1, 'nickname':-1}")//也可以在类上添加索引
@Accessors(chain = true)//支持链式编程
public class Comment implements Serializable {
//@Id
private String id;//主键,_id,如果属性名时id,可以省略注解@Id
@Field("content")
private String content;//如果属性名称和集合中的字段名字不一样,可以像这样添加注解
private Date publishtime;
@Indexed
private String userid;//添加索引,一般会通过命令行去添加索引
private String nickname;
private LocalDateTime createdatetime;
private Integer likenum;
private Integer replynum;
private String parentid;
private String articleid;
}
5、编写dao层
注意:由于是非关系型数据库,所以实体类属性和mongodb数据库中已经存在的字段不需要不一定要保持一致
//评论的持久层接口
public interface CommentDao extends MongoRepository<Comment, String> {
//继承MongoRepository之后已经继承了很多crud方法,具体可以点进去查看
//如果想要实现分页查询,我们需要另增方法,注意方法名格式是有要求的,findBy+字段名
Page<Comment> findByUserid(String userid, Pageable pageable);
}
6、编写service层
@Service
public class CommentService {
@Autowired
private CommentDao commentDao;
@Autowired
private MongoTemplate mongoTemplate;
//调用dao的方法,都是通过dao继承MongoRepository过来的
//增
public void saveComment(Comment comment){
commentDao.save(comment);
}
//更新
public void updateComment(Comment comment){
commentDao.save(comment);
}
//根据id删除
public void deleteCommentById(String id){
commentDao.deleteById(id);
}
//删除所有
public void deleteAllComments(){
commentDao.deleteAll();
}
//根据id查询
public Comment findById(String id){
return commentDao.findById(id).get();
}
//查询所有
public List<Comment> findAll(){
return commentDao.findAll();
}
//分页查询
public Page<Comment> findByPage(String userid, int pageNo, int pageSize){
return commentDao.findByUserid(userid, PageRequest.of(pageNo,pageSize));
}
//实现点赞+1
public void updateCommentLikenum(String id){
//查询条件
Query query = Query.query(Criteria.where("_id").is(id));//查询的条件,如果是多个条件,可以在后面addCriteria
//更新条件
Update update = new Update();
update.inc("likenum");//自增1
//执行更新
mongoTemplate.updateFirst(query, update, Comment.class);
}
}
@SpringBootTest
public class ArticleApplicationTests {
@Resource
private CommentService commentService;
@Test
void test1(){
Comment comment = new Comment();
comment.setContent("ryan自学java")
.setPublishtime(new Date())
.setUserid("10086")
.setNickname("牛排")
.setCreatedatetime(LocalDateTime.now())
.setLikenum(999)
.setReplynum(40)
.setArticleid("9527");
commentService.saveComment(comment);
}
//其他的crud方法就不一一测试了
//如果想要实现分页查询,可以通过在dao层自定义一个方法查询
//另外如果想通过mongodb实现点赞数+1的话,原生的+1方法效率比较低,这时候我们可以通过MongoTemplate来实现
@Test
void testAddLikenum(){
commentService.updateCommentLikenum("2");
}
}
7、测试
注意:
以上是mongodb的简单入门,先写到这里,后续会再进一步学习mongodb
如果需要获取相关资料的也可以私信我哦,觉得可以帮助到你的话麻烦帮忙点个赞