MongoDB

MongoDB优势:面向开发者的易用,高效数据库。

  1. 简单直观:以自然方式来建模,已直观的方式来与数据库交互。
    • 一目了然的对象模型
  2. 结构灵活:弹性模式从容响应需求的频繁变化。
    • 多形性:同一个集合中可以包含不同字段(类型)的文档对象。
    • 动态性:线上修改数据模式,修改时应用与数据库均无需下线。
    • 数据治理:支持使用JSON Schema来规范数据模式。在保证模式灵活动态的前提下,提供数据治理能力。
  3. 快速开发:做更多的事,写更少的代码。
    • JSON模型之快速特性:
      • 数据库引擎只需要在一个存储区读写。
      • 反范式、无关联的组织极大优化查询速度。
      • 程序API自然,开发快速。

MongoDB优势:原生的高可用和横向拓展能力

  1. Replica Set(分片) -2 to 50 个成员。
  2. 自恢复。主节点出现故障恢复到从节点
  3. 多中心容灾能力。
  4. 滚动服务 - 最小化服务终端。

MongoDB优势:横向拓展能力

  • 需要的时候无缝拓展
  • 应用全透明
  • 多种数据分布策略
  • 轻松支持TB-PB数量级

1.MongoDB技术优势总结

  • Json结构和对象模型接近,开发代码量低。
  • Json 的动态模型意味着更容易响应新的业务需求
  • 复制集提供99.999%高可用
  • 分片架构支持海量数据和无缝扩容

2. 下载安装

https://www.mongodb.com/try/download

3.MongoShell 的使用

mac下启动local

## usr/local下在mongo下的新建终端
mongod --dbpath data --logpath log/mongod.log --logappend

mongo数据库启动,不要关终端

新建一个终端窗口

mongo

mongo是MongoDB的交互式JavaScript Shell界面,它为系统管理员提供了强大的页面,并为开发人员提供了直接测试数据库查询和操作的方法。

## 指定端口
mongo --port=27017## 默认
## 指定 主机地址
mongo --host=127.0.0.1## default
## 查看帮助页面
mongo --help

## tips: 直接启动
mongo

## 运行后:
## 展示所有数据库
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
mock    0.000GB
## 使用mock
> use mock
switched to db mock
## 展示集合
> show collections
mock

3.1 javascript支持

mongo shell是基于javascript语法的,mongodb使用spidermonkey作为其内部的javascript解释器引擎,火狐提供了js内核解释器。

3.2 mongo shell命令

1. 数据库操作

show dbs
## 选择和创建数据库 没有会创建
use test

## 集合相当于关系数据库的表
show tables
## 两个命令作用相同
show collections 

## 删除数据库
db.dropDatabase()
## 查看数据库版本 
db.version()

  1. 数据库命名规则
    • 不能是空字符串 ("")
    • 不能含有空格、. 、$、/ 、\ 、\0
    • 应全部小写
    • 最多64字节
  2. 特殊作用的数据库
    • admin :root数据库,一些特定服务端命令只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
    • local:这个数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
    • config:当mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

创建一个数据库后,查看数据库

> use test
switched to db test
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
mock    0.000GB
## 虽然没有持久化但是内存中存在
> db
test

发现并没有出现新创建的数据库

原因:

mongodb的存储分为两部分

  1. 内存
  2. 磁盘

创建的时候存放在内存中,并没有持久化到磁盘中。当数据库中创建集合的时候才会持久化到磁盘。

2. 集合操作

  1. 显示创建
  2. 隐式创建,直接插入文档,集合不存在就自动创建

集合命名规范:

  • 集合名不能是空字符串“”。
  • 集合名不能\0 字符(空字符)
## 查看集合
show collections
## 创建集合 (显示创建)
> db.createCollection("emp")
{ "ok" : 1 }
## 删除集合
> db.emp.drop()
true

## 查看集合详情
db.集合名.stats()
## 删除集合
db.集合名.drop()

## 显示集合名的帮助信息
db.集合名.help()

创建集合的参数:当集合不存在集合中插入文档会创建集合。

## 语法
> db.createCollection("name",options)
## capped 布尔 可选 
## true 创建固定的集合。达到最大值自动覆盖最早的文档
> db.createCollection("emp",true,128,128)
{ "ok" : 1 }
## size 数值 可选 capped为true要指定
## 给集合指定一个最大值(字节)
> db.createCollection("emp",true,128,128)
{ "ok" : 1 }
## max 可选
## 指固定集合中包含文档的最大数量。
> db.createCollection("emp",true,128,128)
{ "ok" : 1 }

3.文档的增删改查

文档(document)的数据结构和JSON基本一样。

所有储存在集合中的数据都是BSON格式。

插入

(1)单个文档的插入,使用insert或者sava()方法向集合中插入文档

  • 参数
    • document:要插入到集合中的文档,或文档数组。(json格式)
    • writeConcern:可选,类型是document。性能和可靠性的级别。
    • ordered:boolean 可选。
      • 如果为真则按插入顺序插入文档,如果一个文档出现错误,mongodb将返回而不处理数组中的其余文档。
      • 如果为假,则执行无序插入,如果其中一个文档出现错误,则继续处理数组中的主文档。目前5版本默认为true.
db.collection.insert(
	<document or array of documents>,
  {
  	writeConcern:<document>,
  	ordered:<boolean>
  }
)

要想comment的集合(表)中插入一条测试数据:

MongoDB Enterprise > db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
WriteResult({ "nInserted" : 1 })  ## 表示插入成功

(2)批量插入(多个文档插入)

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"}
]);

批量查询可能失败,可以用try catch进行异常捕获处理。(某文档出错mongodb不会回滚,所以try catch是比较好的选择)

try {
  db.comment.insertMany([

  ]);
} catch (e) {
	print (e);
}

基本查询

(1)查询所有

// comment 是集合名称
db.comment.find()
db.comment.find({userid:'1001'})
// 只返回第一条数据
db.comment.findOne({userid:'100001'})

(2)投影查询:查询结果返回部分字段

// 查询userid为"1003"的userid和nickname
MongoDB Enterprise > db.comment.find({userid:"1003"},{userid:1,nickname:1})
{ "_id" : "4", "userid" : "1003", "nickname" : "凯撒" }
{ "_id" : "5", "userid" : "1003", "nickname" : "凯撒" }

// 默认展示 _id ,取消显示
MongoDB Enterprise > db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0})
{ "userid" : "1003", "nickname" : "凯撒" }
{ "userid" : "1003", "nickname" : "凯撒" }

更新

语法:

/**
	query:查询条件,将文档查询出来
	update:更新条件
	options:附带选项
	
*/

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
  }
)

(1)覆盖的修改

//在comment集合中修改_id为1的点赞量为1001
/**
数据
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
提示:默认是浮点数
**/
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
//整条数据被覆盖只剩id

(2) 局部修改,使用修改器

//默认只修改第一条数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒2"}})
//修改所有符合条件的数据  需要加第三个参数
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})

(3)列值增长的修改

// 将likenum增长1
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
删除文档

语法:

db.集合名.remove(条件)
// 将数据完全删除
db.comment.remove({})
// 删除id=1的记录
db.comment.remove({_id:"1"})

4.文档的分页查询

1. 统计查询
db.collection.count(query, options)
// 统计comment集合的所有的记录数
db.comment.count()
// 统计userid为1003的记录条数
db.comment.count({userid:"1003"})

2.分页列表查询

基本语法:

db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
// 查询前3条数据
db.comment.find().limit(3)
// 跳过前三条数据
db.comment.find().skip(3)
// 每页显示两个
//第一页
db.comment.find().skip(0).limit(2)
//第二页
db.comment.find().skip(2).limit(2)
//第三页
db.comment.find().skip(4).limit(2)

3.排序查询

用sort()方法可以通过参数指定排序字段

  • 1为升序排列

  • -1为降序排列

  • 语法:

    db.COLLECTION_NAME.find().sort({KEY:1})
    或
    db.集合名称.find().sort(排序方式)
    
// 对userid降序排列,并对访问量进行升序排列
db.comment.find().sort({userid:-1,likenum:1})
// 投影查询
db.comment.find({},{userid:1}).sort({userid:1})

Skip(),limit(),sort的执行顺序

  1. skip()
  2. limit()
  3. sort()

与命令的编写顺序无关。

5.文档的更多查询

1. 正则的复杂条件查询

语法:

db.collection.find({field:/正则表达式/})
或
db.集合.find({字段:/正则表达式/})

正则表达式是js语法

// 查询包含“开水”的所有文档
db.comment.find({content:/开水/})
// 查询内容以“专家”开头的
db.comment.find({content:/^专家/})
2.比较查询

语法:

  1. < $gt
  2. <= $lte
  3. ‘>’ $gt
  4. ‘>=’ $gte
  5. != $ne
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

例子:

// 查询评论点赞数量大于700的记录
db.comment.find({likenum:{$gt:NumberInt(700)}})
3. 包含查询

使用$in操作符

// 查询评论的集合中userid字段包含1003或1004的文档
db.comment.find({userid:{$in:["1003","1004"]}})
// 查询评论集合中userid字段不包含1003和1004的文档
db.comment.find({userid:{$nin:["1003","1004"]}})
4.条件链接查询

查询同时满足两个以上条件,需要使用$and操作符条件进行关联

$and:[ { },{ },{ } ]
//查询评论集合中likenum大于等于700 并且小于2000的文档:
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})

或者的关系

$or:[ { },{ },{ } ]
//查询评论集合中userid为1003,或者点赞数小于1000的文档记录
db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]})

总结

选择切换数据库: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}]})

练习

db.student.save({_id:1,classid:1,age:18,name:"little1",love:["football","swing","cmpgame"]});

db.student.save({_id:2,classid:2,age:19,name:"little2",love:["play"]});

db.student.save({_id:3,classid:2,age:20,name:"little3"});

db.student.save({_id:4,classid:1,age:21,name:"little4"});

db.student.save({_id:5,classid:1,age:22,name:"little5",opt:"woshisheia"});

db.student.save({_id:6,classid:2,age:23,name:"little4"});

 

1、查询 name为little1的学生,并且只显示 age,name两个字段

db.student.find({name:"little1"},{name:1,age:1})

2、查询 name为little1,age为18的学生,并且只显示 age,name,love三个字段

db.student.find({name:"little1",age:18},{name:1,age:1,love:1})

3、查询从第二条文档开始的3条文档

db.student.find().skip(1).limit(3);

4、按age升序排序

*db.student.find().sort({age:1});* 

5、查询 name为little3或age为19的学生

db.student.find({‘$or‘:[{name:‘little3‘},{age:19}]}) 

6、查询出19<age<=21的数据

db.student.find({age:{$gt:19,$lte:21}});

7、查询出存在opt字段的数据

db.student.find({opt:{$exists:true}});

8、查询出name不为little2的数据

db.student.find({name:{$ne:"little2"}});

9、查询出age为161819的数据

db.student.find({age:{$in:[16,18,19]}});  

10、查询出age不为161819的数据

db.student.find({age:{$nin:[16,18,19]}});

11、查询classid为2并且age为20  或者name为little2的学生

db.student.find({‘$or‘:[{classid:2,age:2},{name:"little2"}]}) 

12、查询age小于等于20或者大于22的文档

db.student.find({‘$or‘:[{age:{$lte:20}},{age:{$gt:22}}]}) 

向数据库users中插入如下的文档

var user1 = {

  name: "xiaobu",

  comments: [{userId: "001", content: "评论1"},{ userId: "002",content: "评论2"},{userId: "003",content: "评论3"}],

  favorites : {books: [  "西游记", "红楼梦", "三国演义", "水浒传"] },

  age: 26.0

};

 

var user2 = {

  name:"juyi",

  comments:[{userId:"101",content: "评论1"},{userId: "102",content: "评论2"}],

  favorites:{ movies:["肖生克的救赎", "阿甘正传", "头号玩家"]},

  age: 25.0

};

var user3 = {

  name: "tom",

  favorites: {movies: [ "头号玩家", "肖生克的救赎", "阿甘正传" ]},

  age: 18.0

 };

var user4 = {

  name: "tom",

  favorites: {movies: [ "头号玩家",  "肖生克的救赎", "阿甘正传" ]},

  age: 18.0

};

更新操作:

1、为name为 juyi的文档增加country字段,值为china

db.users.update({name:"juyi"},{$set:{country:"china"}})

2、为所有文档的favorites字段中 增加一个字段fruit 值为一个数组[apple,banana]

db.homework1.update({},{$set:{fruit:[“apple","banana"]}},{multi:true})

3、更新age小于18或者age大于24的文档,删除文档中movies数组中的最后一个元素

db.homework1.update({$or:[{age:{$lt:18}},{age:{$gte:24}}]},{$pop:{"favorites.movies":-1}})

4、向name为tom文档的fruit的数组中增加两个元素 grapes、watermelon,然后取其数组的后三个元素(只更新查询到第一条文档)

db.homework1.update({name:”tom"},{$push:{fruit:{$each:["grapes","watermelon"],$slice:-3}}})

5、向存在favorites.books数组字段的文档中,在books数组的第二个元素位置添加元素“MongoDB”,“NoSQL”,并且数组元素按照升序排序

db.homework1.update({“favorites.books":{KaTeX parse error: Expected 'EOF', got '}' at position 12: exists:true}̲},{push:{“favorites.books”:{ e a c h : [ " M o n g o d b " , " N o S q l " ] , each:["Mongodb","NoSql"], each:["Mongodb","NoSql"],position:1,$sort:1}}})

查询:

1、查询comments字段不存在的文档

db.homework1.find({comments:{$exists:false}})

2、查询favorites.movies中,有”头号玩家”和”肖生克的救赎”的数据

db.homework1.find({“favorites.movies":{$all:["头号玩家","肖生克的救赎"]}})

3、查询name存在”bu”字符,并且age小于20或者大于24(要求使用regex操作符)

db.homework1.find({$and:[{name:{$regex:/bu/}},{$or:[{age:{$lt:20}},{age:{$gt:24}}]}]})

4、查询userId=”101”并且其评语为”评论1”的文档(注意userId的数据类型)

db.homework1.find({comments:{$elemMatch:{userId:”101",content:"评论1"}}})

5、查询name=”xiaobu”的文档,并且只显示comments前三条数据

db.homework1.find({name:”xiaobu"},{comments:{$slice:[0,3]}})

6、查询所有name=”xiaobu”,且comments只显示第2条的评论,且只显示comments和id字段,其它信息不显示

db.homework1.find({name:”xiaobu"},{comments:{$slice:[1,1]},_id:1})

7、查询被userId=101或者userId=001评论过的数据

db.homework1.find({"comments.userId":{$in:["101","001"]}})