NoSQL, MongoDB และ ODM
/ 7 min read
สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video
NoSQL คืออะไร ?
มารู้จักกับ NoSQL ก่อน NoSQL ย่อมาจาก “Non SQL” (ซึ่งเอาจริงๆเหมือนจะเพี้ยนมาจาก Not only SQL) เกิดขึ้นในปี 1998 ที่มีความพยายามจะเอาตัวเองหลุดจาก standard SQL ในสมัยนั้น
NoSQL เป็น 1 ใน category ของ database management ที่จะไม่ใช้ภาษา SQL ในการ query และจัดการ data
NoSQL ถูก design สำหรับการจัดการ data ขนาดใหญ่แบบ “ไม่มี structure” จึงเหมาะกับ data ที่ต้องการความ flexible และ scalable จาก relational database ปกติ
- flexible ในแง่ สามารถเก็บ data ในรูปแบบไหนก็ได้ภายใน 1 table (หรือ collection)
- scalable ถูก design มาให้ทำ horizontal scale ได้ตั้งแต่ตอนคิด idea นี้แล้ว
- performance NoSQL ส่วนใหญ่จะถูกเก็บในรูปแบบของ key-value ซึ่งมีความเร็วในการ lookup (ค้นหา) มากกว่า รวมถึง style data ที่ไม่ได้มีการ fix ไว้ สามารถประยุกต์ไปเก็บข้อมูลรูปแบบอื่นๆเพื่อ performance ที่ดีขึ้นได้
ซึ่ง NoSQL สามารถเก็บข้อมูลเป็น “อะไรก็ได้เลย” เช่น
-
Document-oriented databases:เก็บข้อมูลเป็น collection และ document รูปแบบ JSON (เช่น MongoDB ที่เรากำลังจะพูดถึง) หรือ XLM format
-
Key-value stores: เก็บคู่เป็น Key value คู่กันไว้ใน database เช่น Redis
-
Column-family stores: เก็บ data ในรูปแบบ columns แทนการเก็บเป็น rows. เช่น Apache Cassandra, HBase.
-
Graph databases: เก็บ data ในรูปแบบของ nodes และ edges (เส่นเชื่อมระหว่าง node) ของ graph. เช่น Neo4j, ArangoDB
ที่ตัดสินใจหยิบ MongoDB มาเป็นเพราะเป็น NoSQL ที่สำหรับผม ผมว่า “เข้าใจง่ายที่สุด” อีกหนึ่งตัว และเป็นตัวที่ค่อนข้างยอดฮิตมากสำหรับการยกตัวอย่าง NoSQL
MongoDB คืออะไร ?
เพื่อให้เกิดความเข้าใจสำหรับคนที่ยังไม่เคยเล่น NoSQL เราจะมาลองผ่าน MongoDB กัน
MongoDB คือ NoSQL database ที่ทำการเก็บ data ในรูปแบบของ JSON-like document. โดย data ใน MongoDB นั้นจะมีรูปแบบดังนี้
Database: A database in MongoDB is a container for collections.
- Document ใน MongoDB จะทำการจัดการ document และเก็บ data เอาไว้ในรูปแบบของ set ของ field - value ที่มีหน้าตาคล้าย JSON (1 document = 1 data)
- Collection คือกลุ่มของ documents (มองภาพง่ายๆ collection เหมือน table ใน SQL และ document เหมือน row ใน SQL) โดย collection นั้นจะไม่ทำการบังคับเอาไว้ว่าต้องมี schema แบบไหน = 1 collection สามารถมี document หน้าตา JSON ที่แตกต่างกันออกมาได้
- Database คือ database ใน mongoDB ที่ทำการเก็บ collection ทั้งหมดเอาไว้
โดยการจัดการ Data ใน MongoDB นั้นก็จะมีวิธีการจัดการอยุ่ 4 แบบใหญ่ๆคือ
- Insert = เพิ่ม data ใช้คำสั่งอย่าง
.insert
,.insertOne
,.insertMany
ได้ - Query = ดึง data ใช้คำสั่งอย่าง
.find
- Update = แก้ไข data ใช้คำสั่งอย่าง
.update
,.updateOne
,.updateMany
- Delete = ลบ data ใช้คำสั่งอย่าง
.delete
,.deleteOne
,.deleteMany
มาเล่น MongoDB command กัน
run mongoDB ผ่าน docker-compose
เมื่อ run ขึ้นมาได้ mongoDB จะออกมาที่ Port 27017 ให้ทำการ run command นี้เพื่อเข้าสู่ MongoDB
มาลองเล่น basic MongoDB commands ของ command ทั้ง 4 เคสกัน
1. Insert
- ทำการ insert data 1 document เข้า collection users
** ถ้าใช้ insert (หรือ insertMany) จะเท่ากับ insert หลายข้อมูล, ถ้าเป็น insertOne จะเป็นข้อมูลตัวเดียว
2. Query
- ทำการ read data 1 document จาก collection users
หากต้องการค้นหาข้อมูลจาก field ไหนใน JSON = ให้ใส่ field ที่ต้องการค้นหากับค่าที่ต้องการค้นหาไป เช่น เคสนี้หา field user_name ที่มี value คือ john_doe
3. Update
- ทำการ update data 1 document ใน collection users
- โดยเราจะต้องทำการระบุ field ก่อนว่าเราจะ update ให้กับ document ไหนใน users (ผ่านท่าเดียวกันกับการค้นหาข้อมูลใน collection)
เช่น ต้องการ update field age ให้เป็น 30 กับ document ที่มี field username คือ john_doe จะได้ query หน้าตาประมาณนี้ออกมา
** ถ้าใช้ update (หรือ updateMany) จะเท่ากับ update หลายข้อมูล, ถ้าเป็น updateOne จะเป็นข้อมูลตัวเดียว
4. Delete
- ทำการ delete data 1 document ใน collection users
- โดยเราจะต้องทำการระบุ field ก่อนว่าเราจะ delete ให้กับ document ไหนใน users (ผ่านท่าเดียวกันกับการค้นหาข้อมูลใน collection)
เช่น เราจะทำการลบข้อมูลทั้งหมดที่มี age เท่ากับ 30
** ถ้าใช้ delete (หรือ deleteMany) จะเท่ากับ delete หลายข้อมูล, ถ้าเป็น deleteOne จะเป็นข้อมูลตัวเดียว
Relation และ aggregation
- ใน mongoDB นั้นจะมี _id เป็น field unique key ที่เป็นเหมือน key ที่เป็นตัวแทนของแต่ละข้อมูล
- เราสามารถนำ key นั้นไปทำ relation ระหว่าง database และใช้คำสั่ง
aggregate
ในการค้นหาแทนได้
เช่น เคสนี้เราจะหยิบ _id สัก 1 ตัวใน collection users นำไปสร้างใหม่ใน orders ใน field userId
แล้วหลังจากนั้นใช้ aggregate ค้นหา order จาก userId ที่ได้ทำการ insert เข้าไปแทน
aggregation framework (ท่า aggregate) เป็น fetature ของ MongoDB ที่สามารถทำให้สร้างคำสั่งสำหรับการแปลง data และการ compute data แบบซับซ้อนใน database ได้
หลักการของ aggregation คือ มันจะทำการไป computation (query) ตรงๆใน database ก่อน แล้วนำ record ทั้งหมดที่ได้มารวมกัน + process ใหม่ (a pipeline of stages) และคืนกลับมาเป็น result ตามที่ query aggregate ระบุได้ (เทียบกับ SQL จะคล้ายๆกับ SQL GROUP BY clause)
Ref: https://www.mongodb.com/docs/manual/aggregation/
ต่อกับ Node ผ่าน Mongoose (ODM)
ทีนี้เพื่อให้จัดการง่ายผ่าน ภาษาหรือ Framework Backend ต่างๆ โลกเราได้พัฒนาสิ่งหนึ่งขึ้นมาคือ Object-Document Mapping (ODM)
ODM คืออะไร ?
ODM (Object-Document Mapping) คือ programming technique อย่างหนึ่งที่อำนวยความสะดวกในการจัดการระหว่างภาษาที่เป็น document-oriented databases (อย่าง MongoDB) และ object-oriented programming languages (OOP) โดยการปรับมุมมองของ collection, document ออกมาเป็น Object ออกมาแทน
ซึ่งปัจจุบันได้มี ODM libraries ที่สามารถสื่อสารกับ NoSQL ได้ผ่าน object ไว้หลากหลายตัว ซึ่งในแต่ละ database นั้นก็จะมีตัวที่เหมาะสมแตกต่างกันไป โดยในเคสของ MongoDB จะขอยกตัวอย่างกับ Mongoose ตัวที่ถือว่าเป็นอีกตัวที่ฮิตมากๆมาให้ทุกคนได้รู้จักกัน
Mongoose คืออะไร ?
Mongoose คือ popular ODM library ของฝั่ง MongoDB และ Node.js ซึ่งจะทำการเตรียมคำสั่งที่ใช้สำหรับจัดการกับ MongoDB ผ่านการจัดการด้วย schema-based เป็นหลัก (มอง collection เป็นเหมือน Schema object ที่ต้องทำการ defined เอาไว้ผ่าน code และทุกอย่างก็จัดการผ่าน Schema object แทน)
ซึ่ง Mongoose นั้นได้ทำการเตรียมไว้ทุกอย่างตั้งแต่การ casting (แปลงค่าข้อมูล), validation, query building, business logic hooks และของต่างๆอีกมากมาย
key feature ใหญ่ๆของ Mongoose
-
Schemas Mongoose จะสามารถ define schema ของ document ได้ โดย schema คือการประกาศโครงสร้างที่จะเป็นการบอกว่า document ที่อยู่ใน collection นี้จะมีหน้าตาออกมาประมาณไหน โดยสามารถกำหนด type, default value, validators เอาไว้ได้เลย
-
Models หลังจากที่ define schema เราจะสามารถ compile มาได้อยู่ในรูปแบบของ Model และ Model นั้นจะเป็นตัวแทนในการคุยกับ schema (ที่ไปจัดการกับ collection ใน MongoDB) ได้
-
Validation Mongoose ได้เตรียม built-in validation เอาไว้ เพื่อใช้สำหรับการ validate ข้อมูลที่ถูกต้องก่อนเข้า schema ได้ เพื่อให้แน่ใจว่าเรามีการบันทึกข้อมูลที่ถูกต้องเข้าไป
-
Queries Mongoose ได้เตรียมท่าสำหรับการ query เอาไว้พร้อมเรียบร้อยจากทั้ง 4 (+1) เคสที่เราเล่นผ่าน command มา (ซึ่งเดี๋ยวเราจะมาเล่นกันใน Session นี้)
-
Population Mongoose ได้เตรียม feature กระจายข้อมูล (populate) เอาไว้ ในกรณีที่มีการ insert ข้อมูลต่าง collection เข้าไป
เช่น อย่าง code ด้านล่างนี้คือการกระจายข้อมูล author เข้า collection book โดยนำข้อมูลที่อยู่ใน author ทั้งหมดเข้าไปเก็บคู่กันไว้ใน collection book แทน
ข้อดีนี้ จะมีประโยชน์เอาไว้ใช้สำหรับเคสของการทำ stamp ข้อมูลอย่าง order ที่ต้องนำข้อมูลทั้งหมดมาประกอบกัน เพื่อเก็บไว้ใน document ตัวใหม่แทน
ลง Mongoose ใน project node
ทำการ init project node ขึ้นมา และสามารถลงผ่าน npm ได้เลย
มาลอง Mongoose กัน
- ทำการสร้าง schema มา 2 ตัวคือ users และ orders
ทำการเพิ่ม code ให้แทรก order เข้าไปได้
ลองยิงผ่าน postman ดูโดยใช้ object นี้
ก็จะสามารถเก็บข้อมูลเข้าไปได้
- เพิ่ม API สำหรับการ get data users และ order เข้าไป
และนี่ก็คือตัวอย่างการใช้ Mongoose โดยประมาณ (ท่า update, delete จะคล้ายๆกันสามารถดูผ่าน document เพิ่มเติมได้)
เทียบกับ SQL เราควรพิจารณาใช้ NoSQL ยังไงดี
(กันสับสน เวลาเราบอกว่าใช้ SQL จะหมายถึงการใช้ Relational Database ด้วยนะ)
จากประสบการณ์ที่ใช้งานมาใน Database ทั้ง 2 แบบ ผมจะทำ checklist ให้ตามนี้
เราควรจะใช้ SQL เมื่อ
- Data Integrity คือสิ่งที่สำคัญมาก (พวกการทำ Transaction) = ใช้ SQL จะตอบโจทย์แน่นอน (NoSQL จะแล้วแต่ประเภท database)
- Structured Data data ต้องเป๊ะ = ใช้ SQL ก็จะมั่นใจแน่นอนกว่า
- Complex Queries = ใช้ SQL ต้องยอมรับ ถ้าอยากให้จบภายใน query เดียวได้ SQL ยังคงเก่งกว่าในหลายๆเรื่องมาก มันเลยยังคงสะดวกในเคสของการออก Report (พลัง join, union) ** แต่ performance นั่นอีกเรื่องนะ
เราควรจะใช้ NoSQL เมื่อ
- Scalability (รวมถึง data ใหญ่แน่ๆ) ถ้า database นี้ต้องเล่นกับ transaction หนักแน่ๆ = NoSQL จะ scale ง่ายกว่า
- Unstructured ถ้าข้อมูลมีความไม่แน่นอนในการเก็บข้อมูลสูง (เช่น user อาจจะมีหลากหลายประเภท จนข้อมูลเกิดความหลากหลาย) = NoSQL ตอบโจทย์กว่า
- Rapid Development เน้นขึ้นงานได้ไว = NoSQL จะตอบโจทย์กว่า
โจทย์ส่วนใหญ่ที่ผมมักจะเป็น
- Database ที่เจอกับ user เยอะๆ = มักจะเป็น NoSQL
- Database ที่ใช้งานในระบบหลังบ้าน, report หลังบ้าน = มักจะเป็น SQL
ข้อสังเกตอย่างหนึ่งคือ NoSQL เกิดขึ้นหลัง SQL หลายปี และเกิดมาเพื่อ “แก้ปัญหา” ที่ SQL มี ในเรื่องของ volume data และ ความหลากหลายของ data
แต่เอาเข้าจริงๆ Database ทั้ง 2 ประเภทสามารถใช้ทำงานได้ทั้งคู่ ตราบเท่าที่เราเข้าใจว่า เราจะจัดการกับมันยังไง ไม่ว่าจะเป็น Database ประเภทไหนก็ใช้งานได้ทั้งคู่เช่นเดิมนะครับ
Reference
- Code Quality & Security with SonarQubeมี Video
แนะนำพื้นฐานการทำ Sonarqube Code พร้อม Code Quality & Security
- รู้จักกับ ORM ตัวช่วย query ฐานข้อมูลกันมี Video มี Github
พามาลองเล่นตัว ORM หรือ Object Relational Mapping เทคนิคสำหรับการคุยกับ Database ผ่าน Object กัน
-
- รู้จักกับ Next.js 14 แบบ Quick Overviewมี Video มี Github
พาทัวร์ feature ต่างๆของ Next.js กันแบบรวดเร็วกัน ดูทุก feature ของ Next กัน