ต่อจากตอนที่แล้ว เราได้ทำการเตรียมความพร้อม MongoDB Server
และสร้าง Database พร้อมกับ Collection ไว้เรียบร้อยแล้ว
เนื้อหาต่อไปนี้ เราจะมาดูในเรื่องของการใช้งาน MongoDB Module และคำสั่งต่างๆ
ที่ใช้ในการทำ RESTful API หรือก็คือคำสั่งในการทำการแสดงข้อมูล เพิ่ม ลบ และแก้ไข
ในเนื้อหาก่อหน้านี้เราทำการสร้าง USERS API โดยใช้ฐานข้อมูล MySQL ดังนั้น เนื้อหานี้ เราจะเริ่มต้นโดย
ในโปรเจ็คของเราไม่ได้ใช้ MySQL แล้ว ดังนั้นไฟล์ db.js และ users.js [api/users.js] ก็จะกำหนดโค้ดใหม่ด้วย
การติดตั้ง MongoDB Module
npm install mongodb --save
หลังจากติดตั้ง เรียบร้อย ตอนนี้เราพร้อมทำการเชื่อมต่อ และใช้งาน MongoDB แล้ว
ในส่วนของไฟล์ users.js [api/users.js] เริ่มต้นให้กำหนดโค้ดเป็นดังนี้
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') router.route('/users?') .get((req, res, next) => { // แสดงข้อมูลทั้งหมด return res.json({}) }) .post(validation(schema),(req, res, next) => { // เพิ่มข้อมูลใหม่เข้าไปในฐานข้อมูล และแสดงข้อมูลใหม่ที่เพิ่งเพิ่ม return res.json({}) }) router.route('/user/:id') .all((req, res, next) => { // ตรวจสอบว่า id ข้อมูลที่ส่งมา หรืออยู่ในฐานข้อมูลหรือไม่ // ถ้ามีส่งต่อไปดึงมาแสดง / แก้ไข / ลบ next() }) .get((req, res, next) => { // แสดงรายการข้อมูลจากฐานข้อมูลของ id ข้อมูลที่อต้กงาร return res.json({}) }) .put(validation(schema),(req, res, next) => { // ทำการแก้ไขรายการข้อมูลของ id ข้อมูลที่ต้องการ จากฐานข้อมูล แล้วแสดงรายการข้อมูลที่แก้ไข return res.json({}) }) .delete((req, res, next) => { // ทำการลบช้อมูลของ id ข้อมูลที่ต้องการ จากฐานข้อมูล แล้วแสดงข้อมูลที่เพิ่งลบ return res.json({}) }) module.exports = router
และไฟล์ app.js ก็จะเป็นดังนี้
const express = require('express') // ใช้งาน module express const app = express() // สร้างตัวแปร app เป็น instance ของ express const path = require('path') // เรียกใช้งาน path module const createError = require('http-errors') // เรียกใช้งาน http-errors module const port = 3000 // port // ส่วนของการใช้งาน router module ต่างๆ const indexRouter = require('./routes/index') const userRouter = require('./routes/users') const userApi = require('./api/users') // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.set('view options', {delimiter: '?'}); // app.set('env','production') app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use(express.static(path.join(__dirname, 'public'))) // เรียกใช้งาน indexRouter app.use('/', indexRouter) app.use('/user', userRouter) app.use('/api', [userApi]) // ทำงานทุก request ที่เข้ามา app.use(function(req, res, next) { var err = createError(404) next(err) }) // ส่วนจัดการ error app.use(function (err, req, res, next) { // กำหนด response local variables res.locals.message = err.message res.locals.error = req.app.get('env') === 'development' ? err : {} // กำหนด status และ render หน้า error page res.status(err.status || 500) // ถ้ามี status หรือถ้าไม่มีใช้เป็น 500 res.render('error') }) app.listen(port, function() { console.log(`Example app listening on port ${port}!`) })
สำหรับโครที่เข้ามาอ่านบทความนี้โดยไม่ได้ไล่ลำดับมาแต่ต้น อาจจะไม่เข้าใจที่มาที่ไปของโค้ด ให้กลับไปย้อนลำดับ
ทำความเข้าใจในเนื้อหาแต่ต้นได้
ในไฟล์ app.js เราได้ตัดส่วนของการใช้งาน provinceApi ที่เป็น API ตัวอย่างของ MySQL ที่รองรับการแบ่งหน้าไป
และเหลือไว้แค่ userApi ซึ่งในบทความนี้ เราจะใช้การเชื่อมต่อกับ MongoDB
การเชื่อมต่อกับ MongoDB
ในขั้นตอนนี้ เราจะทำการสร้างไฟล์ module สำหรับทำการเชื่อมต่อกับฐานข้อมูล MongoDB เราจะใช้ไฟล์ชื่อเดิม
คือ db.js ในโฟลเดอร์ config แทนอันเก่าที่ใช้สำหรับการเชื่อมต่อกับ MySQL
ให้เรากำหนดโค้ดให้กับไฟล์ db.js เป็นดังนี้
ไฟล์ db.js [config/db.js]
const MongoClient = require('mongodb').MongoClient // ใช้งาน mongodb module const url = 'mongodb://localhost:27017' // กำหนด url สำหรับ MongoDB Server const dbName = 'testdb' // กำหนดชื่อฐานข้อมูลที่จะใช้งาน // ส่งการเชื่อมต่อฐานข้อมูลไปใช้งาน module.exports = new Promise((resolve, reject)=>{ MongoClient.connect(url, { useNewUrlParser: true }, (error, client) => { if (error) throw error var db = client.db(dbName) console.log("Connected successfully to server") resolve(db) }) })
จะเห็นว่า ถ้าเรามีการเชื่อมต่อไปยัง MongoDB server และไม่เกิดข้อผิดพลาดขึ้น จะขึ้น
"Connected successfully to server" นั่นคือเชื่อมต่อสำเร็จ แต่ใน MongoDB ในขั้นตอนการกำหนด
ชื่อ Database ที่จะเชื่อมต่อ แม้กำหนดชื่อไม่ถูกต้อง ก็ยังขึ้นข้อความว่าเชื่อมต่อสำเร็จ แต่จะมีผลตอน
ไปดึงข้อมูลหรือเรียกใช้งาน collection ในส่วนนั้น ก็จะไม่สามารถดึงข้อมูลได้แทน ตรงนี้ให้เป็นข้อสังเกตไว้
การใช้งาน MongoDB Module
ก่อนจะไปดูโค้ดการใช้งาน RESTful API ด้วย MongoDB ก็มาทำความรู้จักการใช้งาน MongoDB เบื้องต้นกัน
ก่อน ในหัวข้อนี้ เราจะมาดูว่า MongoDB Module หรืดที่เรียกว่า MongoDB Driver Module นั่นสามารถทำอะไรได้บ้าง
การแสดงข้อมูล [SELECT]
db.collection('tbl_users') .find().toArray( (error, results) => { // error ข้อผิดพลาดที่อาจจะเกิดขึ้น // results เป็น array ของข้อมูลผลลัพธ์ })
ตัวอย่าง
db.collection('tbl_users') .find().toArray( (error, results) => { if(error) { throw error } res.json(results) })
จะใช้คำสั่ง find() ในการแสดงข้อมูล ข้างต้นเป็นการแสดงรายการข้อมูลทั้งหมด แต่ถ้าต้องการแสดงเฉพาะข้อมูล
ที่ตรงกับเงื่อนไขเช่น id เท่ากับ 1 ก็จะเพิ่ม เงื่อนไขเข้าไปเป็นดังนี้
.find({id:1}) // กรณี id เป็นตัวเลข (int) .find({id:'1'}) // กรณี id เป็นข้อความ (string) .find({id:1,email:'john.doe@gmail.com'}) // กรณีมีมากกว่า 1 เงื่อนไข
การกำหนดค่าของข้อมูลที่ต้องการค้นหาหรือแสดง ค่านั้นต้องมีรูปแบบข้อมูลที่สัมพันธ์กัน เช่น กรณีเราเก็บ id เป็น int
หรือตัวเลข ถ้าเรากำหนดการแสดงข้อมูลในรูปแบบ {'id':'1'} จะไม่พบข้อมูล
การกำหนดเงื่อนไขในคำสั่ง find() ข้างต้นอยู่ในรูปแบบ { <field>: <value> }
นอกจากนี้ยังมีตัวจัดการเพิ่มเติม ที่ใช้งานอยู่ในรูปแบบ { <field1>: { <operator1>: <value1> }, ... }
ที่ใช้สำหรับหาค่าในรูปแบบพิเศษเพิ่มเติม เช่น ค่าที่มีการเปรียบเทียบ / ค่าที่เป็นแบบ array เป็นต้น
Comparison Operator
$eq [=], $ne [!=], $gt [>], $gte [>=], $lt [<], $lte [<=], $in [IN()], $nin [NOT IN()]
{ field: { $eq: <value> } } { field: { $ne: <value> } } { field: { $gt: <value> } } { field: { $gte: <value> } } { field: { $lt: <value> } } { field: { $lte: <value> } } { field: { $in: [<value1>, <value2>, ... <valueN> ] } } { field: { $nin: [<value1>, <value2>, ... <valueN> ] } }
ข้างต้นเป็นตัวอย่างตัวดำเนินการ เมื่อเทียบกับค่าในคำสั่ง SQL ยกตัวอย่าง
.find({id: {$gte:1} }) // id >=2 .find({id: {$in:[1,2]} }) // id IN (1,2) หรือ id=1 OR id=2
Logical Operator
$and [AND], $or [OR], $not [NOT], nor [NOR]
{ $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] } { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] } { field: { $not: { <operator-expression> } } } { $nor: [ { <expression1> }, { <expression2> }, ... { <expressionN> } ] }
ตัวอย่าง
.find({$and:[{id:1},{email:'john.doe@gmail.com'}]}) // id=1 AND email='john.doe@gmail.com' .find({$or:[{id:1},{email:'will.smith@gmail.com'}]}) // id=1 OR email='will.smith@gmail.com' .find({id:{$not:{$gt:1}}}) // id<=1 OR id field not exists กล่าวคือ มี id <= 1 หรือ ไม่มีฟิลด์ id .find({$nor:[{id:1},{email:'will.smith@gmail.com'}]}) // id !=1 AND email !='will.smith@gmail.cm' หรือ // id !=1 AND email field not exists หรือ // id field not exists AND email !='will.smith@gmail.cm' หรือ // id field not exists AND email field not exists // กรณี $nor อาจจะหมายถึงค่าที่ตรงข้ามทั้งหมดที่เป็นไปได้
ยังมี Operator อื่นๆ สามารถดูเพิ่มเติมได้ที่ Operators
นอกจากคำสั่ง find() แล้วยังมีคำสั่งที่ทำงานลักษณะคล้ายๆ กัน แต่เพิ่มการทำงานบางอย่างเข้าไป เช่น
.findOneAndUpdate() .findOneAndDelete() .findOneAndReplace()
เราจะได้เห็นการใช้งานคำสั่ง findOneAndUpdate() ที่เราจะใช้สำหรับเพิ่มตัว auto increment ให้กับ tbl_lastid
ตัวอย่าง
db.collection('tbl_users') .findOneAndUpdate( {id:2}, { $set:{ name:'Jennifer Lee', email:'jennifer.lee@gmail.com' } },(error, results) => { if (error) throw error return res.json(results) })
คำสั่งข้างต้น จะค้นหาไปที่ id=2 ถ้าพบแล้วก็ทำการอัพเดท โดยใช้ $set operator ทำคำสั่งกำหนดฟิดล์ข้อมูล
ที่จะทำการอัพเดท ข้างต้น ทำการอัพเดท name และ email
การแสดงข้อมูลเฉพาะฟิลด์ [SELECT field1,field2]
การแสดงข้อมูลในหัวข้อที่ผ่านมา เราไม่ได้มีการกำหนดว่า ต้องการแสดงข้อมูลฟิลด์ใดบ้าง นั่นคือ
เมื่อทำการแสดงข้อมูล ก็จะเป็นการแสดงฟิลด์ข้อมูลทั้งหมดใน collection มาแสดง
db.collection('tbl_users') .find() .project({ ชื่อฟิลด์1: 1, ชื่อฟิลด์2: 1 }) .toArray( (error, results) => { // error ข้อผิดพลาดที่อาจจะเกิดขึ้น // results เป็น array ของข้อมูลผลลัพธ์ })
ในการที่จะแสดงฟิลด์ใดๆ ให้เราใช้งานคำสั่ง project() แล้วกำหนด ฟิลด์ที่ต้องการแสดง ในรูปแบบ
{ ชื่อฟิลด์1: 1, ชื่อฟิลด์2: 1 } // เช่น { name: 1, emaiil: 1 }
โดยกำหนดชื่อฟิลด์ที่ต้องการแสดง <field> ให้มีค่าเท่ากับ 1
ตัวอย่าง
db.collection('tbl_users') .find() .project({ name: 1, emaiil: 1 }) .toArray( (error, results) => { if(error) { throw error } res.json(results) })
การใช้งานข้างต้น เป็นการเลือกแสดงเฉพาะฟิดล์ที่ต้องการคือ name, email และ _id หรือถ้าเปรียบเทียบ
ในคำสั่ง SQL ก็คือ
SELECT _id,name,email FROM tbl_users
สังเกตว่า ทำไมถึงมีฟิลด์ _id ออกมาด้วย ทั้งนี้ก็เพราะว่า _id เป็นค่า unique _id ทำงานเสมือนเป็น primary key
ของ document เราสามารถ กำหนดให้ไม่แสดง _id โดยกำหนดค่า เป็น 0 ในรูปแบบดังนี้
.project({ name: 1, emaiil: 1 , _id: 0 })
อย่างไรก็ตาม การกำหนด _id เท่ากับ 0 จะมีผลต่อการกำหนดเงื่อนไขการแสดงของฟิลด์ ดังนั้น ควรให้แสดงฟิลด์
_id เสมอเพื่อป้องกันปัญหานี้
การจะกำหนดฟิลด์ที่ต้องการแสดง กับการกำหนดไฟล์ที่ไม่ต้องการแสดงด้วยค่า 1 หรือ 0 นั้น มีอีกรูปแบบ หรือแนวทาง
เพิ่มเติมดังนี้ สมมติเรามีฟิลด์รายการทั้งหมด 15 ฟิลด์ [A-O] ถ้าจำนวนฟิลด์ที่เราต้องการแสดงมี 10 รายการ เราสามารถกำหนด
ค่าฟิลด์ที่ต้องการแสดงทั้ง 10 ให้เท่ากับ 1 หรือ อีกวิธ๊คือ กำหนดฟิลด์ที่ไม่ต้องการแสดงเท่ากับ 0 ตัวอย่าง
{ A:1, B:1, C:1, D:1, E:1, F:1, G:1, H:1, I:1, J:1 } // หรือแบบนี้ { K:0, L:0, M:0, N:0, 0:0 }
ทั้งสองแบบให้ผลลัพธ์เหมือนกัน คือแสดงฟิลด์ A-J แต่เราจะเห็นว่า ใช้วิธีที่ 2 คือกำหนด K-O เป็น 0 จะสะดวกกว่า
ดั้งนั้น การจะเลือกใช้วิธีใดๆ ก็ต้องพิจารณาให้เหมาะสม ถ้าแสดงจำนวนมากๆ ก็เลือกใช้แบบที่ 2 แต่ถ้าแสดงจำนวนน้อยๆ
ก็เลือกใช้วิธีที่ 1
การแสดงข้อมูลฟิลด์ที่เป็น Object ตัวอย่างเช่น
{ name:'Monika Lous', email:'monika.lous@gmail.com', address:{ city:'San Francisco', country:'USA' } }
ซึ่งถ้าเราต้องการแสดง name และ address เฉพาะ country ก็จะกำหนดในรูปแบบ dot notation
หรือการใช้ (.) เชื่อมค่าที่ต้องการ เป็นดังนี้
{ name:1, 'address.country':1 }
การแสดงข้อมูลฟิลด์ที่เป็น Array ตัวอย่างเช่น
{ name:'Monika Lous', email:'monika.lous@gmail.com', address:{ city:'San Francisco', country:'USA' }, hobbies:[ 'Play Piano', 'Reading Book', 'Drawing Anime' ] }
ในการแสดงฟิลด์ข้อมูลที่เป็น Array เราสามารถใช้ "$slice" projection operator มาช่วยในรูปแบบดังนี้
รูปแบบแรก { array: {$slice: count } }
ตัวอย่าง เมื่อ count เป็นค่าบวก หรือลบก็ได้
{ hobbies:{ $slice: 2 } ) // แสดง hobbies 2 รายการแรก จะได้ Play Piano กับ Reading Book { hobbies:{ $slice: -2 } ) // แสดง hobbies 2 รายการสุดท้าย จะได้ Reading Book กับ Drawing Anime
รูปแบบที่สอง { array: {$slice: [ skip , limit ] } }
ตัวอย่าง เมื่อ skip คือตำแหน่งรายการที่จะข้าม และ limit คือจำนวนรายการที่จะแสดง
{ hobbies:{ $slice: [1,2] } ) // ข้าม 1 รายการแรก แล้วแสดงรายการถัดไปอีก 2 รายการ // จะได้ Reading Book กับ Drawing Anime { hobbies:{ $slice: [-1,2] } ) // ถอยมาจากด้านหลัง 1 รายการ แล้วแสดงรายการถัดไปอีก 2 รายการ // จะได้ Drawing Anime
เราสามารถใช้คำสั่ง limit() ระบุจำนวนข้อมูลที่ต้องการแสดงได้ เช่น
.find().limit(10) // แสดงแค่ 10 รายการ
เราสามารถใช้คำสั่ง sort() กำหนดฟิลด์ข้อมูลที่ต้องการเรียงลำดับได้โดยกำหนด 1 = ASC และ -1 = DESC
.find().limit(10).sort({id:-1})
การเพิ่มข้อมูล [INSERT]
let user = { "id":3, "name":"Jennifer Lee", "email":"jennifer.lee@gmail.com" } db.collection('tbl_users') .insertOne(user, (error, results) => { if (error) throw error return res.json(results) })
การเพิ่มข้อมูลครั้งละ 1 รายการ เราสามารถใช้คำสั่ง insertOne() เพิ่มรายการ โดยสังเกตว่าตัวแปร user
เป็น object ข้อมูลที่จะทำการเพิ่มเพียงรายการเดียว อยู่ในรูปแบบ user = {}
let user = [ { "id":4, "name":"William Smith", "email":"will.smith@gmail.com" }, { "id": 5, "name": "Lorra Jackson", "email": "lorra.jackson@gmail.com" } ] db.collection('tbl_users') .insertMany(user, (error, results) => { if (error) throw error return res.json(results) })
นอกจากนั้น เรายังสามารถทำคำสั่งเพื่อเพิ่มครั้งละหลายๆ รายการในครั้งเดียวโดยใช้คำสั่ง insertMany() สังเกต
ตัวแปร user จะเป็น array ของ object ข้อมูลที่จะบันทึก อยู่ในรูปแบบ user = [{},{}....{]]
ในการเพิ่มข้อมูล ถ้ามีการเพิ่มข้อมูลสำเร็จ จะมีค่า results.insertedId เป็นค่าของ _id ที่เราสามารถนำไปใข้งานได้
กรณีเพิ่มข้อมูลด้วยคำสั่ง insertOne() ส่วนกรณีเพิ่มข้อมูลด้วย insertMany() จะได้ค่า insertedId อยู่ในรูปแบบของ
Array อ้างอิงหรือใช้ข้อมูลจาก results.insertedIds[0]...results.insertedIds[1].... ตามจำนวนรายการที่เพิ่ม
การแก้ไขข้อมูล [UPDATE]
let user = { "name":"Thomas Hug", "email":"thomas.hug@gmail.com" } db.collection('tbl_users') .updateOne({id:5},{ $set: user }, (error, results) => { if (error) throw error return res.json(results) })
การอัพเดทหรือแก้ไขข้อมูลเราสามารถใช้ findOneAndUpdate() ในตัวอย่างก่อนหน้า หรือจะใช้รูปแบบข้างต้น
โดยใช้คำสั่ง updateOne() คำสั่งนี้กำหนดเงื่อนไขใน Argument ตัวแรก ในตัวอย่างคือ {id:5} หมายถึงอัพเดทราย
การที่มี id = 5 ด้วย ข้อมูลที่อัพเดทที่กำหนดด้วยตัวดำเนินการ $set ตัวแปร user ข้างต้นเป็นตัวแปร object ค่าเดียว
อยู่ในรูปแบบ user = {}
การใช้คำสั่ง updateOne() จะทำการอัพเดทเฉพาะรายการข้อมูลแรกที่ตรงกับเงื่อนไข เท่านั้น แม้สมมติว่า id = 5
มีมากกว่า 1 รายการ หากต้องการอัพเดทหลายๆ รายการให้ใช้คำสั่ง updateMany()
let user = { "name":"Thomas Hug", "email":"thomas.hug@gmail.com" } db.collection('tbl_users') .updateMany({id:{$lt:5}},{ $set: user }, (error, results) => { if (error) throw error return res.json(results) })
สังเกตส่วนของการกำหนดเงื่อนไขกรณีอัพเดทหลายรายการด้วยคำสั่ง updateMany() จะเห็นว่าเงื่อนไขคือ
อัพเดทรายการที่มีค่า id < 5 ซึ่งมีอยู่ 4 รายการ นั่นหมายความว่า ทั้งหมดทุกรายการที่มีค่าน้อยกว่า 5 จะถูกอัพเดท
พร้อมกัน ด้วยค่าข้อมูลใหม่ที่เหมือนกัน
ในการอัพเดทไม่ว่ากรณี updateOne() หรือ updateMany() หากทำการอัพเดทข้อมูลสำเร็จ
เราสามารถใช้ค่า results.nModified ซึ่งเป็นค่าที่บอกว่าอัพเดทไปกี่รายการ เช่น เราอาจจะนำไปใช้ในเงื่อนไขว่า
ถ้า results.nModified > 0 แสดงว่ามีการอัพเดทข้อมูลเรียบร้อยแล้ว เป็นต้น
การลบข้อมูล [DELETE]
db.collection('tbl_users') .deleteOne({id:1}, (error, results) => { if (error) throw error return res.json(results) })
ในกรณีการลบข้อมูลจะใช้คำสั่ง deleteOne() โดยกำหนดเงื่อนไขข้อมูลที่ต้องการลบใน Argument แรก หาก
พบรายการที่มี id = 1 ซึ่งอาจมี 1 รายการหรือมากกว่า 1 รายการ ก็จะทำการลบแค่เพียงรายการเดียว หากต้องการลบ
หลายรายการพร้อมกันที่ตรงเงื่อนไข เช่น สมมติเงื่อนไขเราเปลี่ยนเป็น {id:{$gt:3}} หมายถึง มี id > 3 สมมติว่ามี
ทั้งหมด 3 รายการ เราต้องการลบทั้ง 3 รายการพร้อมกัน ก็จะใช้คำสั่ง deleteMany() แทน
db.collection('tbl_users') .deleteMany({id:{$gt:3}}, (error, results) => { if (error) throw error return res.json(results) })
การทำคำสั่งต่อเนื่อง (Transaction)
db.collection('tbl_lastid') .findOneAndUpdate({id:1}, { $inc: { user_id: 1 }},(error, results)=>{ if (error) throw error let ID = results.value.user_id+1 let user = { "id": ID } db.collection('tbl_users') .insertOne(user, (error, results) => { if (error) throw error return res.json(results) }) })
เราสามารถทำคำสั่งต่อเนื่องซ้อนกันในรูปแบบที่เรียกว่า transaction อย่างในตัวอย่างด้านบน เริ่มต้น
ทำคำสั่งแสดงข้อมูลของ tbl_lastid เมื่อหาเจอแล้วก็ทำการอัพเดทฟิลด์ user_id เพิ่มขึ้นครั้ง 1 โดยใช้
$inc ในรูปแบบ { $inc: { user_id: 1 }} จากนั้นนำค่า user_id ที่ได้จากคำสั่งแรก โดยใช้ค่า results.value.user_id
นำค่าไปบวกเพิ่ม 1 แล้วเก็บไว้ในตัวแปร ID นำค่า ID ที่มีการบวกค่าเพิ่ม เข้าไปทำงานต่อในคำสั่งการเพิ่มข้อมูล
ใน tbl_users collection
การใช้งาน MongoDB ร่วมกับ RESTful API
เมื่อเราเข้าใจรูปแบบการใช้งาน MongoDB module เพื่อทำงานร่วมกับฐานข้อมูล MongoDB เบื้องต้นแล้ว ต่อไป
เราจะนำมาประยุกต์ใช้งานกับ USERS API ของเราในไฟล์ users.js [api/users.js] จะได้เป็นดังนี้
ไฟล์ users.js [api/users.js]
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') const db = require('../config/db') router.route('/users?') .get((req, res, next) => { db.then((db)=>{ // เมื่อมีการเชื่อมต่อไปยัง MongoDB server เรียบร้อยแล้ว db.collection('tbl_users') .find().toArray( (error, results) => { // แสดงข้อมูลของ tbl_usrs ทั้งหมด if(error) return res.status(500).json({ "status": 500, "message": "Internal Server Error" }) const result = { "status": 200, "data": results } return res.json(result) }) }) }) .post(validation(schema),(req, res, next) => { db.then((db)=>{ // เมื่อมีการเชื่อมต่อไปยัง MongoDB server เรียบร้อยแล้ว // ก่อนเพิ่มข้อมลใหม่ db.collection('tbl_lastid') // ทำการดึง id ล่าสุดจาก tbl_lastid ที่ได้บันทึกไว้ .findOneAndUpdate({id:1}, { $inc: { user_id: 1 }},(error, results)=>{ // ถ้าเจอก็ให้อัพเดทค่าเก่า เพื่อให้ค่าไม่ซ้ำกัน if(error) return res.status(500).json({ "status": 500, "message": "Internal Server Error" }) // ใช้ค่า user_id จาก tbl_lastid มาบวกค่าเพิ่ม เพื่อนำไปใช้งาน let ID = results.value.user_id+1 // เตรียมข้อมูลที่จะทำการเพิ่ม let user = { "id": ID, // จะได้ค่า ID เป็น auto incremnt ที่เราประยุกต์ขึ้นมา ค่าจะไม่ซ้ำกัน "name": req.body.name, "email": req.body.email } db.collection('tbl_users') .insertOne(user, (error, results) => { // ทำการเพิ่มข้อมูลไปยัง tbl_users if(error) return res.status(500).json({ "status": 500, "message": "Internal Server Error" }) // เพื่อไม่ต้องไปดึงข้อมูลที่เพิ่งเพิม มาแสดง ให้เราใช้เฉพาะ id ข้อมูลใหม่ที่เพิ่งเพิม // รวมกับชุดข้อมูลที่เพิ่งเพิ่ม เป็น ข้อมูลที่ส่งกลับออกมา user = [{_id:results.insertedId, ...user}] const result = { "status": 200, "data": user } return res.json(result) }) }) }) }) router.route('/user/:id') .all((req, res, next) => { db.then((db)=>{ // เมื่อมีการเชื่อมต่อไปยัง MongoDB server เรียบร้อยแล้ว db.collection('tbl_users') // แสดงข้อมูล user ตาม id ที่ส่งมา เพื่อให้แน่ใจว่าจะเป็นตัวเลข เราใส่ +นำหน้าตัวแปร .find({id:+req.params.id}).toArray( (error, results) => { // หาก error หรือไม่พบข้อมูล if(!results.length) return res.status(400).json({ "status": 400, "message": "Not found user with the given ID" }) res.user = results // ส่งต่อข้อมูลไปยัง method ที่มีการใช้งาน next() }) }) }) .get((req, res, next) => { // ถ้าเป็นแสดงข้อมํลของ id ที่ส่งมา ใช้ค่า results ที่ส่งมาจาก middleware ก่อนหน้า แล้วนำไปแสดง const result = { "status": 200, "data": res.user } return res.json(result) }) .put(validation(schema),(req, res, next) => { db.then((db)=>{ // เมื่อมีการเชื่อมต่อไปยัง MongoDB server เรียบร้อยแล้ว // เมื่อมีการแก้ไขข้อมูล เตรียมข้อมูลสำหรับแก้ไข let user = { "id": +req.params.id, "name": req.body.name, "email": req.body.email } db.collection('tbl_users') .updateOne({id:+req.params.id}, // ทำการแก้ไขค่าให้ตรงกับ id ที่กำหนด { $set: user }, (error, results) => { if(error) return res.status(500).json({ "status": 500, "message": "Internal Server Error" }) // ถ้ามีการแก้ไขค่าใหม่ if(results.modifiedCount > 0) { // เอาค่าฟิลด์ทีได้ทำการอัพเดท ไปอัพเดทกับข้อมูลทั้งหมด user = Object.assign(res.user[0], user) }else{ // มีการอัพเดท แต่เป็นค่าเดิม user = res.user } const result = { "status": 200, "data": user } return res.json(result) }) }) }) .delete((req, res, next) => { db.then((db)=>{ // เมื่อมีการเชื่อมต่อไปยัง MongoDB server เรียบร้อยแล้ว db.collection('tbl_users') .deleteOne({id:+req.params.id}, (error, results) => { if(error) return res.status(500).json({ "status": 500, "message": "Internal Server Error" }) const result = { "status": 200, "data": res.user } return res.json(result) }) }) }) module.exports = router
ในการใช้งาน RESTful API ร่วมกับ MongoDB ข้างต้น เรามีการเรียกใช้งาน db module ด้านบน ซึ่งเป็นการเชื่อมต่อ
กับฐานข้อมูล MongoDB เนื่องจากในไฟล์ db.js เราทำการ exports การเชื่อมต่อกับฐานข้อมูลกลับมาในรูปแบบ
Promise Object
ไฟล์ db.js [config/db.js] บางส่วน
// ส่งการเชื่อมต่อฐานข้อมูลไปใช้งาน module.exports = new Promise((resolve, reject)=>{ MongoClient.connect(url, { useNewUrlParser: true }, (error, client) => { if (error) throw error var db = client.db(dbName) console.log("Connected successfully to server") resolve(db) }) })
ดังนั้นเราจะเรียกใช้งานได้ก็ต่อ มีการส่งค่าการเชื่อมต่อมาแล้ว ในรูปแบบของ
new Promise().then(() =>{})
เมื่อเราใช้งานกับ MongoDB module ก็จะได้เป็น
db.then((db)=>{ // เรียกใช้งาน db ซึ่งเป็น Db connection instance ที่เรา exports มาจากไฟล์ db.js })
การเรียกใช้งานข้างต้นทุกครั้ง การทำคำสั่งแสดงข้อมูล เพิ่ม ลบ และแก้ไขข้อมูล ไม่ได้เป็นการทำการเชื่อมต่อ
กับฐานข้อมูลใหม่ ทุกๆ ครั้ง แต่เป็นการใช้ค่า instance เดิมที่ได้มีการเชื่อมต่อเรียบร้อยแล้ว
จะเห็นว่า เราพยายามทำให้รูปแบบการใช้งานร่วมกับ MongoDB มีความคล้ายคลึงและมีรูปแบบที่เหมือนกับการใช้งาน
กับ MySQL เพื่อให้ทำความเข้าใจได้ง่ายขึ้น อย่างไรก็ดี การใช้งาน MongoDB มีรายละเอียดปลีกย่อยมากมาย รวมถึงการ
ทำให้การใช้งาน MongoDB มีประสิทธิภาพมากขึ้น อย่างการทำ index ฟิลด์ข้อมูล ที่มักกำหนดในเงื่อนไขการแสดงข้อมูล
การกำหนดสิทธิ์การเข้าถึงข้อมูล อย่างการสร้าง และเพิ่ม user ให้กับ collection เหล่านี้เป็นต้น ก็สามารถศึกษาเพิ่มเติมได้
และหากมีเกร็ตความรู้หรือรายละเอียดใหม่ๆ ก็อาจจะนำมาอัพเดท ต่อๆ ไป