การจัดการข้อมูลในฐานข้อมูลต้องอาศัยปฏิบัติการพื้นฐาน 4 อย่าง คือ Create (สร้าง) Read (อ่าน) Update (แก้ไข) และ Delete (ลบ) ซึ่งรวมเรียกว่า CRUD Operations ใน MongoDB การทำ CRUD จะใช้คำสั่ง mongosh หรือ Driver ของภาษาโปรแกรมต่าง ๆ โดยทำงานกับข้อมูลในรูปแบบ Document (BSON) แทนการใช้ SQL
บทความนี้จะแนะนำคำสั่ง CRUD ทั้งหมดของ MongoDB ตั้งแต่การเพิ่ม Document การค้นหาด้วย Query Operators การแก้ไขข้อมูลด้วย Update Operators ไปจนถึงการลบข้อมูล พร้อมตัวอย่างที่ใช้งานได้จริง
Create — เพิ่ม Document
การเพิ่มข้อมูลใหม่เข้า Collection ทำได้ 2 คำสั่งหลัก คือ insertOne สำหรับเพิ่มทีละ 1 Document และ insertMany สำหรับเพิ่มหลาย Document พร้อมกัน ถ้า Collection ยังไม่มีอยู่ ระบบจะสร้างให้อัตโนมัติ
insertOne
// เพิ่ม Document เดียว
db.products.insertOne({
name: "Cloud VPS M",
cpu: 2,
ram: 4,
storage: 80,
price: 590,
active: true,
createdAt: new Date()
})
// ผลลัพธ์
// {
// acknowledged: true,
// insertedId: ObjectId("665a1b2c3d4e5f6a7b8c9d0e")
// }
ถ้าไม่ระบุ _id ระบบจะสร้าง ObjectId ให้อัตโนมัติ แต่ถ้าต้องการกำหนดเองก็สามารถใส่ _id ได้โดยตรง โดยค่าต้องไม่ซ้ำกับ Document อื่นใน Collection เดียวกัน
insertMany
// เพิ่มหลาย Document พร้อมกัน
db.products.insertMany([
{ name: "Cloud VPS S", cpu: 1, ram: 2, storage: 40, price: 290 },
{ name: "Cloud VPS L", cpu: 4, ram: 8, storage: 160, price: 1090 },
{ name: "Cloud VPS XL", cpu: 8, ram: 16, storage: 320, price: 2090 }
])
// ผลลัพธ์
// {
// acknowledged: true,
// insertedIds: {
// '0': ObjectId("..."),
// '1': ObjectId("..."),
// '2': ObjectId("...")
// }
// }
โดย Default ถ้า Document ตัวใดตัวหนึ่งเกิด Error (เช่น _id ซ้ำ) ระบบจะหยุดทันทีและ Document ที่เหลือจะไม่ถูกเพิ่ม ถ้าต้องการให้เพิ่มต่อแม้มี Error ให้ตั้ง ordered: false
// Unordered insert — ข้าม Document ที่ Error แล้วทำต่อ
db.products.insertMany(
[
{ _id: 1, name: "Product A" },
{ _id: 1, name: "Product B" }, // ซ้ำ — ข้ามไป
{ _id: 2, name: "Product C" } // ยังถูกเพิ่มได้
],
{ ordered: false }
)
Read — ค้นหา Document
การอ่านข้อมูลเป็นปฏิบัติการที่ใช้บ่อยที่สุด MongoDB มีคำสั่ง find สำหรับค้นหาหลาย Document และ findOne สำหรับค้นหา Document เดียว ความสามารถที่แตกต่างจากฐานข้อมูลเชิงสัมพันธ์คือการ Query ลงไปในโครงสร้าง Document ที่ซ้อนกันได้โดยตรง
find และ findOne
// ค้นหาทุก Document
db.products.find()
// ค้นหา Document เดียว
db.products.findOne({ name: "Cloud VPS M" })
// ค้นหาแบบมีเงื่อนไข
db.products.find({ active: true })
// จำกัด Field ที่แสดง (Projection)
db.products.find(
{ active: true },
{ name: 1, price: 1, _id: 0 }
)
// แสดงเฉพาะ name และ price ไม่แสดง _id
Comparison Operators
ตัวดำเนินการเปรียบเทียบช่วยให้ค้นหาข้อมูลได้ละเอียดยิ่งขึ้น โดยใช้ $ นำหน้าชื่อ Operator
// $eq — เท่ากับ (เหมือนไม่ใส่ Operator)
db.products.find({ price: { $eq: 590 } })
// $ne — ไม่เท่ากับ
db.products.find({ active: { $ne: false } })
// $gt, $gte — มากกว่า, มากกว่าหรือเท่ากับ
db.products.find({ price: { $gt: 500 } })
db.products.find({ ram: { $gte: 4 } })
// $lt, $lte — น้อยกว่า, น้อยกว่าหรือเท่ากับ
db.products.find({ price: { $lt: 1000 } })
// $in — ค่าตรงกับรายการใดรายการหนึ่ง
db.products.find({ cpu: { $in: [2, 4] } })
// $nin — ค่าไม่ตรงกับรายการใดเลย
db.products.find({ cpu: { $nin: [1, 2] } })
// รวมหลายเงื่อนไข — ราคา 500-1500
db.products.find({ price: { $gte: 500, $lte: 1500 } })
Logical Operators
// $and — ทุกเงื่อนไขต้องเป็นจริง (Default เมื่อใส่หลาย Field)
db.products.find({
$and: [
{ price: { $gte: 500 } },
{ price: { $lte: 1500 } },
{ active: true }
]
})
// $or — เงื่อนไขใดเงื่อนไขหนึ่งเป็นจริง
db.products.find({
$or: [
{ cpu: { $gte: 4 } },
{ ram: { $gte: 8 } }
]
})
// $not — กลับเงื่อนไข
db.products.find({
price: { $not: { $gt: 1000 } }
})
// $nor — ไม่ตรงกับเงื่อนไขใดเลย
db.products.find({
$nor: [
{ active: false },
{ price: { $gt: 2000 } }
]
})
Element และ Evaluation Operators
// $exists — ตรวจว่ามี Field นั้นหรือไม่
db.products.find({ discount: { $exists: true } })
// $type — ตรวจประเภทข้อมูล
db.products.find({ price: { $type: "number" } })
// $regex — ค้นหาด้วย Regular Expression
db.products.find({ name: { $regex: /cloud/i } })
// $expr — ใช้ Aggregation Expression ใน Query
db.orders.find({
$expr: { $gt: ["$total", "$budget"] }
})
Query บน Nested Document และ Array
// Dot Notation — Query ลงไปใน Nested Document
db.servers.find({ "specs.ram": { $gte: 8 } })
// ตัวอย่างข้อมูล:
// { name: "Web Server", specs: { cpu: 4, ram: 8, disk: 160 } }
// Array — ค้นหา Document ที่ Array มีค่าที่ต้องการ
db.servers.find({ tags: "production" })
// ตัวอย่างข้อมูล: { name: "Web Server", tags: ["production", "web", "nginx"] }
// $elemMatch — เงื่อนไขหลายข้อบน Element เดียวกันใน Array
db.servers.find({
metrics: {
$elemMatch: { type: "cpu", value: { $gt: 80 } }
}
})
// $size — ค้นหาตามจำนวนสมาชิกใน Array
db.servers.find({ tags: { $size: 3 } })
// $all — Array ต้องมีค่าทั้งหมดที่ระบุ
db.servers.find({ tags: { $all: ["production", "web"] } })
Cursor Methods — จัดการผลลัพธ์
// sort — เรียงลำดับ (1 = น้อยไปมาก, -1 = มากไปน้อย)
db.products.find().sort({ price: 1 })
// limit — จำกัดจำนวนผลลัพธ์
db.products.find().limit(10)
// skip — ข้ามผลลัพธ์ (ใช้สำหรับ Pagination)
db.products.find().skip(20).limit(10) // หน้าที่ 3 (แสดงหน้าละ 10)
// count — นับจำนวน Document
db.products.countDocuments({ active: true })
// distinct — ดึงค่าที่ไม่ซ้ำ
db.products.distinct("cpu")
Update — แก้ไข Document
การแก้ไขข้อมูลใน Collection ทำได้หลายรูปแบบ ตั้งแต่การอัพเดต Field บางตัว การแทนที่ Document ทั้งตัว ไปจนถึงการ Upsert (อัพเดตถ้ามี หรือสร้างใหม่ถ้าไม่มี)
updateOne และ updateMany
// updateOne — แก้ไข Document แรกที่ตรงเงื่อนไข
db.products.updateOne(
{ name: "Cloud VPS M" }, // Filter
{ $set: { price: 550, active: true } } // Update
)
// updateMany — แก้ไขทุก Document ที่ตรงเงื่อนไข
db.products.updateMany(
{ active: false },
{ $set: { active: true, updatedAt: new Date() } }
)
// ผลลัพธ์
// {
// acknowledged: true,
// matchedCount: 5,
// modifiedCount: 5
// }
Update Operators ที่ใช้บ่อย
// $set — กำหนดค่า Field (สร้างใหม่ถ้ายังไม่มี)
db.products.updateOne(
{ name: "Cloud VPS M" },
{ $set: { "specs.bandwidth": "unlimited" } }
)
// $unset — ลบ Field ออก
db.products.updateOne(
{ name: "Cloud VPS M" },
{ $unset: { discount: "" } }
)
// $inc — เพิ่ม/ลดค่าตัวเลข
db.products.updateOne(
{ name: "Cloud VPS M" },
{ $inc: { price: -50, viewCount: 1 } }
)
// $mul — คูณค่า
db.products.updateMany(
{ active: true },
{ $mul: { price: 1.1 } } // ขึ้นราคา 10%
)
// $min, $max — อัพเดตถ้าค่าใหม่น้อยกว่า/มากกว่าค่าเดิม
db.products.updateOne(
{ name: "Cloud VPS M" },
{ $min: { price: 500 } } // ลดเป็น 500 เฉพาะถ้าราคาเดิมมากกว่า
)
// $rename — เปลี่ยนชื่อ Field
db.products.updateMany(
{},
{ $rename: { "cost": "price" } }
)
// $currentDate — กำหนดค่าเป็นวันที่ปัจจุบัน
db.products.updateOne(
{ name: "Cloud VPS M" },
{ $currentDate: { lastModified: true } }
)
Array Update Operators
// $push — เพิ่มสมาชิกเข้า Array
db.servers.updateOne(
{ name: "Web Server" },
{ $push: { tags: "monitored" } }
)
// $push + $each + $sort + $slice — เพิ่มหลายค่าพร้อมจัดเรียงและจำกัดจำนวน
db.servers.updateOne(
{ name: "Web Server" },
{
$push: {
logs: {
$each: [
{ ts: new Date(), msg: "CPU spike" },
{ ts: new Date(), msg: "Memory warning" }
],
$sort: { ts: -1 },
$slice: 100 // เก็บแค่ 100 รายการล่าสุด
}
}
}
)
// $addToSet — เพิ่มเข้า Array เฉพาะถ้ายังไม่มี
db.servers.updateOne(
{ name: "Web Server" },
{ $addToSet: { tags: "production" } }
)
// $pull — ลบสมาชิกที่ตรงเงื่อนไขออกจาก Array
db.servers.updateOne(
{ name: "Web Server" },
{ $pull: { tags: "deprecated" } }
)
// $pop — ลบสมาชิกตัวแรก (-1) หรือตัวสุดท้าย (1)
db.servers.updateOne(
{ name: "Web Server" },
{ $pop: { logs: 1 } } // ลบ log ตัวสุดท้าย
)
// $ (Positional Operator) — อัพเดต Element ที่ตรงเงื่อนไข
db.servers.updateOne(
{ "metrics.type": "cpu" },
{ $set: { "metrics.$.threshold": 90 } }
)
replaceOne — แทนที่ Document ทั้งตัว
// แทนที่ Document ทั้งหมด (ยกเว้น _id)
db.products.replaceOne(
{ name: "Cloud VPS M" },
{
name: "Cloud VPS M",
cpu: 2,
ram: 4,
storage: 100,
price: 590,
active: true,
features: ["SSD NVMe", "Root Access", "DDoS Protection"],
updatedAt: new Date()
}
)
Upsert — อัพเดตหรือสร้างใหม่
Upsert เป็นรูปแบบการอัพเดตที่มีประโยชน์มากในการทำงานจริง คือถ้าพบ Document ที่ตรงเงื่อนไขจะอัพเดต แต่ถ้าไม่พบจะสร้าง Document ใหม่โดยรวม Filter กับ Update เข้าด้วยกัน
// Upsert — ถ้ามีให้อัพเดต ถ้าไม่มีให้สร้างใหม่
db.configs.updateOne(
{ key: "maintenance_mode" },
{
$set: { value: false, updatedAt: new Date() },
$setOnInsert: { createdAt: new Date() }
},
{ upsert: true }
)
// $setOnInsert — กำหนดค่าเฉพาะตอนสร้างใหม่ ไม่แก้ตอนอัพเดต
findOneAndUpdate — อัพเดตแล้วคืน Document
// คืน Document ก่อนอัพเดต (Default)
db.counters.findOneAndUpdate(
{ name: "order_seq" },
{ $inc: { value: 1 } }
)
// คืน Document หลังอัพเดต
db.counters.findOneAndUpdate(
{ name: "order_seq" },
{ $inc: { value: 1 } },
{ returnDocument: "after" }
)
// มีประโยชน์สำหรับสร้าง Auto-increment Sequence
Delete — ลบ Document
การลบข้อมูลมี 2 คำสั่งหลัก คือ deleteOne สำหรับลบ Document แรกที่ตรงเงื่อนไข และ deleteMany สำหรับลบทุก Document ที่ตรงเงื่อนไข ควรระมัดระวังเรื่องเงื่อนไข Filter ให้ดี เพราะ MongoDB ไม่มี Trash หรือ Soft Delete ในตัว
// deleteOne — ลบ Document แรกที่ตรงเงื่อนไข
db.products.deleteOne({ name: "Cloud VPS XL" })
// deleteMany — ลบทุก Document ที่ตรงเงื่อนไข
db.products.deleteMany({ active: false })
// ลบทุก Document ใน Collection (ระวัง!)
db.logs.deleteMany({})
// findOneAndDelete — ลบแล้วคืน Document ที่ถูกลบ
db.tasks.findOneAndDelete(
{ status: "completed" },
{ sort: { completedAt: 1 } } // ลบตัวที่เก่าที่สุด
)
Bulk Write — ทำหลายปฏิบัติการพร้อมกัน
เมื่อต้องทำหลายปฏิบัติการในครั้งเดียว Bulk Write ช่วยลดจำนวน Round-trip ไปยังเซิร์ฟเวอร์ฐานข้อมูล ทำให้ประสิทธิภาพดีขึ้นอย่างมากเมื่อเทียบกับการเรียกคำสั่งทีละตัว
db.products.bulkWrite([
{
insertOne: {
document: { name: "Cloud VPS 2XL", cpu: 16, ram: 32, price: 3990 }
}
},
{
updateOne: {
filter: { name: "Cloud VPS S" },
update: { $set: { price: 250 } }
}
},
{
updateMany: {
filter: { active: false },
update: { $set: { archived: true } }
}
},
{
deleteOne: {
filter: { name: "Deprecated Plan" }
}
},
{
replaceOne: {
filter: { name: "Cloud VPS M" },
replacement: { name: "Cloud VPS M", cpu: 2, ram: 4, price: 490 }
}
}
], { ordered: true })
// ordered: true (Default) — หยุดทันทีเมื่อ Error
// ordered: false — ข้าม Error แล้วทำต่อ
// ผลลัพธ์
// {
// acknowledged: true,
// insertedCount: 1,
// matchedCount: 3,
// modifiedCount: 3,
// deletedCount: 1,
// upsertedCount: 0
// }
ตัวอย่างการใช้งานจริงกับ Node.js
ในการพัฒนาเว็บแอปพลิเคชัน การเรียกใช้ CRUD ผ่าน Driver จะแตกต่างจาก mongosh เล็กน้อย ตัวอย่างนี้แสดงวิธีใช้งานกับ Node.js ซึ่งเป็นภาษาที่ใช้คู่กับ Document Database บ่อยที่สุด
const { MongoClient } = require('mongodb');
const uri = "mongodb://appUser:password@localhost:27017/myapp";
const client = new MongoClient(uri);
async function main() {
await client.connect();
const db = client.db("myapp");
const products = db.collection("products");
// Create
const newProduct = await products.insertOne({
name: "Cloud VPS M",
price: 590,
active: true
});
console.log("Inserted:", newProduct.insertedId);
// Read
const found = await products.find({ active: true })
.sort({ price: 1 })
.limit(10)
.toArray();
console.log("Found:", found.length, "products");
// Update
const updated = await products.updateOne(
{ name: "Cloud VPS M" },
{ $set: { price: 550 } }
);
console.log("Modified:", updated.modifiedCount);
// Delete
const deleted = await products.deleteMany({ active: false });
console.log("Deleted:", deleted.deletedCount);
await client.close();
}
main().catch(console.error);
ตัวอย่างการใช้งานกับ Python
Python เป็นอีกภาษาที่นิยมใช้กับฐานข้อมูลแบบ Document โดยเฉพาะงาน Data Processing และ Backend API ใช้ Driver ชื่อ PyMongo ซึ่งมี Syntax คล้ายกับ mongosh
from pymongo import MongoClient
client = MongoClient("mongodb://appUser:password@localhost:27017/myapp")
db = client["myapp"]
products = db["products"]
# Create
result = products.insert_one({
"name": "Cloud VPS M",
"price": 590,
"active": True
})
print(f"Inserted: {result.inserted_id}")
# Read
for product in products.find({"active": True}).sort("price", 1).limit(10):
print(product["name"], product["price"])
# Update
result = products.update_one(
{"name": "Cloud VPS M"},
{"$set": {"price": 550}}
)
print(f"Modified: {result.modified_count}")
# Delete
result = products.delete_many({"active": False})
print(f"Deleted: {result.deleted_count}")
client.close()
Write Concern — ควบคุมระดับความปลอดภัยในการเขียน
Write Concern กำหนดว่าฐานข้อมูลต้องยืนยันการเขียนในระดับไหนก่อนจะตอบกลับว่าสำเร็จ สำคัญมากในระบบ Replica Set เพราะส่งผลต่อความทนทานของข้อมูลและ Latency
// w: 1 — ยืนยันจาก Primary เท่านั้น (Default)
db.products.insertOne(
{ name: "Test" },
{ writeConcern: { w: 1 } }
)
// w: "majority" — ยืนยันจากเสียงส่วนใหญ่ใน Replica Set
db.products.insertOne(
{ name: "Critical Data" },
{ writeConcern: { w: "majority" } }
)
// w: 0 — ไม่รอยืนยัน (เร็วที่สุดแต่เสี่ยงสูญเสียข้อมูล)
db.logs.insertOne(
{ msg: "Non-critical log" },
{ writeConcern: { w: 0 } }
)
// j: true — ยืนยันว่าเขียนลง Journal แล้ว (ปลอดภัยที่สุด)
db.orders.insertOne(
{ orderId: "ORD-001", total: 1500 },
{ writeConcern: { w: "majority", j: true } }
)
Read Concern และ Read Preference
นอกจาก Write Concern แล้ว ยังมี Read Concern และ Read Preference ที่ควบคุมพฤติกรรมการอ่านข้อมูลในระบบ Replica Set
// Read Concern — ระดับความสอดคล้องของข้อมูลที่อ่าน
// "local" (Default) — อ่านข้อมูลล่าสุดจาก Node ที่เชื่อมต่อ
// "majority" — อ่านเฉพาะข้อมูลที่ยืนยันจากเสียงส่วนใหญ่แล้ว
// "linearizable" — อ่านข้อมูลล่าสุดที่ยืนยันแล้วจริง ๆ (ช้าที่สุด)
db.products.find({ active: true }).readConcern("majority")
// Read Preference — เลือก Node ที่จะอ่าน
// "primary" (Default) — อ่านจาก Primary เท่านั้น
// "primaryPreferred" — อ่านจาก Primary ถ้าพร้อม ไม่งั้นอ่านจาก Secondary
// "secondary" — อ่านจาก Secondary เท่านั้น
// "secondaryPreferred" — อ่านจาก Secondary ถ้าพร้อม ไม่งั้นอ่านจาก Primary
// "nearest" — อ่านจาก Node ที่ Latency ต่ำที่สุด
// เหมาะสำหรับ Report หรือ Analytics ที่ไม่ต้องการข้อมูลล่าสุดมาก
db.products.find().readPref("secondaryPreferred")
แนวทางปฏิบัติที่ดีสำหรับ CRUD
การใช้ CRUD อย่างมีประสิทธิภาพไม่ได้ขึ้นอยู่กับแค่คำสั่ง แต่ยังรวมถึงวิธีคิดและออกแบบ Query ที่เหมาะสมกับรูปแบบการใช้งาน
- สร้าง Index บน Field ที่ใช้ในเงื่อนไข Filter บ่อย ๆ เพื่อเพิ่มความเร็วในการค้นหา
- ใช้ Projection เพื่อดึงเฉพาะ Field ที่ต้องการ ลดปริมาณข้อมูลที่ส่งผ่าน Network
- หลีกเลี่ยง skip จำนวนมากในการทำ Pagination ควรใช้ Range-based Pagination แทน เช่น ใช้ _id หรือ Timestamp เป็นเงื่อนไข
- ใช้ bulkWrite เมื่อต้องทำหลายปฏิบัติการ เพื่อลด Network Round-trip
- ตั้ง Write Concern เป็น majority สำหรับข้อมูลสำคัญ และใช้ w: 0 เฉพาะ Log ที่ยอมสูญเสียได้
- ใช้ Upsert เมื่อต้องการ Insert-or-Update เพื่อลดจำนวนคำสั่ง
- ทดสอบ Query ด้วย explain() ก่อนนำไปใช้งานจริง เพื่อตรวจสอบว่าใช้ Index หรือไม่
- ระวังการใช้ updateMany กับเงื่อนไขที่กว้างเกินไป ควรทดสอบด้วย find ก่อนเสมอ
สรุป
CRUD Operations เป็นพื้นฐานสำคัญที่สุดในการทำงานกับ MongoDB ตั้งแต่การเพิ่มข้อมูลด้วย insertOne/insertMany การค้นหาด้วย find พร้อม Query Operators ที่หลากหลาย การแก้ไขด้วย Update Operators ที่ยืดหยุ่น ไปจนถึงการลบข้อมูลและการทำ Bulk Write สำหรับ Batch Operations การเข้าใจ Write Concern และ Read Concern ยังช่วยให้ควบคุมความปลอดภัยและประสิทธิภาพของข้อมูลได้อย่างเหมาะสม
แนะนำบริการ DE
การรัน MongoDB สำหรับงาน Production ต้องการเซิร์ฟเวอร์ที่มี RAM เพียงพอสำหรับ WiredTiger Cache และ SSD สำหรับ I/O ที่เร็ว Cloud VPS ของ DE รองรับการเลือก RAM และ SSD NVMe ตามความต้องการ พร้อม Root Access เต็มที่สำหรับติดตั้งและตั้งค่าฐานข้อมูลได้อย่างอิสระ
สำหรับโปรเจกต์ที่ใช้ Document Database ร่วมกับเว็บแอปพลิเคชันและไม่ต้องการจัดการเซิร์ฟเวอร์เอง Cloud Hosting ของ DE เป็นทางเลือกที่สะดวกพร้อม Managed Infrastructure ให้พร้อมใช้งาน

