Mongoose

1. 什么是Mongoose?

mongoose是MongoDB的数据库的对象模型工具。可以通过操作在nodeJS中对mongoose的操作实现对数据库的操作。

背景知识:

ORM:Object Relational Mapping对象关系映射。

是将对数据库的操作映射成对象的操作。(mongoose是一种ORM)

ORM优点

1. 屏蔽数据库操作的细节

2.跨数据库平台

2. 如何实现Mongoose?

1. 创建映射对象

const mongoose = require('mongoose');
const conn = mongoose.createConnection('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});
// 错误监听
conn.on('error', function(err) {
    console.error(err);
});
// 连接监听
conn.on('open', function() {
    console.log('连接成功');
})

2. 定义Schema类

Schema类定义了字段的名称、类型、默认值

const UserSchema = new mongoose.Schema({
  username: String,
  password: String,
}, {collection: 'user'});// 第二个参数定义集合的名称

其中Schema的完整类型示例如下:

  const personSchema = new mongoose.Schema({
    name: {type: String, required: true}, // 字符串
    binary: Buffer, // 二进制
    living: Boolean, // 布尔值
    birthday: {type: Date, default: Date.now}, // 日期类型
    age: Number, //Number类型
    // ObjectId类型
    _id: Schema.Types.ObjectId, // 主键
    _fk: Schema.Types.ObjectId, // 外键;其他集合的主键
    //数组类型
    array: [], 
    arrOfString: [String], //字符串数组
    arrOfNumber: [Number], //数字数组
    arrOfDate: [Date], //日期数组
    arrOfBuffer: [Buffer], //Buffer数组
    arrOfBoolean: [Boolean], //布尔数组
    arrOfObjectId:[Schema.Types.ObjectId] // ObjectId数组
    // 内嵌文档
    nested: {
      name: String
    }
  })

3. 创建对象模型

const User = conn.model('User', UserSchema);

4. 创建实体对象

const zhangsan = new User({username: 'zhangsan', password: 1});
// 可以改成async函数
zhangsan.save(function(err, result) {
  if (!err) {
    console.log(result)
  }
})

5. 常见操作方法

let mongoose = require('mongoose');
let conn = mongoose.createConnection('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
}); 
const UserSchema = new mongoose.Schema({
  username: {type: String, required: true},
  age: Number,
  password: String,
  createAt: {type: Date, default: Date.now}
});
const User = conn.model('User', UserSchema);

(async function() {
  try{  
    // 插入文档
    let user1 = await User.create({username: "lyra", age: 18});
    let user2 = await User.create({username: "lee", age: 10});
    // 更新文档
    let updatedUser1 = await User.updateOne({username: 'lyra'}, {username: 'lyraLee'});
    let updatedUser2 = await User.updateMany({}, {password: '123456'});
    // 查询文档
    let findUser1 = await User.find({age: {$gte: 10, $lt: 12}});
    console.log(findUser1); // 返回一个数组
    let findUser2 = await User.findOne({});
    console.log(findUser2); // 返回一个对象
    let findUser3 = await User.findById({_id: '5e4d62bab945ba909211d280'});
    console.log(findUser3); // 返回一个对象
  } catch(err) {
    console.log(err);
  }
})();

6. 高级查询方法

1. 带外键查询

let mongoose = require('mongoose');
let ObjectId = mongoose.Schema.Types.ObjectId;
let conn = mongoose.createConnection('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
}); 

const UserSchema = new mongoose.Schema({
  username: {type: String, required: true},
  age: Number,
  password: String,
  createAt: {type: Date, default: Date.now}
}, {collection: 'user'}); //指定集合名称为user;否则默认users
const ArticleSchema = new mongoose.Schema({
  title: String,
  content: String,
  author: {type: ObjectId, ref: 'User'} //ref表明该字段为外键;对应模型名称
});

const User = conn.model('User', UserSchema);
const Article = conn.model('Article', ArticleSchema);

// 带外键查询
(async function() {
  const user = await User.create({username: 'lyra', age: 10, password: 1});
  await Article.create({title: '标题', content: '内容', author: user._id})
  // 查看文章的信息-包含作者的具体信息-populate的参数是外键对应的字段名称
  const article = await Article.findById('5e4d6c2eb8ba3590edada416').populate('author'); 
  console.log(article)
})()

2. 分页查询

// 分页查询
const pageSize = 3;
const pageNum = 2;
const users = [];
for(let i=0; i< 10; i++) {
  users.push({username: 'lyra'+i, password: i, age: 18+i})
}
(async function() {
  await User.create(users);
  User.find().sort({age: 1}).skip((pageNum-1)*pageSize).limit(pageSize).exec(function(err, result) {
    if(!err) {
      console.log(result);
    }
  })
})();

3. Mongoose模型扩展

1. 静态方法和实体方法

当某些逻辑存在重复使用的情况时,可以将其进行封装。

优点:

1. 当代码的逻辑修改时,不需要大面积修改原有代码。

2. 方法的使用者不用关心内部的实现逻辑。

扩展静态方法依据: 针对的整个集合(如:求年龄最大者);

const mongoose = require('mongoose');
const conn = mongoose.createConnection('mongodb://localhost:27017/school', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

const { Schema } = mongoose;
const UserSchema = new Schema({
  username: String,
  password: String,
  createAt: {type: Date, default: Date.now}
})
// 扩展Schema类静态方法;要位于model创建之前
UserSchema.statics.login = function(username, password) {
  return this.findOne({username, password});
}

const User = conn.model('User', UserSchema);
(async function() {
  await User.create({username: '1', password: '1'})
})();

// 静态方法校验
(async function(username, password) {
  const result = await User.login(username, password);
  console.log(result);
})('1', '1');

扩展实体方法依据: 针对的是个体(如:用户是否已经成年);

const mongoose = require('mongoose');
const conn = mongoose.createConnection('mongodb://localhost:27017/school', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

const { Schema } = mongoose;
const UserSchema = new Schema({
  username: String,
  password: String,
  createAt: {type: Date, default: Date.now}
})

// 扩展实体方法;要位于model创建之前
UserSchema.methods.login = function() {
  // 从实体获取模型的方法this.model('User')
  return this.model('User').findOne({username: this.username, password: this.password})
}

const User = conn.model('User', UserSchema);
(async function() {
  await User.create({username: '1', password: '1'})
})();

// 实体方法校验
(async function() {
  const user = new User({username: '1', password: '1'});
  const result = await user.login();
  console.log(result);
})()

2. 钩子-hook

在具体的操作实现前后添加一些操作的逻辑。

/**
 * 例如: 在注册的用户信息保存save()之前,需要将密码通过加盐算法进行加密
 */
const mongoose = require('mongoose');
const crypto = require('crypto');
const conn = mongoose.createConnection('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

const { Schema } = mongoose;
const UserSchema = new Schema({
  username: String,
  password: String,
  createAt: {type: Date, default: Date.now}
}, {collection: 'user'});
// save()前添加钩子
UserSchema.pre('save', function(next) {
  this.password = crypto.createHmac('sha256', 'lyra').update(this.password).digest('base64');
  next();
});
const User = conn.model('User', UserSchema);

(async function register(username, password) {
  const user = new User({username, password});
  user.save();
})('a', '1');

3. 虚拟属性-virtual

4. 插件-plugin

针对非自己创建的Schema,或者给定的Schema不允许直接操作时。

插件代码:

// schema是原始Schema; options是参数
module.exports = function(schema, options) {
  // 添加字段
  schema.add({lastModified: Date});
  // 钩子;每次更新前添加变更时间
  schema.pre('save', function(next) {
    this.lastModified = new Date();
    next();
  })
}

逻辑代码:

const mongoose = require('mongoose');
const conn = mongoose.createConnection('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});
const UserSchema = new mongoose.Schema({
  username: String,
  password: String,
}, {collection: 'user'});

// 插件用于在保持原有Schema不变的基础上;修改Schema
let plugin = require('./plugin');
UserSchema.plugin(plugin, {index: true});
const User = conn.model('User', UserSchema);
User.create({username: 'lyra'});

 

posted @ 2020-02-23 19:29  Lyra李  阅读(476)  评论(0编辑  收藏  举报