node - mongose.js

947 阅读3分钟

Mongoose介绍

Mongoose.js中文网

面对这样的困境, 编写MongoDB验证,转换和业务逻辑是非常麻烦的. 所以发明了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'));

Mongoose为模型提供了一种直接的,基于scheme结构去定义你的数据模型。它内置数据验证, 查询构建,业务逻辑钩子等,开箱即用

Mongoose是一个优雅的NodeJS对象文档模型Object Document Module,通过关系型数据库的思想来设计非关系型数据库,它基于mongodb驱动,简化操作

image-20211017204313816

Mogoose对mongodb做了大量的封装

  • Scheme 对应了表
  • Module 对应了数据库集合
  • Documents 文档对象

初始化&安装

npm init -y
npm i express mongodb mongoose -s

使用

// 引入模块
const mongoose = require('mongoose')
// 连接、配置数据库,返回一个待定的连接
mongoose.connect('mongodb://192.168.31.107:27017', { 
  user: 'maxuan',
  pass: '601109',
  dbName: 'blog',
  autoIndex: false,
  useNewUrlParser: true
})
// 获取数据库对象
const db = mongoose.connection
// 连接失败的警告
db.on('error', console.error.bind(console, 'connection error:'))
// 连接成功
db.once('open',async ()=> {
  // we are connected!
  // 定义一个Schema = 表
  const blogSchema = new mongoose.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
    }
  })
  // 把Schema编译为一个模型
  const blogs = mongoose.model('blogs', blogSchema)
  
  // 通过模型对数据库的集合进行操作
  
})

在配置连接后面配置数据库名称,如果不填写,会自动生成test数据库

在连接最新版mongodb时会有一个报错,需要添加 useUnifiedTopology: true 配置支持

mongoose没有insertOne()方法,插入一条数据就在数组内传入一个对象

创建的集合名会被转为全小写,如果名字为复数的保持不变,如果为单数,则自动判断单词添加s或者es

运行 server.js,打印数据插入成功,刷新数据库,集合创建完成,数据插入成功

image-20211017233038061

以下增删改查操作都要在 db.once('open',async ()=> { } 内的模型下完成

Query API文档

Mongoose 5.0 中文文档 - Query

增加

await blogs.insertMany([
  {
    title: 'Vue',
    author: 'youyuxi',
    body: 'Vue是一个轻量级的高效的前端框架',
    comments: [
      {
        body: '说得对',
        date: new Date()
      }
    ],
    hidden: true,
    meta: {
      votes: 10,
      favs: 200
    }
  },
  {
    title: 'React',
    author: 'facebook',
    body: 'React是一个更厉害的前端框架',
    comments: [
      {
        body: '说得不完全对',
        date: new Date()
      }
    ],
    hidden: false,
    meta: {
      votes: 20,
      favs: 400
    }
  }
])
console.log('数据插入成功');

查询

let r = await blogs.find({author: 'youyuxi'})
console.log(r)
/*
[
  {
    meta: { votes: 10, favs: 200 },
    _id: new ObjectId("616c477f330f6921c48c5baf"),
    title: 'Vue',
    author: 'youyuxi',
    body: 'Vue是一个轻量级的高效的前端框架',
    comments: [ [Object] ],
    hidden: true,
    date: 2021-10-17T15:55:43.727Z,
    __v: 0
  }
]
*/

这里获取到的id如果是new ObjectId,需要前端通过 r[0]._id.toHexString() 转换为字符串,将这个字符串返回服务器可用于查询或其他操作

调用mongoose的查询 and() api方法

let r = await blogs.find().and([
  {
    author: 'facebook'
  },
  {
    title: 'Vue'
  }
])
console.log(r);

更新

更新并返回更新状态

r = await blogs.where({author: 'youyuxi'}).updateOne({title: 'Vue.js'})
console.log(r)
/*
{ 
	acknowledged: true,
  modifiedCount: 1,  upsertedId: null,
  upsertedCount: 0,
  matchedCount: 1
}
*/

更新并返回更新结果

r = await blogs.findOneAndUpdate({author: 'youyuxi'},{$set:{title: 'Vue.js'}})
console.log(r);
/*
{
  meta: { votes: 10, favs: 200 },
  _id: new ObjectId("616c4898ab1f6d1fc43fced6"),
  title: 'Vue.js',
  author: 'youyuxi',
  body: 'Vue是一个轻量级的高效的前端框架',
  comments: [
    {
      body: '说得对',
      date: 2021-10-17T16:00:24.138Z,
      _id: new ObjectId("616c4898ab1f6d1fc43fced7")
    }
  ],
  hidden: true,
  date: 2021-10-17T16:00:24.148Z,
  __v: 0
}
*/

删除

r = await blogs.deleteOne({author: 'youyuxi'})
console.log(r);
// { deletedCount: 1 }

定义方法

实例方法

定义在Schema对象上的方法,this后需要通过 model('blogs') 调用模型对象,必须定义在编译blogs模型前面,且因为this指向不能用箭头函数

使用的时候要new一个模型实例,并配置实例方法find内对应的参数,然后在模型实例上调用这个方法

blogSchema.methods.findAuthor = function() {
  // 返回一个对模型查询的条件
  return this.model('blogs').find({author: this.author}).exec()
}

// 把Schema编译为一个模型
const blogs = mongoose.model('blogs', blogSchema)
// 获取模型实例并配置查询参数
let b = new blogs({author: 'youyuxi'})
// 调用实例方法返回结果
r = await b.findAuthor()
console.log(r);

/*
[
  {
    meta: { votes: 10, favs: 200 },
    _id: new ObjectId("616c4898ab1f6d1fc43fced6"),
    title: 'Vue',
    author: 'youyuxi',
    body: 'Vue是一个轻量级的高效的前端框架',
    comments: [ [Object] ],
    hidden: true,
    date: 2021-10-17T16:00:24.148Z,
    __v: 0
  }
]
*/

__v: 0 — 用来在 save 文档时作为一个查询条件,以免在从「取出数据」到「save 数据」这段时间里,数据被其他进程修改,导致最后修改出现冲突

此处用的是find,获取到的结果可能是多个所以返回的是个数组,如果目标是返回提个唯一的结果,使用findOne

定义实例方法后的exec(callback)方法,作用是在获取到数据调用回调函数,这个方法可用于

静态方法

定义在模型上的方法,模型对象上直接调用静态方法并传入参数,返回给 r,必须定义在编译blogs模型前面,且因为this指向不能用箭头函数

blogSchema.statics.findTitle = function(title) {
    return this.findOne({title: title}).exec()
  }

const blogs = mongoose.model('blogs', blogSchema)

r = await blogs.findTitle('Vue')
console.log(r);

虚拟属性

虚拟属性是使用在返回结果r对象上的处理函数,相当于Vue的计算属性,是对返回结果进行处理的

blogSchema.virtual('getContent').get(function() {
  return `${this.author}发布了一个叫${this.title}的前端框架`
})
// 把Schema编译为一个模型
const blogs = mongoose.model('blogs', blogSchema)
r = await blogs.findTitle('Vue')
// ------------------------↓↓↓↓↓↓↓
console.log(r.getContent)
// youyuxi发布了一个叫Vue的前端框架