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'});