RDBMS(Relational Database Management System):
关系型数据库.

MongoDB属于文档数据库

MongoDB的数据模型是面向文档的,所谓文档是一种类似于JSON的结构,简单理解MongoDB这个数据库中存的是各种各样的JSON(BSON)

BSON中的BBinary就是说这其实是一个二进制的JSON文件

  • 数据库(database)数据库是一个仓库,在仓库中可以存放集合。
  • 集合(collection)集合类似于数组,在集合中可以存放文档。
  • 文档(document)文档数据库中的最小单位,我们存储和操作的内容都是文档。

展示数据库

1
2
3
show dbs
#或者
show databases

进入数据库

1
2
3
4
use test

# 查看当前数据库
db

注意:在mongo中,数据库和集合都不需要手动创建.
当我们创建文档时,如果文档所在的集合或数据库不存在会自动创建数据和集合

1
2
# 查看数据库的集合(很想MySQL的show tables)
show collections
SQL术语 MongoDB术语 解释说明
database database 数据库
table collecion 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接MongoDB不支持
primary key primary key 主键,MongoDB自动将_id设置为主键

文档:
{name:“张三”,age:20,hobby:{“看书”,“旅游”,“唱歌”}}

注意:

  1. 文档中的键/值对是有序的
  2. 文档的键必须是字符串

如果将文档类比成数据库中的行,那么集合就可以类比成数据库的表

在mongodb中的集合是无模式的,也就是说集合中存储的文档的结构可以是不同的,比如下面的两个文档可以同时存入到一个集合中:
{“name”:”hyl”}
{“name”:”hyl”,”age”:1}

Type Number String Notes
Double 1 “double” —–
字符串 2 “string” —–
对象 3 “object” —–
数组 4 “array” —–
二进制数据 5 “binData” —–
未定义 6 “undefined” 已过期
ObjectId 7 “objectId” —–
Boolean 8 “bool” —–
日期 9 “date” —–
10 “null” —–
正则表达式 11 “regex” —–
DBPointer 12 “dbPointer” 已过期
JavaScript(代码) 13 “javascript” 此数据类型用于将JavaScript代码存储到文档中
符号 14 “symbol” 已过期
JavaScript(带范围) 15 “javascriptWithScope” —–
32位整数 16 “int” —–
时间戳 17 “timestamp” —–
64位整数 18 “long” —–
Decimal128 19 “decimal” New in version 3.4
Min key -1 “minKey” —–
Max key 127 “maxKey” —–

操作mongoDB数据库

  1. 创建数据库:
    如果数据库不存在则创建数据库,否则切换到指定的数据库

    1
    use 数据库名
  2. 查看所有数据库:

    1
    show dbs

    注意:刚创建的数据库是不能显示的.必须插入数据库才能显示

  3. 查看当前正在使用的数据库

    1
    db

    或:

    1
    db.getName()
  4. 删除数据库:

    1
    db.dropDatabase()
  5. 插入数据

    1
    db.student.insert({name:"hyl",age:18,gender:1,address:"北京"})
  6. 断开连接:

    1
    exit

操作集合

  1. 查看当前数据库下有哪些集合

    1
    show collections
  2. 创建集合

    1
    db.createCollection("集合名")

    或者:
    创建一个空的集合并添加一个文档

    1
    db.集合名.insert(document)
  3. 删除当前数据库中的集合

    1
    db.集合名.drop(document)

操作文档

  1. 插入文件

    • 使用 insert()方法插入文档

      1
      db.集合名.insert(document)

      eg:

      1
      db.student.insert({name:"hyl",age:18,gender:1,address:"北京"})

      如果要插入多条数据:
      注意要添加中括号:

      1
      2
      3
      db.student.insert(
      [{name:"hyl",age:18,gender:1,address:"北京"},
      {name:"dsz",age:15,gender:0,address:"广州"},])
    • 使用save方法插入文档:

      1
      db.集合名.save(文档)

      如果不指定_id字段,save()方法类似于 Insert()方法.如果指定_id字段,则会更新_id字段的数据.

      1
      2
      db.student.save(
      {name:"hyl",age:18,gender:1,address:"北京"})
      1
      2
      3
      db.student.save(
      {_id:ObjectId(5995096201 723fe2a0d8d17"},name:"poi",age:22,gender:7 address:"石家庄",isDelete:e}
      )
  2. 文档更新:

    • update()方法用于更新已经存在的文档

      1
      2
      3
      4
      5
      6
      7
      8
      9
      db.集合名.update(
      query,
      update,
      {
      upset:<boolean>,
      multi:<boolean>,
      writeConcern:<document>
      }
      )

      参数说明:

      1. query:
        updata的查询条件,类似于sql里的update语句内where后面的内容.
      2. update:
        update的对象和一些更新的操作符($set,​$inc)等
      3. upset:
        可选.如果不存在update的记录,是否当成新数据插入,true为插入,False为不插入(默认False)
      4. multi:
        可选.如果查找到多条数据,是否更新全部数据,否则只更新一条数据(默认False)
      5. writeConcern:
        可选,抛出异常的级别.
1
2
3
4
5
// 示例:将李雷的年龄更新为25
db.student.update({name:"lilei"},{$set:{age:25}})

// 将所有名字是李雷的人年龄更新为25
db.student.update({name:"lilei"},{$set:{age:25}},{mutil:true})

删除集合的某个文档:$unset

1
db.student.update({name:"lilei"},{$unset:{age:25}})
  • save()方法通过传入的文档替换已有文档:

    1
    2
    3
    4
    5
    6
    db.集合名.save(
    document,
    {
    writeConcern:<document>
    }
    )
  1. 文档删除

    • 说明:在执行 remove()函数前,先执行find命令来判断执行的条件是否存在是一个良好习惯

      1
      2
      3
      4
      5
      6
      7
      db.集合名.remove(
      query,
      {
      justOne:<boolean>,
      writeConcern:<document>
      }
      )
      1. query:
        可选,删除文档的条件
      2. justOne:
        可选,如果为tureen或1,则只删除一个文档
      3. writeConcern:
        可选,抛出异常的级别.
    • eg:删除名字为tom的学生

      1
      2
      3
      db.student.remove({name:"tom"})

      db.student.remove({name:"tom"},{justOne:"true"})
  2. 文档查询:

    • find()方法:

      1
      2
      3
      4
      // 查询集合下所有的文档
      db.集合名.find()

      db.student.find()
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 查询指定列
      db.集合名.find(
      query,
      {
      <key>:1,
      <key>:1
      }
      )

      // 查看年龄为18的所有人的姓名,性别:
      db.student.find({age:18},{name:1,age:1})

      query:查询条件
      key:要显示的字段,1表示显示

    • pretty()方法以格式化的方式来显示文档

      1
      bd.student.find().pretty()
    • findOne():方法查询匹配结果哦第一条数据

      1
      db.student.findOne({gender:0})
  3. 查询条件操作符:

    • $gt,$gte,$lt,$lte:大于,大于等于,小于,小于等于.

      1
      2
      3
      4
      db.集合名.find(<key>:{$gt:<value>})

      // 查询年龄大于20的学生
      db.student.find({age:{$gt:20}})
      1
      2
      // 查询年龄大于20,小于40的学生
      db.student.find({age:{$gte:20,$lte:40}})
    • 等于使用冒号:

      1
      db.student.findOne({gender:0})
    • 使用id进行查询:

      1
      db.student.find({"_id":ObjectId("59995084b019723fe2a0d8d14")})
    • id查询某个结果集的数据条数

      1
      db.student.find().count()
    • 查询某个字段的值当中是否包含另一个值

      1
      2
      // 查询名字包含yl的学生
      db.student.find({name:/yl/})
    • ObjectId()查询某个字段的值是否以另一个值开头

      1
      2
      // 查询名字以h开头的学生
      db.student.find({name:/^h/})
  4. and,or:

    1
    2
    3
    db.集合名.find(条件1,条件2,条件3....)

    db.student.find({age:{$gt:16},gender:0})
    1
    2
    3
    4
    5
    db.集合名.find({
    $or:[{条件1},{条件2},{条件3}....]
    })

    db.student.find({$or:[{age:{$gt:16}},{gender:0}]})
    1
    2
    3
    4
    5
    db.集合名.find({
    条件1,
    条件2,
    $or:[{条件3},{条件4}]
    })
  5. limit,skip:

    • limit():读取指定数量的数据记录

      1
      db.student.find().limit(2)
    • skip():跳过指定数量的数据

      1
      2
      // 跳过前三条
      db.student.find().skip(3)
    • limit与skip联合使用:
      实现分页功能

      1
      db.student.find().skip(3).limit(2)
  6. 排序:

    1
    db.集合名.find().sort(<key>:1|-1)

    1表示升序,-1表示降序

    1
    db.student.find().sort(age:1)

练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 进入my_test效据库
use my_test

# 向数据库的user集合中插入一个文档
db.user.insert({name:'hyl'})

# 查询user集合中的文档
db.users.find()

# 统计数据库user集合中的文档数量
db.users.find().count()

# 查询数据库user集合中username为sunwukong的文档
db.users.find({username:'sunwukong'})

# 向数据库user集合中的username为sunwukona的文档,添加一个address属性,属性值为huaguoshan
db.users.update({username:'sunwukona'},{$set:{address:'sunwukona'}})

# 使用{username:“tangseng”}替换username为 zhubajie的文档
db.users.replaceOne({username:'zhubajie'},{username:'tangseng'})
# 或者
db.users.update({username:'zhubajie'},{username:'tangseng'})

# 删除username为sunwukong的文档的address属性
db.users.update({username:'sunwukong'},{$unset:{address:1}})

# 向username为sunwukong的文档中,添加一个 hobby:{cities:["beijing","shanghai","shenzhen"],movies:["sangu","hero"]}
db.users.update({username:sunwukong},{$set:hobby:{cities:["beijing","shanghai","shenzhen"],movies:["sangu","hero"]})

# 向username为tangseng的文档中,添加一个 {hobby:{movies:["A Chinese odyssey","King of comedy"]}
db.users.update({username:'tangseng'},{$set:{hobby:{movies:["A Chinese odyssey","King of comedy"]} )

# 查询喜欢电影<hero>的文档
db.users.find({"hobby.movies":'hero'})

# 向tangseng中添加一个新的电影<teretellar>
db.users.update({username:'tangseng'},{$push:{"hobby.movies":'teretellar'}})

# 删除喜欢beijing的用户
db.users.remove({'hobby.cities':'beijing'})

# 删除user集合
db.users.drop()

# 向numbers中插入20000条数据
for (var i=x; i<=20000; i++){
db.numbers.insert({num:x})
}
# 或者
var arr = [];
for (var i=x; i<=20000; i++){
arr.push(num:i);
}
db.numbers.insert(arr)

# 查询numbers中num为500的文档
db.numbers.find({'num':500})

# 查询numbers中num大于5000的文档
db.numbers.find({'num':{$gt:500}})

# 查询numbers中num小于30的文档
db.numbers.find({'num':{$lt:30}})

# 查询numbers大于40小于50的文档
db.numbers.find({'num':{$gt:40,$lt:50}})

# 查看numbers集合中的前10条效据
db.numbers.find().limit(10)

# 查看numbers集合中的第11条到20条数指
db.numbers.find().skip(10).limit(10)

# 查看numbers集合中的第21条到30条数据
db.numbers.find().skip(20).limit(10)

MongoDB支持直接通过内嵌文档的属性进行查询.使用点号获取.
但是注意,此时的属性名必须使用引号

db.users.update({username:sunwukong},{$set:hobby:{cities:["beijing","shanghai","shenzhen"],movies:["sangu","hero"]})

db.users.find({hobby.movies:'hero'})是错的.
必须使用db.users.find({"hobby.movies":'hero'})

发现movies其实是一个数组.所以说,MongoDB还支持数组的匹配

$push用于向数组中添加一个新的元素

db.users.update({username:'tangseng'},{$set:{hobby:{movies:["A Chinese odyssey","King of comedy"]} )

db.users.update({username:'tangseng'},{$push:{"hobby.movies":'teretellar'}})

还可以使用$addToSet,有点类似于Redis的msetnx,如果数组中已经存在了该元素,则添加失败

注意查询集可以使用skip()和limit()

配合使用:
skip((页码-1)*每页显示的条数).limit(每页显示的条数)

MongoDB会自动调整limitskip的位置,所以写成.skip().limit()也行


文档间的关系

  • 一对一
  • 一对多
  • 多对多

MongoDB可以通过内嵌文档的形式来体现出一对一的关系

1
2
3
4
5
6
7
8
9
10
db.person.insert({
name:'黄蓉',
husband:{
name:'郭靖'
}
},{
name:'潘金莲',
husband:{
name:'武大郎'
})

一对多也可以使用内嵌文档的形式表示:
只要将内嵌文档的字典改为列表即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.artcile.insert({
title:'这是文章标题1',
reply:[
{name:'用户1',context:'评论1'},
{name:'用户2',context:'评论2'},
{name:'用户3',context:'评论3'},
]
},{
title:'这是文章标题2',
reply:[
{name:'用户1',context:'评论1'},
{name:'用户2',context:'评论2'},
{name:'用户3',context:'评论3'},
]
})

和MySQL类似,MongoDB一对多关系也可以拆成两张表:

1
2
3
4
5
6
7
8
9
10
11
12
13
db.artcile.insert({
title:'这是文章标题',
content:'这是文章内容',
author:'这是文章作者',
reply:ObjectId('46123887135871578715')
})

// ObjectId('46123887135871578715')
db.reply.insert({
{name:'用户1',context:'评论1'},
{name:'用户2',context:'评论2'},
{name:'用户3',context:'评论3'},
})

那么拆成两个集合如何查找?
查找标题为XXX的文章对应的回复:

1
2
var replyid = db.artcile.findOne({title:'XXX'})._id;
db.reply.find({replyid:replyid})

这种查找方式很像Django里的ORM的会造成1+N问题的查找方式

多对多的关系在MongoDB中还是以两张表的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
db.terchers.insert([
{name:'tercher1'},
{name:'tercher2'},
{name:'tercher3'},
])

db.stus.insert([
{name:'student1',
tech_id:[
ObjectId('46ssadasdsdfas5')
ObjectId('461asdasdff8715')
ObjectId('4612388713dfgdf')
]},
{name:'student2',
tech_id:[
ObjectId('46ssadasdsdfas5')
ObjectId('461asdasdff8715')
ObjectId('4612388713dfgdf')
]}
])

练习:
现在有两个JSON文件dept.jsonemp.json(部门和员工)

1560821848709

1560821842958

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 查均工资小于2000的员工
db.emp.find({sal:{$lt:2000}})

# 查均工资在1000-2000之间的员工
db.emp.find({sal:{$lt:2000,$gt:1000}})

# 查工资小于1000或大于2500的员工
db.emp.find({$or:[sal:{$lt:1000},sal:{$gt:2500}]})

# 查均财务部的所有员工
var dept = db.dept.find({dname:'财务部'})[0].deptn
db.emp.find({depno:depto})

# 查均销售部的所有员工
var dept = db.dept.findOne({dname:'销售部'})
db.emp.find({depno:'deptOne.deptno'})

# 查询所有mgr为7698的所有员工
db.emp.find({mgr:7698})

# 为所有薪资低于1000的员工增加工资400元
db.emp.update({sal:{$lte:1000},{$inc:{sal:400}})

查均财务部的所有员工

var dept = db.dept.find({dname:'财务部'})[0].deptn
db.emp.find({depno:depto})

注意 db.dept.find({dname:'财务部'})的查询结果是数组,要用[0]切出来

或者直接使用
var dept = db.dept.findOne({dname:'财务部'}).deptn


排序

1
2
# 主要关键字sal升序排列,次要关键字empno降序排列
db.emp.find().sort({sal:1,empno:-1})

limit,skip和sort可以任意顺序编写

投影:

1
2
3
4
5
// 查询全部行
db.emp.find({})

// 截取出ename列和sal列(这种方式成为投影)
db.emp.find({},{ename:1,sal:1})

Mongoose:Node.js的MongoDB的模块

  • 可以为文档创建一个模式结构(Schema)
  • 可以对模型中的对象/文档进行验证
  • 数据可以通过类型转换转换为对象模型
  • 可以使用中间件来应用业务逻辑挂钩
  • 比Node原生的MongoDB驱动更容易

mongoose中为我们提供了几个新的对象

  • Schema(模式对象)
    Schema对象定义约束了数据库中的文档结构
  • ModeI
    Mode对象作为集合中的所有文档的表示,相当于MongoDB数据库中的集合collection
  • Document
    表示集合中的具体文档,相当于集合中的一个具体的文档

安装

1
npm i mongoose --save

导入:

1
var mongoose = require('mongoose');

连接:

1
Mongoose.connect('mongodb://localhost:27017/数据库',{useMongoClient:true})

监听数据库的连接:使用mongoose的connection属性

1
2
3
4
// 数据库成功连接的事件
mongoose.connection.once('open',function(){});
// 数据库断开的事件
mongoose.connection.once('close',function(){});

简单使用:

1
2
3
4
5
6
7
8
9
10
var mongoose = require('mongoose');

Mongoose.connect('mongodb://localhost:27017/test_db',{useMongoClient:true});

mongoose.connection.once('open',function(){
console.log('数据库连接成功')
});

// 断开数据库连接
mongoose.disconnect();

Schema模式对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var mongoose = require('mongoose');

Mongoose.connect('mongodb://localhost:27017/test_db',{useMongoClient:true});

mongoose.connection.once('open',function(){
console.log('数据库连接成功')
});

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{ body: String, date: Date }],
// 设置默认值
date: { type: Date,
default: Date.now
},
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});

// 和MySQL一样,我们是通过Schema来创建Model的
// mongoose.model(modelName, Schema)
var BlogModel = mongoose.model('Blog', blogSchema);

// 插入文档
BlogModel.create({
title:'this is blog',
author:'liangbo',
},function (err){
if(!err){
console.log('插入成功');
}
})

使用Model对数据库进行增删改查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var BlogModel = mongoose.model('Blog', blogSchema);

// 创建:Model.create(docs,[callback])
BlogModel.create([
{
title:'this is blog1',
author:'liangbo1',
},
{
title:'this is blog2',
author:'liangbo2',
}
],function (err){
if(!err){
console.log('插入成功');
}
})

// 查询
// Model.find(conditions,[projection],[options],[callback])
// Model.findById(id,[projection],[options],[callback])
// Model.findOne(conditions,[projection],[options],[callback])
// projection:投影,options:查询选项(如skip,limit)

// 查询title为XXX的文章,显示他的标题,作者,(不显示id),跳过前面3行
// -_id表示不显示id列
BlogModel.find({title:'XXX'},{'title author -_id'},{skip:3},function(err,docs){
if(!err){
// 查询成功直接返回查询结果
console.log(docs);
console.log(docs[0].author);
}
})

通过find()查询的结果,返回的对象就是Document文档对象

有点类似于Django的Model,mongoose中的Document对象就是Model的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 修改
// Model.update(conditions,doc,[options],[callback])
// Model.updateMany(conditions,doc,[options],[callback])
// Model.updateOne(conditions,doc,[options],[callback])

var BlogModel = mongoose.model('Blog', blogSchema);

// 将标题为XXX的文章对应的作者改为liangbo
BlogModel.update({title:'XXX'},{$set:{author:'liangbo'}},function (err){
if(!err){
console.log('修改成功');
}
})
1
2
3
4
5
6
7
8
9
10
11
12
// 删除
// Model.remove(conditions,[callback])
// Model.deleteOne(conditions,[callback])
// Model.deleteMany(conditions,[callback])

var BlogModel = mongoose.model('Blog', blogSchema);

BlogModel.remove({title:'XXX'},function (err){
if(!err){
console.log('删除成功');
}
})

Count:

1
2
3
4
5
6
7
// Model.count(conditions,[callback])

BlogModel.count({title:'XXX'},function (err,count){
if(!err){
console.log('一共有',count);
}
})

document

Document是Model的实例,和Django不同的是Model查询结果是查询集(queryset),mongoose通过Model查询的结果都是Document

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var mongoose = require('mongoose');

Mongoose.connect('mongodb://localhost:27017/test_db',{useMongoClient:true});

mongoose.connection.once('open',function(){
console.log('数据库连接成功')
});

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{ body: String, date: Date }],
// 设置默认值
date: { type: Date,
default: Date.now
},
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});

var BlogModel = mongoose.model('Blog', blogSchema);

// 创建文档对象
var blog = new BlogModel({
title:'XXX',
author:'liangbo',
})

Document的方式:

  • save
  • equals(doc)
  • id
  • get(path,[type])
  • set(path, value, [type])
  • update(update, [options], [callback])
  • save([callback])
  • remove([callback])
  • isNew
  • isInit(path)
  • toJSON()
  • toObject():转化为普通的js对象
1
2
3
4
5
6
// Model#save([options],[fn])
blog.save(function(err){
if(!err){
console.log('保存成功');
}
})

这种方法和DJango的Model操作及其相似,可以使用Model.create方法创建行,也可以使用document.save创建行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StudentModel.findOne({},function (err,doc){
if (!err){
doc.update({$set:{age:28}},function (err){
if (!err){
console.log('修改成功');
}
})
}
});
// or
StudentModel.findOne({},function (err,doc){
if (!err){
doc.age = 18;
doc.save();
}
});