[TOC]

1. MongoDB简介

  1. MongoDB是一个开源的NoSQL数据库;
  2. 同时它也是一个对象数据库,没有固定的模式和结构;
  3. 所有的数据以文档的形式存储,数据格式就是JSON。

2. Mongoose是什么?

  1. MongooseMongoDB的一个对象模型工具;
  2. 同时它也是针对MongoDB操作的一个对象模型库,封装了MongoDB对文档的的一些增删改查等常用方法;
  3. NodeJS操作Mongodb数据库变得更加灵活简单。

3. Mongoose模块介绍

mongoose模块用来将nodejs中的对象与mongodb中的文档进行对应的模块。 Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具。那么要使用它,首先你得装上node.jsmongodb

3.1 mongoose安装

npm install mongoose
1

安装成功后,就可以通过require('mongoose')来使用。

官网实例:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));
1
2
3
4
5
6
7

Mongoose提供了一个直观的,基于模式的解决方案来建模应用程序数据。它包括内置的类型转换,验证,查询构建,业务逻辑挂钩等,开箱即用;是mongoDB的好基友。

为了保存网站的用户数据和业务数据,通常需要一个数据库。MongoDB和Node.js特别般配,因为mongodb是基于文档的非关系型数据库,文档是按BSON(JSON的轻量化二进制格式)存储的,增删改查等管理数据库的命令和JavaScript语法很像。如果你在node.js里访问MongoDB的数据,会有我们是一家人的感觉,特别亲切。

MongoDB使用集合(collection)和文档(document)来描述和存储数据,collection就相当于表,document相当于行,不过MySQL之类的关系型数据库,表结构是固定的,比如某一行由若干列组成,行行都一样,而MongoDB不同,一个集合里的多个文档可以有不同的结构,更灵活一些。

78f5517bf2e0a7ef14533b03e675848e.png

4. 配置与连接

创建一个db.js:

// 引入mongoose
const mongoose = require('mongoose');
// 连接字符串
const DB_URL = 'mongodb://localhost:27017/mongoosesample';
const db = mongoose.connection;

// 连接数据库
mongoose.connect(DB_URL, {useNewUrlParser: true});

// 连接成功
db.on('connected', () => {
    console.log('Mongoose connection open to:' + DB_URL);
});

// 连接失败
db.on('error', err => {
    console.log('Mongoose connection error:' + err);
});

// 连接断开
db.on('disconnected', () => {
    console.log('Mongoose connection disconnected');
});

module.exports = mongoose;
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
const mongoose = require('mongoose');
const DB_URL = 'mongodb://localhost:27017/mongoosesample';

/**
 * 连接
 */
mongoose.connect(DB_URL);

/**
  * 连接成功
  */
mongoose.connection.on('connected', () => {
    console.log('Mongoose connection open to ' + DB_URL);
});

/**
 * 连接异常
 */
mongoose.connection.on('error', (err) => {
    console.log('Mongoose connection error: ' + err);
});

/**
 * 连接断开
 */
mongoose.connection.on('disconnected', () => {
    console.log('Mongoose connection disconnected');
});

module.exports = mongoose;
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

5. Modal与Schema

5.1 Schema

Schema是数据库集合的模型骨架,定义了集合中的字段的名称和类型以及默认值等信息。

schemamongoose里会用到的一种数据模式,可以理解为表结构的定义;每个schema会映射到mongodb中的一个collection,它不具备操作数据库的能力。我们先改造一下db.js,导出mongoose对象。

5.2 Schema.Type

NodeJS中的基本数据类型都属于Schema.Type。另外Mongoose还定义了自己的类型,基本属性类型有:

  • 字符串(String)
  • 日期型(Date)
  • 数值型(Number)
  • 布尔型(Boolean)
  • null
  • 数组([])
  • 内嵌文档

定义Schema:

const personSchema = new Schema({
  name: String, //姓名
  binary: Buffer, //二进制
  living: Boolean, //是否活着
  birthday: Date, //生日
  age: Number, //年龄
  _id: Schema.Types.ObjectId, //主键
  _fk: Schema.Types.ObjectId, //外键
  array: [], //数组
  arrOfString: [String], //字符串数组
  arrOfNumber: [Number], //数字数组
  arrOfDate: [Date], //日期数组
  arrOfBuffer: [Buffer], //Buffer数组
  arrOfBoolean: [Boolean], //布尔值数组
  arrOfObjectId: [Schema.Types.ObjectId] //对象ID数组
  nested: { //内嵌文档
    name: String,
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

5.3 Model

Model是由通过Schema构造而成,除了具有Schema定义的数据库骨架以外,还可以操作数据库。如何通过Schema来创建Model呢,如下:

// 连接数据库
mongoose.connect("mongodb://123.57.143.189:27017/zfpx");
// 两个参数表示定义一个模型
// PersonSchema是定义好的Schema
// Person是Modal的名称,第一个字母要大写
const PersonModel = mongoose.model("Person", PersonSchema);
// 如果该Model已经定义,则可以直接通过名字获取
const PersonModel = mongoose.model('Person'); // 一个参数表示获取已定义的模型
1
2
3
4
5
6
7
8

拥有了Model,我们也就拥有了操作数据库的能力。

在数据库中的集合名称等于模型名转小写再转复数,比如Person>person>people,Child>child>children。

d94d433935b73f873232794d099c6bca.png 86f41e35a12e070046d03ef229c7b431.png

在上例中,我们的模型名称为Person,最后的collection的名称为people

6. 创建文档

7. 删除文档

8. 简单查询

9. 条件语句

mongoose增删改查Demo

在项目中安装mongoose:

cnpm install mongoose // 或者
yarn add mongoose
1
2

做个简单项目的项目结构,对数据的增删改查,都是简单的操作,主要是引入mongoose的链接操作,复杂的增删改查在MongoDB的基础知识中学习。

目录结构如下:

d85407c74206be2e7a56e6bf73a7486f.png

MongoDB数据库框架mongoose

db.js 		连接数据库

Model.js 	构建模型骨架,构建Model

insert.js 	插入数据()

delete.js 	删除数据()

update.js 	修改数据()

find.js 	查询数据()

MongoDB可视化工具robo 3t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

连接数据库

连接数据库,创建db.js文件

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/user', { useNewUrlParser: true });

// 连接失败
mongoose.connection.on('error', err => {
    console.error('数据库链接失败:' + err);
});

// 连接成功
mongoose.connection.on('open', () => {
    console.log('数据库链接成功');
});

// 断开数据库
mongoose.connection.on('disconnected', () => {
    console.log('数据库断开成功');
})

// 将mongoose导出
module.exports = mongoose;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

构建模型骨架Schame

构建模型骨架Schame,构建Model,创建文件Model.js:

const mongoose = require('./db.js');

// 模型骨架
const Schema = new mongoose.Schema({
    username: {type: String},
    age: {type: Number, default: 10},
    address: {type: String},
    time: {type: Date} // 创建时间
});

// 定义好了Schema,接下就是生成Model。model是由schema生成的模型,可以对数据库的操作。
// 由schema构造生成Model
const Model = mongoose.model('user', Schema);

module.exports = Model;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

定义一个Schema就这么简单,指定字段名和类型。Schema Types内置类型如下:

  • String
  • Number
  • Boolean | Bool
  • Array
  • Buffer
  • Date
  • ObjectId | Oid
  • Mixed

插入数据

增加数据,创建文件insert.js:

const Model = require('./Model.js');

// 插入数据
Model.create([
    {
        username: 'wangwu',
        age: 33,
        address: 'tianjin',
        time: new Date()
    }
], (err, res) => {
    if (err) {
        console.error(err);
    }
    else {
        console.log('插入数据成功');
        console.log(res);
    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

删除数据

删除数据, 创建文件delete.js:

const Model = require('./Model.js');

// 删除数据
Model.deleteOne({username: 'zhaoliu'}, (err, res) => {
	if(err) {
		console.error(err);
    }
    else {
		console.log(res);
	}
});
1
2
3
4
5
6
7
8
9
10
11

修改数据

修改数据, 创建文件 update.js:

const Model = require('./Model.js');
// 更新数据
Model.updateOne({username: 'liutao'}, {age: 62}, (err, res) => {
	if(err) {
		console.error('Error: ' + err);
        return;
    }
    console.log(res);
});
1
2
3
4
5
6
7
8
9

查询数据

查询数据,创建文件find.js

// 引入模型
const Model = require('./Model.js');

// 查询数据(通过调用模型的find方法)
// find函数的第一个参数是查询条件,第二个参数是查询操作完成后的回调,回调函数的第一个参数是查询出错后的错误对象,第二个参数是查询的结果。
// 当第一个参数为{}空对象时,可以返回所有结果
Model.find({username: 'liutao'}, (err, res) => {
	if(err){
		console.log(err);
        return;
    }
    console.log(res);
})
1
2
3
4
5
6
7
8
9
10
11
12
13

findOne

findOne()方法:只返回符合查询条件的第一条数据。

const Model = require('./Model.js');

// 查询数据
Model.findOne({username: 'liutao'}, (err, res) => {
	if(err){
		console.log(err);
    }
    else {
		console.log(res);
	}
});
1
2
3
4
5
6
7
8
9
10
11

条件查询

const Person = require('./Model.js');
// 查询username为lisi或者wangwu的数据
const cond = {
    $or: [
        {username: 'lisi'},
        {username: 'wangwu'}
    ]
};
Person.find(cond, (err, res) => {
	if(err){
        console.log(err);
        return;
    }
    console.log('cond:', cond, 'res:', res);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

f21824f094c8bd4aed15ea810f1448e9.png

Mongoose 使用进阶

模式的扩展

默认值

默认值的类型:

  • 固定值
  • 即时生成
const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    username: {type: String, default: 'lisi'},
    age: {type: Number, default: 12},
    address: {type: String, default: 'beijing'},
    time: {type: Date, default: Date.now}
});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
const person = new Person(); // 这里不传参数将使用Modal的默认值
console.log(person);

module.exports = Person;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

c7a7dcfa1ad7a161f7ece2fa422038ba.png

模式修饰符

  • 预定义的模式修饰符(例如:去除字段值两侧的空格)
  • 自定义setter修饰符
  • 自定义getter修饰符

setter.js

const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    username: {
        type: String,
        trim: true // 去空格(预定义的模式修饰符)
    },
    age: {type: Number}
});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
const person = new Person({
    username: '  zhaoliu ',
    age: 20
});
console.log(person);

module.exports = Person;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

9e3af90848a33613066b44730f61a428.png

如上图所示:username左右两侧的空格被去除了。

自定义setter修饰符demo:

const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    username: {
        type: String,
        trim: true // 去空格
    },
    age: {type: Number},
    blog: {
        type: String,
        set: url => { // 如果blog前面没有协议,则默认添加'http://'
            if (!url) return url;
            if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
                url = 'http://' + url;
            }
            return url;
        }
    }
});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
const person = new Person({
    username: '  zhaoliu ',
    age: 20,
    blog: 'www.baidu.com'
});
console.log(person);

module.exports = Person;
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

09d7801364fea6d7e07ba1ec48197f88.png

如上图所示,blog字段前面加上了相应的协议。

自定义getter修饰符demo:

const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    blog: {
        type: String,
        get: url => { // 如果blog前面没有协议,则默认添加'http://'
            if (!url) return url;
            if (url.indexOf('http://') !== 0 || url.indexOf('https://') !== 0) {
                url = 'http://' + url;
            }
            return url;
        }
    }
});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
const person = new Person({
    blog: 'www.baidu.com' // 这里的值会传给get修饰符
});
console.log(person);

module.exports = Person;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

4c598c8135f365d237bfc2eaf6a8e647.png

虚拟属性(使用Schema.virtual()方法创建)

virtual.js

const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    fristName: String,
    lastName: String
});

// 创建虚拟属性
PersonSchema.virtual('fullName').get(function() {
    return this.fristName + ' ' + this.lastName;
});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
// 实例化
const person = new Person({
    fristName: 'li',
    lastName: 'si'
});
console.log('fullName:', person.fullName);

module.exports = Person;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

2bf27c8bc990dabcecc671843533e78b.png

将person转为json:

const mongoose = require('./db.js');

// 模型骨架
const PersonSchema = new mongoose.Schema({
    fristName: String,
    lastName: String
});

// 创建虚拟属性
PersonSchema.virtual('fullName').get(function() {
    return this.fristName + ' ' + this.lastName;
});

PersonSchema.set('toJSON', {getters: true, virtual: true});

// 由schema构造生成Model
const Person = mongoose.model('Person', PersonSchema);
// 实例化
const person = new Person({
    fristName: 'li',
    lastName: 'si'
});
console.log('fullName:', person.fullName);
console.log('JSON:', JSON.stringify(person));

module.exports = Person;
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

48104b28cd61e006633934d001120139.png

索引

索引的目的

  • 唯一索引
  • 辅助索引(用来增加查询速度)

模型的方法

自定义静态方法:

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/book', { useNewUrlParser: true });

// 模型骨架
const BookSchema = new mongoose.Schema({
    isbn: Number,
    name: String
});
// 定义静态方法
BookSchema.statics.findByISBN = function(isbn, cb) {
    this.findOne({isbn: isbn}, (err, doc) => {
        cb(err, doc);
    });
};
const Book = mongoose.model('Book', BookSchema);
const book = new Book({
    name: '计算机程序设计',
    isbn: 123456
});
book.save(err => {
    if (err) {
        return console.log('save book failed', err);
    }
    Book.findByISBN(123456, (err, doc) => {
        console.log('findByISBN, err, doc:', err, doc);
    });
});
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

5c8c5ab3aebecb880ad9ddaea3ff7fea.png

自定义实例方法:

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/book', { useNewUrlParser: true });

// 模型骨架
const BookSchema = new mongoose.Schema({
    isbn: Number,
    name: String
});
// 定义静态方法
BookSchema.statics.findByISBN = function(isbn, cb) {
    this.findOne({isbn: isbn}, (err, doc) => {
        cb(err, doc);
    });
};
// 定义实例方法
BookSchema.methods.print = function() {
    console.log('Book info:');
    console.log('\tTitle:', this.name);
    console.log('\tISBN:', this.isbn);
};
const Book = mongoose.model('Book', BookSchema);
const book = new Book({
    name: '计算机程序设计',
    isbn: 123456
});
book.save(err => {
    if (err) {
        return console.log('save book failed', err);
    }
    Book.findByISBN(123456, (err, doc) => {
        console.log('findByISBN, err, doc:', err, doc);
    });
    book.print(); // 调用实例方法
});
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

99821836cd3ed06aec8d52fb1346c476.png

数据的校验

预定义的验证器:required,Number(max,min),String(enum,match)。

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/book', { useNewUrlParser: true });

// 模型骨架
const OrderSchema = new mongoose.Schema({
    count: {
        type: Number,
        required: true, // 设置必须
        max: 1000, // 设置最大值
        min: 10 // 设置最小值
    },
    status: {
        type: String,
        enum: ['created', 'success', 'failed'] // 枚举验证器
    },
    desc: {
        type: String,
        match: /book/g, // 正则验证器,值中必须包含book字符串
        validate: function(desc) { // 自定义验证器,必须大于10个字符
            return desc.length >= 10;
        }
    }
});

const Order = mongoose.model('Order', OrderSchema);
const order = new Order();
order.count = 100;
order.status = 'success';
order.desc = 'test';
order.save(err => {
    if (err) {
        return console.log('save failed', err);
    }
    console.log('save success');
});
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

2c620e33b5c083e275da09ae07777384.png 2fe112e5e6ff274bfea95821c76e3b64.png f4245f4e6d6f6313e7a2c51f7ad7a2cc.png 57fc355ae4747f9db26db598cadd4045.png

自定义验证器:

中间件

974c76ca967771029d7a58a9f5cdf3d9.png

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/book', { useNewUrlParser: true });

// 模型骨架
const ResellerSchema = new mongoose.Schema({
    address: String
});

ResellerSchema.post('save', function(next) {
    console.log('我是后置中间件,在保存动作之后执行');
    next();
});
ResellerSchema.pre('save', true, function(next, done) {
    console.log('我是预处理中间件,在保存动作之前执行');
    next();
    done();
});
const Reseller = mongoose.model('Reseller', ResellerSchema);
const reseller = new Reseller({
    address: 'beijing'
});
reseller.save();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

513c89ff966c6342ce4bd868c79bc346.png

DBRef

DBRef作用:在一个collection中引用另一个collection中的数据。实现集合数据的交叉引用。

// 连接数据库
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/book', { useNewUrlParser: true });

// 模型骨架
const UserSchema = new mongoose.Schema({
    username: String
});

const NewsSchema = new mongoose.Schema({
    title: String,
    author: {
        type: mongoose.Schema.ObjectId,
        ref: 'User' // 指定使用User集合中的数据,通过ref指定将两个集合关联起来
    }
});

const Users = mongoose.model('User', UserSchema);
const News = mongoose.model('News', NewsSchema);

const user = new Users({
    username: 'lisi'
});
const news = new News({
    title: '武侠小说',
    author: user
});

user.save(err => {
    if (err) {
        return console.log('save failed', err);
    }
    news.save(err => {
        if (err) {
            return console.log('save news failed:', err);
        }
        News.findOne().populate('author').exec((err, doc) => {
            console.log('save success:', doc);
        });
    });
});
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

db5cec2389e40d97006390e90b188bd9.png

参考文档

  1. https://github.com/Automattic/mongoose
  2. 挑战全栈 MongoDB基础视频教程
  3. mongoose4.5中文文档
  4. mongoose文档
  5. Nodejs学习笔记(十四)— Mongoose介绍和入门
  6. Mongoose介绍和入门
  7. mongoose中文文档
  8. Mongoose增查改删学习笔记
  9. mongodb和nodejs mongoose使用详解
  10. Node+express+mongoose 基础笔记
  11. 超详细的数据库mongoose的使用方法/教程
  12. MongoDB的可视化工具 robo 3t MongoDB的可视化工具 robo 3t