Skip to content

mongoose操纵MongoDB

一、mongoose是什么?

Mongoose是MongoDB的一个对象模型工具,是基于node-mongoldb-native开发的MongoDB nodes驱动,可以在异步的 环境下执行。同时它也是针对MongoDB操作的一个对象模型库,封装了MongoDB对文档的的一些增删改查等常用方法,让Node JS操作Mongodb数据库变得更加灵活简单。

简单的理解:Mongoose就是一个ORM,专门针对mongodb数据库操作的,类似于Sequelize。

Mongoose中文官网:http://www.mongoosejs.net/

二、Mongoose能做什么?

Mongoose因为封装了对MongoDB对文档操作的常用处理方法,让NodeJS操作MongoDB数据库变得更加容易。 Mongoose为模型提供了一种直接的,基于scheme结构去定义你的数据模型。它内置数据验证, 查询构建,业务逻辑钩子等,开箱即用。

三、node中使用mongoose连接并操纵mongoDB

大致步骤如下:

  • npm安装Mongoose,并引入
  • 设置集合规则Schema
  • 将Schema 编译成一 Model
  • 用编译成的Model对MondoDB进行操作

注意:

  • 在MongoDB中不需要显式创建数据库,如果正在使用的数据库不存在,MongoDB会自动创建。
  • Mongoose 的一切始于 Schema。每个 schema 都会映射到一个 MongoDB collection ,并定义这个collection里的文档的构成。

简单理解:scheme就是sequelize里面的模型model定义的规则,用来定义数据库表(mongodb里面称为集合)结构骨架等信息,通过schema创建集合

(一)安装mongoose

首先得安装mongoDB数据库,并开启服务。

bash
$ npm install mongoose --save

接着,新建db.js文件,引入,mongoose

js
/* db.js */

// 引入mongoose
const mongoose = require('mongoose');

(二)mongoose连接mongoDB

1、连接没有密码的数据库

mongoose.connect("mongodb://localhost/blog")

例如:

js
mongoose.connect("mongodb://localhost/blog") 
  .then(() => console.log("数据库连接成功"))
  .catch((err) => console.log("数据库连接失败", err));

2、连接有密码的数据库

mongoose.connect("mongodb://用户名:密码@localhost/blog")

例如:

js
mongoose.connect("mongodb://localhost/blog")
mongoose.connect("mongodb://localhost/blog") 
  .then(() => console.log("数据库连接成功"))
  .catch((err) => console.log("数据库连接失败", err));

MongoDB设置密码请查看另一篇文章《MongoDB的安装配置与卸载》

说明:

  • 1、blog是我们自定义的数据库名字(若blog不存在会自动创建)
  • 2、27017是mongoDB数据库的默认端口,可以省略不写
  • 3、方法可以传入一个回调函数,也可以不穿,不传返回的是Promise

(三)设置集合规则Schema

Schema是一个对象,用来规定集合(表)的列由哪些字段构成,并且这列的一些存储规则

mongoose下有一个构造函数Schema, 通过实例化这个构造函数,传入相应的字段并设置规则,构造函数就会返回我们创建好的schema

1、创建schema示例

例如:我们要创建一个用户集合

js
// 设置集合规则
const userSchema = new mongoose.Schema({
  // 直接指定name字段的数据类型
  name: String,

  // 也可以通过对象的形式定义的更详细
  role: {
    type: String,
    required: true, // 必填
    required: [true, "role必填"], // 字段必填,并自定义检验提示信息,当检验不通过时候,error.messag内返回该信息
    default: "默认值", // 默认值, 如果是函数,函数返回值为默认值
    // select:"", // 布尔值 指定 query 的默认 projections
    validate: {
      //自定义检验规则
      validate: {
        validator: function (v) {
          return v.length > 5;
        },
        message: "role字段长度不符合规则",
      },
    },
    get: (v) => Math.round(v), // 获取器
    set: (v) => Math.round(v), // 设置器
    /* 
    字符串 仅mongoose >= 4.10.0。 
    aliase 是一种特殊的虚拟值,它的 getter 和 setter 会无缝链接到另一个值。
    这是一个节省带宽的做法, 你可以储存一个更短的属性名到数据库,同时在调用的时候保持可读性。
    */
    alias: "a",
  },
  tags: [String],
  insertTime: { type: Date, default: Date.now },
  isPublished: Boolean,
});

2、mongoose 的常用合法 SchemaTypes:

  • String 字符串
  • Number 数值
  • Date 日期
  • Buffer 二进制(图片、音视频等文件)
  • Boolean 布尔值
  • Mixed 一个啥都可以放的 SchemaType , 虽然便利,但也会让数据难以维护
  • ObjectId 要指定类型为 ObjectId,在声明中使用 Schema.Types.ObjectId
  • Array 数组
  • enum 枚举值

更多关于SchemaTypes查看官网:http://www.mongoosejs.net/docs/schematypes.html

(四)通过schema创建模型

上一步我们创建了用户集合规则,现在可以用这个规则来创建一个集合,具体代码如下

js
// user是我们定义的集合名称,会转成复数,userSchema是上面定义的集合规则,返回模型的构造函数User
const User = mongoose.model("user", userSchema);

需要注意:

  • 1、mongoose.model方法返回一个模型构造函数,我们可以用这个构造函数上的一些方法来对数据库进行操纵。
  • 2、这里model的第一个参数是指定我们要创建的集合名称,mongoose会自定将其变成复数(加上S),如 user--->users
  • 3、当用mongoose对集合User进行CURD时候,若检测到没有数据库或没有集合,mongoose会自动创建数据库,mongoose.model方法会在数据库blog下创建一个集合users

至此,我们可以用模型来对数据库进行操作了

image.png

(五)用模型对数据库进行操作

image.png

1、新增数据(创建文档)

方式一:

js
// 1. 创建集合类的实例
const users = new User({ 
    name: '张三',
    role: '管理员',
    tags: ['nodejs', 'html'],
    isPublished: true
});
// 2. 保存实例
users.save();

方式二(推荐):

js
// 创建并保存

 a、回调函数形式

//直接调用User模型上的静态方法create
User.create({name: "李四",role: "管理员",tags: ["css", "html"],isPublished: true,},(err, doc) => {
    //  错误对象
    console.log(err);
    //  当前插入的文档
    console.log(doc);
});

b、Promise的形式(推荐)

// Model.create还支持promise 可以写成下面的形式,或用async await
User.create({name: "王五",role: "超级管理员",tags: ["nodejs", "html"],isPublished: true,})
  .then((doc) => console.log(doc))
  .catch((err) => console.log(err));

打开数据库图形化工具MongoDBCompass,可以看到刚刚添加的三条数据 image.png

2、修改

js
// 修改单个
User.updateOne({name:"张三"}, {name:"张三1",role:"超级管理员"}).then(result => console.log(result))

// 批量修改
User.updateMany({查询条件}, {要更改的值}).then(result => console.log(result))

// 根据id更新
Course.findByIdAndUpdate(id, {
    $set: {
        author: 'mosh',
        isPublished: false
    }
}, err => {})

3、删除

js
// 删除单个 findOneAndDelete 只会删除一个如果有多条数据 就删除第一个
Course.findOneAndDelete({}).then(result => console.log(result))

// 删除多个 deleteMany  特别注意:传入{}则删除所有
User.deleteMany({}).then(result => console.log(result))

4、查询

  • findOne
  • find find方法返回的是一个数组,findOne返回的是一个对象或null
js
// 查所有
Model.find({
    name: '张三',
    isPublished: true,
    age:{ // age大于20且小于30
        $gt:20, 
        $lt:30  
    },
    title:{  // title包含"打豆豆"的
        $in:['打豆豆']
    }
})
.limit(10)  // 每页几条
.skip(1)    // 跳过几条,类似于 offset
.sort({name: 1}) // 1 按name升序  -1 按name降序
.select("name tag _id") // 只是展示name tag _id字段
.then(res=>{})
.catch(err=>{})

/* 模糊匹配: */
const { name } = params; // 查询条件参数
if (name) {
  let reg = new RegExp(name,'i');
  params.name = reg; 
}
console.log(params);
let res = await service.tag.findAll(params);



// 查某条
Model.findOne({_id:"61ab6fb1ad5d812392682aed"}).then(res=>{}).catch(err=>{})

5、模型关联

https://www.cnblogs.com/galaxy2490781718/p/13374749.html 关联步骤:

  • 1、在创建Schema 时定义联合表和id
  • 2、使用populate方法进行关联查询

1、在创建Schema 时定义联合表和id

js
var userSchema=new Schema({
	id:ObjectId,
	name:String,
	age:{type:Number,default:0},
	role:[ {
        type: ObjectId, // 字段类型是ObjectId(可以理解为存的是roles表的_id值)
        ref: 'Role'     // 关联Role模型
    }]
})
var User=mongoose.model('user',userSchema)

注意:

  • 这里用数组对象最好,如果直接是对象,表现出来就是一对一的关系 。
  • 如果是数据就是一对多的关系 。

2、使用populate

js
User.find().populate('role').then(res=>{})

注意:

  • 没有populate方法的find()会显示role字段绑定id字符串,加上populate就可以将role关联查找出来,把id转换成对应关联表的对象。

问题:

1.设置了获取器但是在获取数据时候不起作用?

解决: 加上 schema.set("toJSON", { getters: true, virtuals: true });

image.png

js
const moment = require("moment");
module.exports = (app) => {
  const mongoose = app.mongoose;
  const Schema = mongoose.Schema;

  const TagSchema = new Schema({
    name: {
      type: String,
      required: [true, "标签名称必填"],
    },
    desc: String,
    status: {
      type: Boolean,
      default: true,
    },
    // 创建时间
    insertTime: {
      type: Date,
      default: Date.now,
      get: (v) => moment(v).format("YYYY-MM-DD"),
    },
    createUser: String, // 创建人
    updateUser: String, // 更新人
    updateTime: {
      type: Date,
      get: (v) => {
        // 更新时间
        return v ? moment(v).format("YYYY-MM-DD") : null;
      },
    },
  });

  TagSchema.set("toObject", { getters: true, virtuals: true }); // toObject时能够转换
  TagSchema.set("toJSON", { getters: true, virtuals: true }); // toJson时能够转换


  return mongoose.model("Tag", TagSchema);
};

2.mongoose来查询某字段的时候,如何返回指定字段

使用MongoDB的时候需要只查询指定的字段进行返回,也就是类似mysql里面的 SELECT id,name,age 这样而不是SELECT *。在MongoDB里面映射(projection)声明用来限制所有查询匹配文档的返回字段。projection以文档的形式列举结果集中要包含或者排除的字段。可以指定要包含的字段(例如:{field:1})或者指定要排除的字段(例如:{field:0})。默认_id是包含在结果集合中的,要从结果集中排除_id字段,需要在projection中指定排除_id字段({_id:0})。除了_id字段,不能在一个projection中联合使用包含和排除语意。

1)返回所有匹配文档的所有字段

如果没有指定projection,find()方法返回所有匹配文档的所有字段。如下:

js
Model.find({status:true})

将返回Model中status为true的所有数据,是一个数组

2)返回指定字段和_id字段:

一个projection可以明确地指定多个字段。下面的操作中,find()方法返回匹配的所有文档。在结果集中,只有name和age字段,默认_id字段也是返回的。

js
Model.find({status:true},{name:1,age:1})

可以通过在projection中指定排除_id字段将其从结果中去掉,如下例子所示:

js
Model.find({status:true},{name:1,age:1,_id:0})

3)返回除排除掉以外的字段:

这个操作返回所有status为true的文档,在结果中sex字段不返回,其余字段全部返回。

js
Model.find({status:true},{sex:0})

4)数组字段的projection:

$elemMatch$slice运算符是对数组进行projection的唯一途径。