安装

  1. npm i mongoose

连接 mongodb

如果没有密码且在本地

  1. const mongoose = require('mongoose')
  2. // 简单 connect() 方法
  3. mongoose.connect('mongodb://localhost/dbName')
  4. // 在connect() 方法上配置options,并设置回调
  5. // useNewUrlParser 在新版本中需要设置
  6. mongoose.connect('mongodb://localhost/mongoose_test', {useNewUrlParser: true}, (err) => {
  7. if (err) {
  8. console.log('err', err)
  9. }
  10. console.log('数据库连接成功...')
  11. })

如果有参数

const mongoose = require('mongoose')
mongoose.connect('mongodb://myAdmin:12345@localhost:27017/dbName')
// myAdmin: 用户名
// 12345: 密码
// 27017: 端口

Schemas

每个 schema 都会映射到一个 MongoDB collection(集合,表) ,并定义这个collection里的文档的构成。

const UserSchema = mongoose.Schema({
  name: String,
  age: Number,
  status: Number
})

:::warning [todo] 添加Schemas的其他使用方法 :::

Model

Model 是由 Schema 生成的模型,可以操作数据库。
mongoose.model() 方法可以传入两个参数或三个参数。 :::info 参数1:为模型名称,首字母要大写
参数2:为 Schema 对象
参数3:数据库collection名称,可省略
==> 注意:
参数3省略时,会创建一个名称为模型名称(参数1)复数的集合(表)。 :::

const UserSchema = mongoose.Schema({
  name: String,
  age: Number,
  status: Number
})
// 两个参数,推荐
const UserModel = mongoose.model('User', UserSchema)

查找数据

查找方法较多,此处列举部分。

find()

基本语法:

// doc 代表记录
UserModel.find({条件}, (err, doc) => {
})

查询所有

// 查询所有,没有条件
UserModel.find({}, (err, doc) => {
  if (err) return
  console.log('find', doc)
})

保存数据

需要新建 Model 实例,通过实例的 save() 方法。

const user = new UserModel({
  name: 'mangolee',
  age: 30,
  status: 0
})
// ret: 为返回的结果,返回保存的对象
user.save((err, ret) => {
  console.log('result', ret)
})

修改数据

修改数据的方法有很多,这里只列举部分。

updateOne()

// 参数1: 条件, name 属性为 mango 的第一条记录
// 参数2: 更新项, 将 staus 属性更新为 1
// 参数3:  回调, result 返回更新情况
UserModel.updateOne({name: 'mango'},{status: 1}, (err, result) => {
  if (err) return
  console.log('update', result)
})

update()

UserModel.update({name: 'mango'},{status: 1}, (err, result) => {
  if (err) return
  console.log('update', result)
})

删除数据

deleteOne()

UserModel.deleteOne({条件}, (err, result) => {

})

mongoose 默认参数

在定义 Schema 时设置默认参数

const UserSchema = mongoose.Schema({
  name: String,
  age: Number,
  status: {
    type: Number,
    default: 0
  }
})

mongoose 模块化

将Model 定义 和 Model操作 分离开,一般这样分离:
使用 模块化后 不会影响性能问题。

Project目录
|_src
    |_model
      |_ db.js  实现数据库连接
    |_ user.js 配置Schema,Model,暴露Model
    |_ news.js 配置Schema,Model, 暴露Model
  |_其他目录
      |_app.js 使用各种 Model

model/db.js

/**
 * 连接数据库,暴露连接成功后的mongoose对象
 */
const mongoose = require('mongoose')

// 连接
mongoose.connect('mongodb://localhost/mongoose_test', {useNewUrlParser: true}, (err) => {
  if (err) {
    console.log('err', err)
  }
  console.log('数据库连接成功...')
})

module.exports = mongoose

model/ user.js

/**
 * 操作 users 集合(表)
 * 配置 Schema, Model
 */
const mongoose = require('./db')

const UserSchema = mongoose.Schema({
  name: String,
  age: Number,
  status: Number
})
// 两个参数,推荐
const UserModel = mongoose.model('User', UserSchema)

module.exports = UserModel

model/news.js

/**
 * 操作 users 集合(表)
 * 配置 Schema, Model
 */
const mongoose = require('./db')

const NewsSchema = mongoose.Schema({
  name: String,
  content: String,
  status: Number
})

const NewsModel = mongoose.model('News', NewsSchema, 'news')

module.exports = NewsModel

app.js 操作数据库

const UserModel = require('../model/users')
const NewsModel = require('../model/news')
// 操作
const user = new UserModel({
  name: 'Tom',
  age: 29
})
user.save((err, result) => {
  console.log('user save ', result)
})

const news = new NewsModel({
  name: '头条',
  content: '这是一条新闻'
})
news.save((err, result) => {
  console.log('news save', result)
})

mongoose 预定义模式修饰符

mongoose 提供的预定义模式修饰符,对增加的数据进行一此格式化。

  • lowercase
  • uppercase
  • trim
const UserSchema = mongoose.Schema({
  name: {
    type: String,
    trim: true, // 去除空格
    uppercase: true // 转换为大写
  },
  age: Number,
  status: {
    type: Number,
    default: 0
  }
})

Getters 与 Setters 自定义修饰符

除了mongoose 内置的修饰符以外,我们还可以通过set(建议使用) 修饰符在增加数据的时候对数据进行格式化。也可以通过get(不建议使用)在实例获取数据的时候对数据进行格式化。 :::info set:建议使用
get: 不建议使用,只有在 user.content 方法,获取数据时采会调用 getter :::

var NewsSchema=mongoose.Schema({
  title:"string",
  author:String,
  pic:String,
  redirect:{
  type:String,
  set(url){
    if(!url) return url;
    if(url.indexOf('http://')!=0 && url.indexOf('https://')!=0){
      url = 'http://' + url;
    }
    return url;
  },
  get: function(url){
    if(!url) return url;
    if(url.indexOf('http://')!=0 && url.indexOf('https://')!=0){
      url = 'http://' + url;
    }
    return url;
    }
  },
  content:String,
  status:{
  type:Number,
  default:1
  }
})

例子:

Schema 中定义 setter 和 getter

/**
 * 操作 users 集合(表)
 * 配置 Schema, Model
 */
const mongoose = require('./db')

const NewsSchema = mongoose.Schema({
  name: String,
  content: {
    type: String,
    set(content) {
      return 'setter:' + content
    },
    get(content) {
      return 'getter:' + content
    }
  },
  status: Number
})

const NewsModel = mongoose.model('News', NewsSchema, 'news')

module.exports = NewsModel

使用

const NewsModel = require('../model/news')

const news = new NewsModel({
  name: '头条',
  content: '随便一条2',
  status: 0
})

news.save((err) => {
  if (!err) {
    console.log('保存成功')
  }
})
// 记录:
// {content: 'setters: 随便一条2'}

let result = null
NewsModel.findOne({
  name: '头条'
}, (err, doc) => {
  result = doc
  console.log('result:', result)
  console.log(result.content) // 获取时会加上getter:, 'getter: setter: 随便一条2'
})

MongoDB 索引

索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧。

索引基础

创建索引命令

// username 字段创建索引
db.user.ensureIndex({
    "username": 1 // 1 表示升序存储, -1 表示降序存储
})

获取当前集合索引

db.user.getIndexes()

删除索引的命令

db.user.dropIndex({
    "username": 1
})

创建复合索引

db.user.ensureIndex({
    "username": 1,  // username 升序
  "age": -1       // age 降序存储
})

该索引被创建后,基于username和age的查询将会用到该索引,或者是基于username的查询也会用到该索引,但是只是基于age的查询将不会用到该复合索引。因此可以说,如果想用到复合索引,必须在查询条件中包含复合索引中的前N个索引列。然而如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB可以智能的帮助我们调整

// MongoDB在检索之前将会动态的调整查询条件文档的顺序,以使该查询可以用到刚刚创建的复合索引。
db.user.find({"age": 30, "username": "stephen"})

指定索引名

MongoDB都会根据索引的keyname和索引方向为新创建的索引自动分配一个索引名,下面的命令可以在创建索引时为其指定索引名,如:

db.user.ensureIndex({"username":1}, {"name": "userIndex"}) // 索引名为userIndex

:::warning 随着集合的增长,需要针对查询中大量的排序做索引。如果没有对索引的键调用sort,MongoDB需要将所有数据提取到内存并排序。因此在做无索引排序时,如果数据量过大以致无法在内存中进行排序,此时MongoDB将会报错。 :::

唯一索引

在缺省情况下创建的索引均不是唯一索引。下面的示例将创建唯一索引,如:

db.user.ensureIndex({"userId": 1}, {"unique": true})
  • 如果再次插入userid重复的文档时,MongoDB将报错,以提示插入重复键。
  • 如果插入的文档中不包含userid键,那么该文档中该键的值为null,如果多次插入类似的文档,MongoDB将会报出同样的错误

索引的一些参数

image.png

如果在为已有数据的文档创建索引时,可以执行下面的命令,以使MongoDB在后台创建索引,这样的创建时就不会阻塞其他操作。但是相比而言,以阻塞方式创建索引,会使整个创建过程效率更高,但是在创建时MongoDB将无法接收其他的操作。

db.user.ensureIndex({"username":1},{"background":true})

使用 explain

explain是非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节。explain会返回一个文档,而不是游标本身。如:
image.png

explain会返回查询使用的索引情况,耗时和扫描文档数的统计信息。

explain(“executionStats”) 查询具体的执行时间

db.tablename.find().explain( "executionStats" ) 
关注输出的如下数值:explain.executionStats.executionTimeMillis

Mongoose 创建索引

Mongoose 在定义 Schema 的时候可以指定索引:

  • 唯一索引
  • 普通索引
var DeviceSchema = new mongoose.Schema({
  sn: {
    type: Number,
    // 唯一索引
    unique: true
  },
  name: {
    type: String,
    // 普通索引
    index: true
  }
});

Mongoose 内置的 CURD 操作

  • Model.deleteMany()
  • Model.deleteOne()
  • Model.find()
  • Model.findById()
  • Model.findByIdAndDelete()
  • Model.findByIdAndRemove()
  • Model.findByIdAndUpdate()
  • Model.findOne()
  • Model.findOneAndDelete()
  • Model.findOneAndRemove()
  • Model.findOneAndUpdate()
  • Model.replaceOne()
  • Model.updateMany()
  • Model.updateOne()

Mongoose 扩展 CURD,定义静态和实例方法

在 Schema 上定义静态和实例方法。

var mongoose=require('./db.js');
var UserSchema=mongoose.Schema({
  name:{
    type:String
  },
  age:Number,
  status:{
    type:Number,
    default:1
  }
  })
  // 静态方法,推荐  注意使用 statics
  UserSchema.statics.findByUid=function(uid,cb){
    this.find({"_id":uid},function(err,docs){
    cb(err,docs)
  })
  }
  // 实例方法 注意使用 methods
  UserSchema.methods.print = function(){
    console.log('这是一个实例方法');
    console.log(this);
};
module.exports=mongoose.model('User',UserSchema,'user');

Mongoose 数据校验

Mongoose 校验参数

  • required : 表示这个数据必须传入
  • max: 用于Number 类型数据,最大值
  • min: 用于Number 类型数据,最小值
  • enum:枚举类型,要求数据必须满足枚举值enum: [‘0’, ‘1’, ‘2’]
  • match:增加的数据必须符合match(正则)的规则
  • maxlength:最大值
  • minlength:最小值
var UserSchema = new mongoose.Schema({
  name:{
    type:String,
    required: true,
  },
  age: {
    type: Number,
    // 是否必须的校验器
    required: true,
    // 数字类型的最大值校验器
    max: 120,
    // 数字类型的最小值校验器
    min: 0
  },
  status: {
    type: String,
    // 设置字符串的可选值
    enum: ['0', '1', '2']
  },
  phone:{
    type:Number,
    match: /^\d{11}$/
  },
  desc: {
    type: String,
    maxlength:20,
    minlength:10
  }
});

Mongoose 自定义验证器

通过 validate 字段自定义验证器。

var UserSchema = new mongoose.Schema({
  name:{
    type:String,
    required: true,
  },
  age: {
    type: Number,
    // 是否必须的校验器
    required: true,
    // 数字类型的最大值校验器
    max: 120,
    // 数字类型的最小值校验器
    min: 0
  },
  status: {
    type: String,
    // 设置字符串的可选值
    enum: ['0', '1', '2']
  },
  phone:{
    type:Number,
    match: /^\d{11}$/
  },
  desc: {
    type: String,
    // 自定义的验证器,如果通过验证返回true,没有通过则返回false
    validate: function(desc) {
      return desc.length >= 10;
    }
  }
});