1.简介
1.1 使用场景
- 文章评论数据分析:
- 数据量大;
- 写入操作频繁;
- 价值较低;
1.2 MongoDB特点
- 1.高性能、开源、模式自由(schema free)的文档型数据库;
- 2.数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘;
- 3.虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能;
- 4.支持二进制数据及大型对象;
1.3 MongoDB体系结构
MongoDB 的逻辑结构是一种层次结构。主要由:文档(document)、集合(collection)、数据库(database)这三部分组成的。逻辑结构是面向用户的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构;
(1)文档(document)
- 相当于关系数据库中的一行记录;
- 多个键及其关联的值;
特点:
- 不能有重复的键、区分大小写
- 不能包含空字符
(2)集合(collection)
- 相当于关系数据库的表;
- 一组文档
命名:
- 不能是空字符串;
- 不能含有空字符;
- 不能以”system.”开头;
- 不能含有保留字$;
(3)数据库
1.多个集合组成;
2.保留数据库:
- admin:用户添加权限
- local:数据永远不会被复制,用来存储本地单台服务器的任意集合
- config:保存分片的相关信息
(4)层次结构
1.4 数据类型
(1)基本数据类型:
- null:用于创建空值或表示不存在的字段
- boolean:布尔值。用于存储布尔值(真/假)
- string:字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的
- integer:整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位
- double:双精度浮点值。用于存储浮点值
- Binary Data:二进制数据。用于存储二进制数据
- Regular expression:正则表达式类型。用于存储正则表达式
- Code:代码类型。用于在文档中存储 JavaScript 代码
(2)数字
- 3种数据类型
- 32位整数
- 64位整数
- 64位浮点数
(3)日期
- 表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期;
date:日期时间,用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息;
timestamp:
- BSON 有一个特殊的时间戳类型,与普通的日期类型不相关。时间戳值是一个 64 位的值,前32位是一个 time_t 值【与Unix新纪元(1970年1月1日)相差的秒数】,后32位是在某秒中操作的一个递增的序数;
- 在单个 mongod 实例中,时间戳值通常是唯一的;
(4)内嵌文档
- 把整个文档作为另一个文档中键中的一个值;
(5)数组
- 用于将数组或列表或多个值存储为一个键;
(6)ObjectId
ObjectId 类似唯一主键,可以很快地去生成和排序,包含 12 bytes,含义是:
- 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时;
- 接下来的 3 个字节是机器标识码;
- 紧接的两个字节由进程 id 组成 PID;
- 最后三个字节是随机数;
MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象
2.基本使用
2.1 常用命令
(1)统计条数
- 统计集合记录数:db.comment.count()
- 统计userid为1013的记录条数:db.comment.count({userid:"1013"})
(2)模糊查询
格式:/模糊查询字符串/
- 查询评论内容包含“流量”的所有文档:db.comment.find({content:/流量/})
- 查询评论内容中以“加班”开头的:db.comment.find({content:/^加班/})
(3)包含与不包含
- 查询评论集合中userid字段包含1013和1014的文档:db.comment.find({userid:{$in:["1013","1014"]}})
- 查询评论集合中userid字段不包含1013和1014的文档:db.comment.find({userid:{$nin:["1013","1014"]}})
(4)条件连接
格式:$and:[ {条件},{条件},{条件} ]
- 查询评论集合中thumbup大于等于1000 并且小于2000的文档:db.comment.find({$and:[ {thumbup:{$gte:1000}} ,{thumbup:{$lt:2000} }]})
- 如果两个以上条件之间是或者的关系,我们使用操作符进行关联,与前面and的使用方式相同:$or:[ {条件},{条件},{条件} ]
- 查询评论集合中userid为1013,或者点赞数小于2000的文档记录:db.comment.find({$or:[ {userid:"1013"} ,{thumbup:{$lt:2000} }]})
(5)列值增长
- 对某列值在原有值的基础上进行增加或减少,可以使用$inc运算符:db.comment.update({_id:"2"},{$inc:{thumbup:1}})
2.2 选择和创建数据库
use 数据库名称:
- 如果数据库存在则选择该数据库,如果数据库不存在则自动创建:use commentdb
- 查看有权限查看的所有数据库:show dbs
- 查看当前正在使用的数据库:db
- 数据库删除:db.dropDatabase():主要用来删除已经持久化的数据库
2.3 集合操作
显式创建:db.createCollection(name)
- 查看集合:show collections 需要先选择数据库之后,才能查看该数据库的集合
- 创建集合:db.createCollection(name, options)
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项
- capped 布尔(可选)
如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档;
当该值为 true 时,必须指定 size 参数;
- autoIndexId 布尔
3.2 之后不再支持该参数。(可选)如为 true,自动在 _id 字段创建索引。默认为 false;
- size 数值(可选)
为固定集合指定一个最大值,即字节数
如果 capped 为 true,也需要指定该字段。
- max 数值(可选)
指定固定集合中包含文档的最大数量
db.createCollection("runoob")
- 隐式创建
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合;
- 集合删除
db.collection.drop()或db.集合.drop();
例:db.mycollection.drop():返回true或false;
2.4 文档基本CRUD
1.文档的插入
(1)单个文档插入
- 使用insert() 或 save() 方法向集合中插入文档
- 语法
- document:要插入到集合中的文档或文档数组(json格式)
- Ordered:可选。如果为真,则按顺序插入数组中的文档,如果其中一个文档出现错误,MongoDB将返回而不处理数组中的其余文档。如果为假,则执行无序插入,如果其中一个文档出现错误,则继续处理数组中的主文档。在版本2.6+中默认为true
- 示例
db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明媚",
"userid":"1001",
"nickname":"Rose",
"createdatetime":new Date(),"likenum":NumberInt(10),
"state":null})
- 1)comment集合如果不存在,则会隐式创建
- 2)mongo中的数字,默认情况下是double类型,如果要存整型,必须使用函数NumberInt(整型数字),否则取出来就有问题了
- 3)插入当前日期使用 new Date()
- 4)插入的数据没有指定 _id ,会自动生成主键值
- 5)如果某字段没值,可以赋值为null,或不写该字段
运行结果
注意事项
- 1. 文档中的键/值对是有序的
- 2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)
- 3. MongoDB区分类型和大小写
- 4. MongoDB的文档不能有重复的键
- 5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符
(2)批量插入
- 示例:批量插入多条文章评论
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" } ]);
- 插入时指定了 _id ,则主键就是该值
- 如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉
- 因为批量插入由于数据较多容易出现失败,因此,可以使用try catch进行异常捕捉处理,测试的时候可以不处理
2.文档的基本查询
语法格式:
示例
- 查询spit集合的所有文档:
db.comment.find()
- 查询userid为1003的数据:
db.comment.find({userid:’1003’})
- 只查一条:
db.comment.findOne({userid:’1003’})
- 投影查询:
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段):
>db.comment.find({userid:"1003"},{userid:1,nickname:1})
返回结果:
{ "_id" : "4", "userid" : "1003", "nickname" : "凯撒" }
{ "_id" : "5", "userid" : "1003", "nickname" : "凯撒" }
- 默认 _id 会显示:
>db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0})
- 查询所有数据,但只显示 _id、userid、nickname:
>db.comment.find({},{userid:1,nickname:1})
3.文档的更新
语法格式:db.collection.update(query, update, options)
示例:
- 覆盖的修改
例如:修改_id为1的记录,点赞量为1001:
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
执行后,我们会发现,这条文档除了likenum字段其它字段都不见了
- 局部修改
使用修改器$set来实现
例如:修改_id为2的记录,浏览量为889:
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
- 批量的修改
例如:更新所有用户为1003 的用户的昵称为凯撒大帝
如果不加后面的参数,则只更新符合条件的第一条记录
- 列值增长的修改
对某列值在原有值的基础上进行增加或减少,可以使用$inc 运算符来实现
例如:对3号数据的点赞数,每次递增1
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
4.删除文档
语法格式:db.集合名称.remove(条件)
示例:
- 将数据全部删除
db.comment.remove({})
- 删除_id=1的记录
db.comment.remove({_id:"1"})
2.5 文档的分页查询
1.统计查询
语法:db.collection.count(query, options)
示例:
- 统计所有记录数:统计comment集合的所有的记录数:
db.comment.count()
- 统计userid为1003的记录条数:
db.comment.count({userid:"1003"})
2.分页列表查询
语法格式:
- 可以使用limit()方法来读取指定数量的数据,使用skip()方法来跳过指定数量的数据
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
- 如果你想返回指定条数的记录,可以在find方法后调用limit来返回结果(默认值20)
db.comment.find().limit(3)
- skip方法同样接受一个数字参数作为跳过的记录条数(默认值0)
db.comment.find().skip(3)
示例:每页2个,第二页开始:跳过前两条数据,接着值显示3和4条数据
3.排序查询
sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列;
语法格式:
示例:
- 对userid降序排列,并对访问量进行升序排列
db.comment.find().sort({userid:-1,likenum:1})
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关;
4.高级查询
(1)正则查询
语法格式:
示例:
- 要查询评论内容包含“开水”的所有文档
db.comment.find({content:/开水/})
- 查询评论的内容中以“专家”开头的
db.comment.find({content:/^专家/})
(2)比较查询
语法格式:
示例:
查询评论点赞数量大于700的记录:
db.comment.find({likenum:{$gt:NumberInt(700)}})
(3)包含查询
语法格式:
包含使用$in操作:
db.comment.find({userid:{$in:["1003","1004"]}})
不包含使用$nin操作符:
db.comment.find({userid:{$nin:["1003","1004"]}})
示例:查询评论的集合中userid字段包含1003或1004的文档
db.comment.find({userid:{$in:["1003","1004"]}})
(4)条件连接查询
需要使用$and操作符将条件进行关联:$and:[ { },{ },{ } ]
示例:
- 查询评论集合中likenum大于等于700 并且小于2000的文档
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},
{likenum:{$lt:NumberInt(2000)}}]})
- 查询评论集合中userid为1003,或者点赞数小于1000的文档记录
db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]})