node中使用mongoose

施飞驰
2023-12-01

下载 mongoose

npm install mongoose

引入 mongoose 并连接数据库

const mongoose = require('mongoose')
const DB_URL = 'mongodb://localhost/mongoose-test' 

mongoose.connect(DB_URL,{useNewUrlParser:true,useUnifiedTopology:true }, (err) => {
  if (err) return console.log(err)
  console.log('数据库成功连接')
})

// 这种也可以看数据库是否连接成功
mongoose.connection.on('connected', () => console.log('数据库连接成功'))
mongoose.connection.on('error', () => console.log('数据库连接异常'))
mongoose.connection.on('disconnectied', () => console.log('数据库连接断开'))

定义Schema

数据库中的Schema,为数据库对象的集合。schema 是 mongoose 里会用到的一种数据模式,可以理解为表结构的定义;每个 schema 会映射到 mongodb 中的一个 collection,他不具备操作数据库的能力

// 设计 users 表(集合) 定义一个Schema Schema里面的对象和数据库表里面的字段需要一一对应
const UserSchema = new mongoose.Schema({
  name: String,
  age: Number,
  status: 'number'
})


/*
  定义数据库模型 操作数据库
  model里面的第一个参数 要注意:首字母大写、要和数据库 (集合) 名称对应
  这个模型会和模型名称相同的复数的数据库建立连接:如通过下面方法创建,那么这个模型将会操作 users 这个表(集合)
  复数 >==== 会给名字多加个s  如:User ===> users   Phone ===> phones
*/

const User = mongoose.model('User', UserSchema)		// 默认会操作users表
// const User = mongoose.model('User', UserSchmea, 'user')	// 默认会操作user表

// 查询
User.find({}, (err, doc) => {
  if (err) {
    console.log(err)
  } else {
    console.log(doc)
  }
})

查询数据

const User = mongoose.model('User', UserSchema)

// {查找 status 是1的} ,{只显示name和age, _id不显示},{找两条数据},(回调函数)
User.find({ status: 1 }, {name: 1, age: 1, _id: 0}, {limit: 2}, (err, doc) => {
  if (err) return console.log(err)
  console.log(doc)
})

User.find({}, (err, doc) => {
  if (err) {
    console.log(err)
  } else {
    console.log(doc)
  }
})

添加数据 单条添加

const User = mongoose.model('User', UserSchema)

/*
  先实例化 Model 通过实例化 User Model 创建添加的数据
  在实例 .save()
*/
const newUser = new User({	// 实例化模型 传入增加的数据
  name: '李四',
  age: 18,
  status: 1
})

// 第二个参数返回的是 添加成功的数据
newUser.save((err, v) => {
  if (err) return console.log(err)
  console.log(v)
  console.log('添加成功')
})

添加数据 多条添加

const User = mongoose.model('User', UserSchema)

// 定义数据
let arr = [
  { name: '小猪', age: 13, status: 0 },
  { name: '小明', age: 32, status: 1 },
  { name: '小红', age: 20, status: 1 },
  { name: '小赖', age: 22, status: 1 },
  { name: '小狗', age: 50, status: 1 },
  { name: '小力', age: 10, status: 0 }
]

// 使用 insertMany 插入多条数据
// doc 就是你添加成功之后给你返回的数据
User.insertMany(arr, (err, doc) => {
  if (err) return console.log(err)
  console.log('插入成功')
  console.log(doc)
})

// 方法2
let newArr = new Array()

arr.forEach((value, index) => {
  const info = new User(value)
  newArr.push(info)
})

User.insertMany(newArr, (err, doc) => {
  if (err) return console.log('数据添加失败')
  console.log(doc)
  console.log('数据添加成功')
})

修改数据

const User = mongoose.model('User', UserSchema)

// 把name的值是小四的字段里,age的值改成14
User.updateOne({
  'name': '小四'
}, {
  'age': 14
}, (err, res) => {
  if (err) return console.log('更新失败')
  console.log(res)
  console.log('更新成功')
})

删除数据

const User = mongoose.model('User', UserSchema)

// 删除一个status是0的
User.deleteOne({ status: 0 }, (err, doc) => {
  if (err) return console.log(err)
  console.log(doc)
})

// 删除所有 status是0的
User.deleteMany({ status: 0 }, (err, doc) => {
  if (err) return console.log(err)
  console.log(doc)
})

Getters与Setters 自定义修饰符

Getters:

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    get(params) {
      return '测试' + params
    }
  },
  age: Number,
  status: 'number'
})

const User = mongoose.model('User', UserSchema)
const newUser = new User({
  name: '小龙',
  age: 18,
  status: 1,
  url: 'https://www.baodu.com'
})
console.log(newUser.name)  // 测试小龙

Setters:

const UserSchema = new mongoose.Schema({
  name: {
    type: String
  },
  age: Number,
  status: 'number',
  url: {
    type: String,
    set(parmas) {
      if (!parmas) return ''
      const isHttp = parmas.indexOf('http://')
      const isHttps = parmas.indexOf('https://')
      if (isHttp !== 0 && isHttps !== 0) return 'http://' + parmas
      return parmas
    }
  }
})

const User = mongoose.model('User', UserSchema)
const newUser = new User({
  name: '小龙',
  age: 18,
  status: 1,
  url: 'www.baodu.com'
})
console.log(newUser)
/*
{
  name: '小龙',
  age: 18,
  status: 1,
  url: 'http://www.baodu.com',
  _id: new ObjectId("61fb5217081f1e60d8711b0e")
}
*/

mongoose 索引

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

mongoose 中除了以前创建索引的方式,我们也可以在定义Schema的时候指定创建索引

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

Model 的静态方法和实例方法

静态方法

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


// 封装静态查询方法
UserSchema.statics.findByAge = function(age, callback) {
  this.find({age}, (err, docs) => {
    callback(err, docs)
  })
}

const User = mongoose.model('User', UserSchema)

User.findByAge('30', (err, res) => {
  if (err) return console.log(err)
  console.log(res)
})

实例方法 (基本不用)

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

// 实例方法
UserSchema.methods.print = function() {
  console.log(this)
}

const User = mongoose.model('User', UserSchema)

const user = new User({
  name: '小刘',
  age: 18,
  status: 1
})

user.print()
/*
{
  name: '小刘',
  age: 18,
  status: 1,
  _id: new ObjectId("61fb6b16f14ac45e7d3d9b25")
}
*/

Mongoose 数据校验

mongoose数据校验是指用户通过mongoose给mongodb数据库增加数据的时候,对数据的合法性进行的验证。

在mongoose里面定义Schema的时候,通过设置字段类型,修饰符、默认参数 、数据校验等都是为了数据库数据的一致性。

校验参数

属性方法说明
require表示这个数据必须传入
max用于Number类型数据,最大值
min用于Number类型数据,最小值
enum枚举类型,要求数据必须满足枚举值 enum: [‘0’, ‘1’, ‘2’]
match增加的数据必须符合 match (正则) 的规则
maxlength最大值
minlength最小值
validate(v)自定义验证
trim去除两边的空格
const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,    // 必须传入
    trim: true,		   // 修饰符 去除两边空格
    maxlength: 8,      // 最大8位
    minlength: 2,      // 最小2位
  },
  age: {
    type: Number,
    max: 100,          // 最大值是100
    min: 8             // 最小值是8
  },
  status: {
    type: Number,
    enum: [0, 1]      // 只能传入0和1
  },
  url: {
    type: String,
    match: /^http:\/\/(.*)/i
  }
})

const User = mongoose.model('User', UserSchema)

const user = new User({
  name: 'zss',
  age: 12,
  status: 0,
  url: 'http://'
})
user.save()
/*
  name值为空
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: name: Path `name` is required.\
*/

/* 
  name长度小于2
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: name: Path `name` (`z`) is shorter than the minimum allowed length (2).
*/

/*
  name长度大于8
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: name: Path `name` (`zsdsadsadas`) is longer than the maximum allowed length (8).
*/

/*
  status只能传入0和1
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: status: `50` is not a valid enum value for path `status`.
*/

/*
  age最大值是100,最小值是8
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: age: Path `age` (102) is more than maximum allowed value (100).
*/

/*
  url必须以http://开头
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: url: Path `url` is invalid (http:/).
*/

自定义校验

const UserSchema = new mongoose.Schema({
  num: {
    type: Number,
    validate(value) {
      return value % 2 === 0
    }
  }
})

const User = mongoose.model('User', UserSchema)

const user = new User({
  num: 7
})
user.save()

/*
  num 必须是偶数
  UnhandledPromiseRejectionWarning: ValidationError: User validation failed: num: Validator failed for path `num` with value `7`
*/

Mongoose 中使用 aggregate 聚合管道

$ne:表示not equals 就是不等于的意思

查询某字段不为空的数据
db.user.find({fieldName: {$ne:null}})

查询字段等于空的数据
db.user.find({fieldName: {$eq:null}})

$exists:表示是否存在。值为false表示不存在,值为true表示存在

查询某字段不为空的数据
db.user.find({fieldName:{$exists:true}})

查询某字段不存在的数据
db.user.find({fieldName:{$exists:false}})

mongoose中获取ObjectID

引入 mongoose 使用 mongoose.Types.ObjectId('xczxcxzczxcz3234')

const User = mongoose.model('User', UserSchema)

User.aggregate([
  {
    $lookup: {
      from: 'phones',
      localField: 'phone_id',
      foreignField: '_id',
      as: 'user_phone'
    }
  },
  {
    $match: {
      'phone_id': { $exists: true }
    }
  }
], (err, docs) => {
  console.log(docs)
})

多表关联查询

/*
	from: user	这是要关联 user 表
	localField  这是要跟 article 表里的 classify_id 字段进行关联
	foreignField 这是要跟 user 表里的 _id字段进行关联
	as			 这是别名,就是给这个新的数据起个名
*/

const { Article } = require('./db')
// 要关联多个表就继续往下写 { $lookup: {...} }
Article.aggregate([
  {
    $lookup: {
      from: 'articlecate',
      localField: 'classify_id',
      foreignField: '_id',
      as: 'cate'
    }
  },
  {
    $lookup: {
      from: 'user',
      localField: 'author_id',
      foreignField: '_id',
      as: 'user'
    }
  }
], (err, docs) => {
  console.log(JSON.stringify(docs))
})

多表关联查询结果

[
	{
		// 这个是article表
		"_id": "61fb9de67d8a21e93c2eaad2",
		"title": "第一个科技文章",
		"classify_id": "61fb9619215153325d17a193",
		"author_id": "61fb9bdee12c49d0f430d216",
		"author_name": "张三",
		"description": "这是张三发布的第一个科技文章,属于科技分类",
		"content": "这是科技 文章的内容,此处省略一万字",
		"__v": 0,
		// article表 关联了 articlecate表 并且起名叫cate
		"cate": [{
			"_id": "61fb9619215153325d17a193",
			"title": "科技新闻",
			"description": "科技新闻的描述。。。",
			"__v": 0
		}],
		// article表 关联了 user表 并且起名叫user
		"user": [{
			"_id": "61fb9bdee12c49d0f430d216",
			"username": "zhangsan",
			"password": "123456",
			"name": "张三",
			"age": 18,
			"sex": "男",
			"tel": 14895466411,
			"__v": 0
		}]
	},

	{
		"_id": "61fb9de67d8a21e93c2eaad3",
		"title": "第二个科技文章",
		"classify_id": "61fb9619215153325d17a193",
		"author_id": "61fb9bdee12c49d0f430d216",
		"author_name": "张三",
		"description": "这是张三发布的第二个科技文章,属于科技分类",
		"content": "这是科技文章的内容,此处省略一万字",
		"__v": 0,
		"cate": [{
			"_id": "61fb9619215153325d17a193",
			"title": "科技新闻",
			"description": "科技新闻的描述。。。",
			"__v": 0
		}],
		"user": [{
			"_id": "61fb9bdee12c49d0f430d216",
			"username": "zhangsan",
			"password": "123456",
			"name": "张三",
			"age": 18,
			"sex": "男",
			"tel": 14895466411,
			"__v": 0
		}]
	},

	{
		"_id": "61fb9de67d8a21e93c2eaad4",
		"title": "第一个金融文章",
		"classify_id": "61fb96563188bc6d398ed013",
		"author_id": "61fb9bdee12c49d0f430d216",
		"author_name": "张三",
		"description": "这是张三发布的第一个金融文章,属于金融分类",
		"content": "这是金融文章的内容,此处省略一万字",
		"__v": 0,
		"cate": [{
			"_id": "61fb96563188bc6d398ed013",
			"title": "金融新闻",
			"description": "金融新闻的描述。。。",
			"__v": 0
		}],
		"user": [{
			"_id": "61fb9bdee12c49d0f430d216",
			"username": "zhangsan",
			"password": "123456",
			"name": "张三",
			"age": 18,
			"sex": "男",
			"tel": 14895466411,
			"__v": 0
		}]
	}
]

多表查询 populate

//Article表

 // 分类ID
  classify_id: {
    type: mongoose.Types.ObjectId,
    ref: 'ArticleCate'  // 关联的Model
  },
  // 作者ID
  author_id: {
    type: mongoose.Types.ObjectId,
    ref: 'User'
  },
// app.js

const { Article } = require('./db')

// populate('classify_id')   需要关联的字段
Article.find({}).populate('classify_id').populate('author_id').exec((err, docs) => {
  console.log(docs)
})

这是返回的数据,这里不能起别名,直接就覆盖字段了

  {
    _id: new ObjectId("61fb9e5735882d989cf8833a"),
    title: '第二个美食文章(小红)',
    classify_id: {
      _id: new ObjectId("61fb9645c342558ade4e1f3c"),
      title: '美食新闻',
      description: '美食新闻的描述。。。',
      __v: 0
    },
    author_id: {
      _id: new ObjectId("61fb9bdee12c49d0f430d219"),
      username: 'xiaohong',
      password: '123456',
      name: '小红',
      age: 18,
      sex: '女',
      tel: 15478932000,
      __v: 0
    },
    author_name: '小红',
    description: '这是小红发布的第二个美食文章,属于美食分类',
    content: '这是美食文章的内容,此处省略一万字',
    __v: 0
  }

性能还是推荐使用 aggregate

 类似资料: