Mongoose的使用
4.1簡介
Mongoose是一個對象文檔模型(ODM)庫,它對Node原生的MongoDB模塊進行了進一步的優化封裝,並提供了更多的功能。
4.2優勢
1) 可以為文檔創建一個模式結構(Schema)
2) 可以對模型中的對象/文檔進行驗證
3) 數據可以通過類型轉換轉換為對象模型
4) 可以使用中間件來應用業務邏輯掛鈎
5) 比Node原生的MongoDB驅動更容易
4.3核心對象
4.3.1 Schema
模式(約束)對象,通過Schema可以對集合進行約束
4.3.2 Model
模型對象,相當於數據庫中的集合,通過該對象可以對集合進行操作
4.3.3 Document
文檔對象,它和數據庫中的文檔相對應,通過它可以讀取文檔的信息,也可以對文檔進行各種操作
4.4使用
4.4.1連接數據庫
npm init 先自動創建一個package.json文件
1) 下載安裝Mongoose
npm i mongoose --save
2) 引入Mongoose
var mongoose = require("mongoose");
3) 連接MongoDB數據庫
mongoose.connect("mongodb://ip地址:端口號/數據庫名");
const mongoose = require("mongoose"); //1.連接數據庫 // connect //第一個實參是數據庫地址URL //第二個實參是配置對象 //第三個實參是連接數據庫的回調參數 mongoose.connect("mongodb://localhost:27017/0318_mongoose_demo",{ useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex:true },function(err){ if(err){ console.log("連接數據庫失敗"); }else{ console.log("連接數據庫成功"); } }); // mongoose.connection.once("open",function(err){ // if(err){ // console.log("連接數據庫失敗"); // }else{ // console.log("連接數據庫成功"); // } // }) // 2.創建約束對象(Schema) let starsSchema = new mongoose.Schema({ name:{ type:String, //聲明當前字段數據類型 required:true, //聲明當前字段是否必傳 unique:true //聲明當前字段是否唯一 }, age:Number, sex:{ type:String, default:"未知" //聲明當前字段的默認值 }, roles:[String], //聲明當前字段數據類型為數組, 內部子元素的類型必須是String info:mongoose.Schema.Types.Mixed }) //3.創建模型對象(Model) //第一個實參->集合名稱 //第二個實參->約束對象 let starsModel = mongoose.model('stars',starsSchema); //4.創建文檔對象 let starDocument = new starsModel({ name:"彭於晏", age:18.01, sex:"猛男", roles:["唐鈺小寶"], info:"斷臂帥哥" }) //5.將文檔對象存儲至數據庫中,是一個promise對象 starDocument.save() .then(function(){ console.log('數據添加成功!!!') })
即可將數據保存在mongodb中,前提是mongodb服務已啟動
使用方式
1) Model的方法
create()
- 創建一個或多個文檔對象並添加到數據庫中
find()
- 查找所有符合條件的文檔,返回的是數組
update()
- 修改(替換)一個或多個
remove()
- 刪除一個或多個文檔
let mongoose = require('mongoose'); //1.連接數據庫 mongoose.connect("mongodb://localhost:27017/0318_mongoose_demo",{ useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex:true },function(err){ if(err){ console.log("連接失敗") }else{ console.log("連接成功") } }) // 2.創建約束對象(Schema) let starsSchema = new mongoose.Schema({ name:{ type:String, //聲明當前字段數據類型 required:true, //聲明當前字段是否必傳 unique:true //聲明當前字段是否唯一 }, age:Number, sex:{ type:String, default:"未知" //聲明當前字段的默認值 }, roles:[String], //聲明當前字段數據類型為數組, 內部子元素的類型必須是String info:mongoose.Schema.Types.Mixed }) //3.創建模型對象(Model) //第一個實參->集合名稱 //第二個實參->約束對象 let starsModel = mongoose.model('stars',starsSchema); //C->create ,是個promise // starsModel.create({ // name:"雞你太美", // age:9.99, // sex:"男孩子", // roles:["NBA形象大使"], // info:"cxk" // }) // .then(function(){ // console.log("添加成功") // }) //R->read // starsModel.find({age:17.99}) // .then(function(res){ // console.log(res) // }) // starsModel.findOne({age:17.99}) // .then(function(res){ // console.log(res) // }) // U->update // starsModel.updateMany({age:17.99},{$set:{age:37.99}}) // .then(function(res){ // console.log(res) // }) // starsModel.updateOne({age:37.99},{$set:{age:27.99}}) // .then(function(res){ // console.log(res) // }) // D->delete // starsModel.remove({sex:"男孩子"}) // .then(function(res){ // console.log(res) // })
required: true 必傳字段 minlength:3 字符串最小長度 maxlength: 20 字符串最大長度 min: 2 數值最小為2 max: 100 數值最大為100 enum: ['html', 'css', 'javascript', 'node.js'] trim: true 去除字符串兩邊的空格 validate: 自定義驗證器 default: 默認值
// 引入mongoose第三方模塊 用來操作數據庫 const mongoose = require('mongoose'); // 數據庫連接 mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true}) // 連接成功 .then(() => console.log('數據庫連接成功')) // 連接失敗 .catch(err => console.log(err, '數據庫連接失敗')); const postSchema = new mongoose.Schema({ title: { type: String, // 必選字段 required: [true, '請傳入文章標題'], // 字符串的最小長度 minlength: [2, '文章長度不能小於2'], // 字符串的最大長度 maxlength: [5, '文章長度最大不能超過5'], // 去除字符串兩邊的空格 trim: true }, age: { type: Number, // 數字的最小范圍 min: 18, // 數字的最大范圍 max: 100 }, publishDate: { type: Date, // 默認值 default: Date.now }, category: { type: String, // 枚舉 列舉出當前字段可以擁有的值 enum: { values: ['html', 'css', 'javascript', 'node.js'], message: '分類名稱要在一定的范圍內才可以' } }, author: { type: String, validate: { validator: v => { // 返回布爾值 // true 驗證成功 // false 驗證失敗 // v 要驗證的值 return v && v.length > 4 }, // 自定義錯誤信息 message: '傳入的值不符合驗證規則' } } }); const Post = mongoose.model('Post', postSchema); Post.create({title:'aa', age: 60, category: 'java', author: 'bd'}) .then(result => console.log(result)) .catch(error => { // 獲取錯誤信息對象,數組 const err = error.errors; // 循環錯誤信息對象 for (var attr in err) { // 將錯誤信息打印到控制台中 console.log(err[attr]['message']); } })
通常不同集合的數據之間是有關系的,例如文章信息和用戶信息存儲在不同集合中,但文章是某個用戶發表的,要查詢文章的所有信息包括發表用戶,就需要用到集合關聯。
使用id對集合進行關聯
使用populate方法進行關聯集合查詢
// 引入mongoose第三方模塊 用來操作數據庫 const mongoose = require('mongoose'); // 數據庫連接 mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true}) // 連接成功 .then(() => console.log('數據庫連接成功')) // 連接失敗 .catch(err => console.log(err, '數據庫連接失敗')); // 用戶集合規則 const userSchema = new mongoose.Schema({ name: { type: String, required: true } }); // 文章集合規則 const postSchema = new mongoose.Schema({ title: { type: String }, // Post和User集合進行關聯 author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } }); // 用戶集合 const User = mongoose.model('User', userSchema); // 文章集合 const Post = mongoose.model('Post', postSchema); // 創建用戶 // User.create({name: 'itheima'}).then(result => console.log(result)); // 創建文章 // Post.create({titile: '123', author: '5c0caae2c4e4081c28439791'}).then(result => console.log(result)); //聯合查詢 Post.find().populate('author').then(result => console.log(result))
打印結果
搭建網站服務器,實現客戶端與服務器端的通信 連接數據庫,創建用戶集合,向集合中插入文檔 當用戶訪問/list時,將所有用戶信息查詢出來 將用戶信息和表格HTML進行拼接並將拼接結果響應回客戶端 當用戶訪問/add時,呈現表單頁面,並實現添加用戶信息功能 當用戶訪問/modify時,呈現修改頁面,並實現修改用戶信息功能 當用戶訪問/delete時,實現用戶刪除功能
提前將這些數據導入mogodb數據庫playground中的User集合中
{"_id":{"$oid":"5c09f1e5aeb04b22f8460965"},"name":"張三","age":20,"hobbies":["足球","籃球","橄欖球"],"email":"zhangsan@itcast.cn","password":"123456"} {"_id":{"$oid":"5c09f236aeb04b22f8460967"},"name":"李四","age":10,"hobbies":["足球","籃球"],"email":"lisi@itcast.cn","password":"654321"} {"_id":{"$oid":"5c09f267aeb04b22f8460968"},"name":"王五","age":25,"hobbies":["敲代碼"],"email":"wangwu@itcast.cn","password":"123456"} {"_id":{"$oid":"5c09f294aeb04b22f8460969"},"name":"趙六","age":50,"hobbies":["吃飯","睡覺","打豆豆"],"email":"zhaoliu@itcast.cn","password":"123456"} {"_id":{"$oid":"5c09f2b6aeb04b22f846096a"},"name":"王二麻子","age":32,"hobbies":["吃飯"],"email":"wangermazi@itcast.cn","password":"123456"} {"_id":{"$oid":"5c09f2d9aeb04b22f846096b"},"name":"狗蛋","age":14,"hobbies":["打豆豆"],"email":"goudan@163.com","password":"123456"}
從數據庫中獲取數據,然后將數據填充 到模板中,返回html給瀏覽器,俗稱ssr渲染
app.js
// 搭建網站服務器,實現客戶端與服務器端的通信 // 連接數據庫,創建用戶集合,向集合中插入文檔 // 當用戶訪問/list時,將所有用戶信息查詢出來 // 實現路由功能 // 呈現用戶列表頁面 // 從數據庫中查詢用戶信息 將用戶信息展示在列表中 // 將用戶信息和表格HTML進行拼接並將拼接結果響應回客戶端 // 當用戶訪問/add時,呈現表單頁面,並實現添加用戶信息功能 // 當用戶訪問/modify時,呈現修改頁面,並實現修改用戶信息功能 // 修改用戶信息分為兩大步驟 // 1.增加頁面路由 呈現頁面 // 1.在點擊修改按鈕的時候 將用戶ID傳遞到當前頁面 // 2.從數據庫中查詢當前用戶信息 將用戶信息展示到頁面中 // 2.實現用戶修改功能 // 1.指定表單的提交地址以及請求方式 // 2.接受客戶端傳遞過來的修改信息 找到用戶 將用戶信息更改為最新的 // 當用戶訪問/delete時,實現用戶刪除功能 const http = require('http'); const url = require('url'); const querystring = require('querystring'); require('./model/index.js'); // 導入數據庫集合模塊 const User = require('./model/user'); // 創建服務器 const app = http.createServer(); // 為服務器對象添加請求事件 app.on('request', async (req, res) => { // 請求方式 const method = req.method; // 請求地址,query為路由路徑?后攜帶的參數如id, 默認是字符串,后面加個true,轉換為對象 const { pathname, query } = url.parse(req.url, true); if (method == 'GET') { // 呈現用戶列表頁面 if (pathname == '/list') { // 查詢用戶信息,返回數組 let users = await User.find(); // html字符串 let list = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h6> <a href="/add" class="btn btn-primary">添加用戶</a> </h6> <table class="table table-striped table-bordered"> <tr> <td>用戶名</td> <td>年齡</td> <td>愛好</td> <td>郵箱</td> <td>操作</td> </tr> `; // 對數據進行循環操作 users.forEach(item => { list += ` <tr> <td>${item.name}</td> <td>${item.age}</td> <td> `; item.hobbies.forEach(item => { list += `<span>${item}</span>`; }) list += `</td> <td>${item.email}</td> <td> <a href="/remove?id=${item._id}" class="btn btn-danger btn-xs">刪除</a> <a href="/modify?id=${item._id}" class="btn btn-success btn-xs">修改</a> </td> </tr>`; }); list += ` </table> </div> </body> </html> `; // 返回一個html給瀏覽器 res.end(list); }else if (pathname == '/add') { // 呈現添加用戶表單頁面 let add = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h3>添加用戶</h3> <form method="post" action="/add"> <div class="form-group"> <label>用戶名</label> <input name="name" type="text" class="form-control" placeholder="請填寫用戶名"> </div> <div class="form-group"> <label>密碼</label> <input name="password" type="password" class="form-control" placeholder="請輸入密碼"> </div> <div class="form-group"> <label>年齡</label> <input name="age" type="text" class="form-control" placeholder="請填寫郵箱"> </div> <div class="form-group"> <label>郵箱</label> <input name="email" type="email" class="form-control" placeholder="請填寫郵箱"> </div> <div class="form-group"> <label>請選擇愛好</label> <div> <label class="checkbox-inline"> <input type="checkbox" value="足球" name="hobbies"> 足球 </label> <label class="checkbox-inline"> <input type="checkbox" value="籃球" name="hobbies"> 籃球 </label> <label class="checkbox-inline"> <input type="checkbox" value="橄欖球" name="hobbies"> 橄欖球 </label> <label class="checkbox-inline"> <input type="checkbox" value="敲代碼" name="hobbies"> 敲代碼 </label> <label class="checkbox-inline"> <input type="checkbox" value="抽煙" name="hobbies"> 抽煙 </label> <label class="checkbox-inline"> <input type="checkbox" value="喝酒" name="hobbies"> 喝酒 </label> <label class="checkbox-inline"> <input type="checkbox" value="燙頭" name="hobbies"> 燙頭 </label> </div> </div> <button type="submit" class="btn btn-primary">添加用戶</button> </form> </div> </body> </html> `; res.end(add) }else if (pathname == '/modify') { // 查詢對應的數據 let user = await User.findOne({_id: query.id}); let hobbies = ['足球', '籃球', '橄欖球', '敲代碼', '抽煙', '喝酒', '燙頭', '吃飯', '睡覺', '打豆豆'] console.log(user) // 呈現修改用戶表單頁面 let modify = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h3>修改用戶</h3> <form method="post" action="/modify?id=${user._id}"> <div class="form-group"> <label>用戶名</label> <input value="${user.name}" name="name" type="text" class="form-control" placeholder="請填寫用戶名"> </div> <div class="form-group"> <label>密碼</label> <input value="${user.password}" name="password" type="password" class="form-control" placeholder="請輸入密碼"> </div> <div class="form-group"> <label>年齡</label> <input value="${user.age}" name="age" type="text" class="form-control" placeholder="請填寫郵箱"> </div> <div class="form-group"> <label>郵箱</label> <input value="${user.email}" name="email" type="email" class="form-control" placeholder="請填寫郵箱"> </div> <div class="form-group"> <label>請選擇愛好</label> <div> `; hobbies.forEach(item => { // 判斷當前循環項在不在用戶的愛好數據組,checked,選中 let isHobby = user.hobbies.includes(item); if (isHobby) { modify += ` <label class="checkbox-inline"> <input type="checkbox" value="${item}" name="hobbies" checked> ${item} </label> ` }else { modify += ` <label class="checkbox-inline"> <input type="checkbox" value="${item}" name="hobbies"> ${item} </label> ` } }) modify += ` </div> </div> <button type="submit" class="btn btn-primary">修改用戶</button> </form> </div> </body> </html> `; res.end(modify) }else if (pathname == '/remove') { // res.end(query.id) // 通過id,刪除一個 await User.findOneAndDelete({_id: query.id}); res.writeHead(301, { Location: '/list' }); res.end(); } }else if (method == 'POST') { // 用戶添加功能 if (pathname == '/add') { // 接受用戶提交的信息 let formData = ''; // 接受post參數,流式接收 req.on('data', param => { formData += param; }) // post參數接受完畢 req.on('end', async () => { // 將接收的post參數,字符串轉為對象形式 let user = querystring.parse(formData) // 將用戶提交的信息添加到數據庫中 await User.create(user); // 301代表重定向 // location 跳轉地址 res.writeHead(301, { Location: '/list' }); res.end(); }) }else if (pathname == '/modify') { // 接受用戶提交的信息 let formData = ''; // 接受post參數,流式接收參數 req.on('data', param => { formData += param; }) // post參數接受完畢 req.on('end', async () => { // 轉換成對象形式 let user = querystring.parse(formData) // 將用戶提交的信息跟新到數據庫中 await User.updateOne({_id: query.id}, user); // 301代表重定向 // location 跳轉地址 res.writeHead(301, { Location: '/list' }); res.end(); }) } } }); // 監聽端口 app.listen(3000);
model--》index.js, 連接數據庫
const mongoose = require('mongoose'); // 數據庫連接 27017是mongodb數據庫的默認端口 mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true }) .then(() => console.log('數據庫連接成功')) .catch(() => console.log('數據庫連接失敗'));
model-->user.js, 創建數據庫集合
const mongoose = require('mongoose'); // 創建用戶集合規則 const userSchema = new mongoose.Schema({ name: { type: String, required: true, minlength: 2, maxlength: 20 }, age: { type: Number, min: 18, max: 80 }, password: String, email: String, hobbies: [ String ] }); // 創建集合 返回集合構造函數 const User = mongoose.model('User', userSchema); module.exports = User;
第一步(數據查詢),用戶地址欄輸入/list路徑,返回html頁面給瀏覽器
第二步(添加用戶),點擊添加用戶按鈕連接,a標簽,get請求,跳轉到/add, 收集好參數,點擊添加用戶按鈕,form表單,重定向到/list,post請求,查看是否增加用戶成功
第三部(修改用戶),在/list,點擊修改按鈕,a標簽,跳轉到/modify, 並將用戶id傳遞過去,填寫數據后,點擊修改用戶按鈕,form表單,post請求攜帶用戶id參數,用戶數據庫根據id跟新數據,然后重定向/list,查看修改用戶數據是否成功
第四部分(修改用戶),點擊刪除按鈕,a標簽,get請求,跳轉到/remove,並攜帶用戶id參數,數據庫根據id去刪除該用戶,並跳轉到/list,是否刪除成功
總結,返回的html用字符串拼接,太麻煩,需要用到模板引擎就就簡便了