Mongoose 10min快速入门

267 阅读2分钟

连接 mongodb

const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017").then(() => {
  console.log("连接成功");
});

创建 schema 并生成 model

const userSchema = new mongoose.Schema({
  name: String,
  age: Number,
});

// create model based on schema
module.exports = mongoose.model("User", userSchema);

使用 model 创建新的 document

方法一:

// 创建一个新的user document,但此时并未存储到数据库中
const user = new User({ name: "jiaqi", age: 26 });
// 将新创建的user document存储到数据库中
await user.save(); // 这个是异步的

方法二:

const user = await User.create({ name: "kyle", age: 36 });

如果创建 document 后想要更新字段可以这样:

const user = await User.create({ name: "kyle", age: 36 });
// 想要给user改一个名字
user.name = "jiaqi";
// 然后保存到数据库中
await user.save();

常见的 schema 类型以及验证

const userSchema = new mongoose.Schema({
  name: String,
  age: Number,
  email: String,
  createdAt: Date,
  updatedAt: Date,
  bestFriend: mongoose.Schema.Types.ObjectId,
  hobby: [String],
  address: {
    street: String,
    city: String,
  },
});

如果不想 address 是这种嵌套的对象,可以单独为它定义一个 schema

const addressSchema = new mongoose.Schema({
  street: String,
  city: String,
});

const userSchema = new mongoose.Schema({
  name: String,
  age: Number,
  email: String,
  createdAt: Date,
  updatedAt: Date,
  bestFriend: mongoose.Schema.Types.ObjectId,
  hobby: [String],
  address: addressSchema,
});

mongoose 里面内置了很多配置,如minmaxmaxLengthrequireduniqueimmutabledefault。还可以自定义 validator 进行验证。

const addressSchema = new mongoose.Schema({
  street: String,
  city: String,
});

const userSchema = new mongoose.Schema({
  name: String,
  age: {
    type: Number,
    min: 1,
    max: 100,
    validate: {
      validator: function (v) {
        // 判断年龄是否大于18岁
        return v > 18;
      },
      message: props => `${props.value} is not a valid age, must be greater than 18`
    }
  },
  email: {
    type: String,
    required: true,
    maxLength: 20, //最大长度
    lowercase: true, //会自动将email转换为小写
  },
  createdAt: {
    type: Date,
    immutable: true, //不可更改
    default: Date.now()
  },
  updatedAt: {
    type: Date,
    default: Date.now()
  },
  bestFriend: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  hobby: [String],
  address: addressSchema
})

但是不管是内置的还是自定义的验证,这些验证只会在执行了createsave方法后执行。 但是 update 等方法,并没有调用 create 和 save 函数,导致没有执行这些验证。

query

findById:

const user = await User.findById("62bb1a173826d18e9fa0f0f7");

find:

可以根据指定字段进行查询,$gt 代表大于。查询名字为 jiaqi,且年龄大于 18

const user = await User.find({ name: "jiaqi", age: { $gt: 18 } });

findOne:

和 find 一样,但是只返回第一个匹配的 document

const user = await User.findOne({ name: "jiaqi", age: { $gt: 18 } });

deleteMany:

deleteMany 删掉所有匹配的 document;deleteOne 删掉第一个被匹配的。

const result = await User.deleteMany({ name: "jiaqi", age: { $gt: 18 } });
console.log(result); //{ acknowledged: true, deletedCount: 42 }

还有很多query,比如updateOneupdateMany等等。

queries

.where 允许我们创建自己的 query。

let user = await User.where("name").equals("kyle"); //查找名字为kyle的user
user = await User.where("age").gt(18); // 查找年龄大于18的
user = await User.where("age")
  .gt(18)
  .where("name")
  .equals("kyle")
  .limit(2)
  .select("age"); //连在一起写

console.log(user);

如果之前定义好了bestFriend,然后并且写入一个bestFriend的id:

let user = await User.where('name').equals('kyle').limit(1);
user[0].bestFriend = '62bb24f2a966af6629d12282';
await user[0].save();
console.log(user[0]);

之后如果使用populate,就可以打印出关联的bestFriend信息。

let user = await User.where('name').equals('kyle').populate('bestFriend').limit(1);
console.log(user[0]);

{
  _id: new ObjectId("62bb1a6269fdf0942a5621ea"),
  name: 'kyle',
  age: 36,
  __v: 1,
  bestFriend: {
    _id: new ObjectId("62bb24f2a966af6629d12282"),
    name: 'Jiaqi',
    email: 'test@qq.com',
    createdAt: 2022-06-28T15:57:38.508Z,
    updatedAt: 2022-06-28T15:57:38.508Z,
    hobby: [],
    __v: 0
  },
  email: '2473487465@qq.com',
  hobby: [],
  updatedAt: 2022-06-29T14:11:09.719Z
}

schema methods/ virtuals

实例上的methods

给schema添加methods,之后每个实例都可以调用

// 注意这里不是箭头函数,因为我们需要this指向user实例 
userSchema.methods.sayHi =function(){
  console.log(`Hi, I'm ${this.name}`);
}

// 调用
let user = await User.findOne({name:'Jiaqi'});
user.sayHi();

model上的methods

作用在model上的方法,不能作用在query上

// 给model添加静态方法
userSchema.statics.findByName = function (name) {
  // 让查找的时候不区分大小写
  return this.where({name:new RegExp(name, 'i')});
}

// 此时不再区分大小写,作用在model上
let user = await User.findByName('jiAqi');
// let user = await User.where().findByName('jiAqi'); (❌)
console.log(user);

给query添加方法

给query添加方法,是作用在query上的方法,不是作用在model上

userSchema.query.byName = function (name) {
  return this.where({ name: new RegExp(name, 'i') });
}
let user = await User.where().byName('jiAqi');
console.log(user);

virtuals

Virtuals are document properties that you can get and set but that do not get persisted to MongoDB.he getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage.

// 可以读取到nameAndEmail属性,但是不会存到数据库中
userSchema.virtual('nameAndEmail').get(function(){
  return `${this.name} <${this.email}>`;
})

let user = await User.findOne({ name: new RegExp('jiaqi', 'i') });
console.log(user.nameAndEmail); //Jiaqi <test@qq.com>

中间件

如果想要在某个操作之前执行中间件,可以使用pre,反之使用post

// 在保存到数据库中之前,会执行这个操作
userSchema.pre('save',function(next){
  this.updatedAt = Date.now();
  next();
})

let user = await User.findOne({ name: new RegExp('jiaqi', 'i') });
console.log(user);
await user.save();
console.log(user);

post的回调函数的参数和pre不同,第一个参数是document,第二个才是next

// 第一个参数数刚刚保存到数据的doument
userSchema.post('save',function(doc,next){
  console.log(doc);
  this.sayHi();
  next();
})