Hiểu về cấu trúc Document và Collection trong MongoDB
MongoDB là một database NoSQL dạng document-based. Khác với SQL, MongoDB không bắt buộc schema cố định trước khi chèn dữ liệu. Đơn vị dữ liệu nhỏ nhất là Document, được lưu trữ dưới dạng JSON (BSON). Một tập hợp các Document cùng loại gọi là Collection.
Trong MongoDB, mỗi Document có một trường đặc biệt là _id. Nếu bạn không tự tạo _id, MongoDB sẽ tự động sinh ra một ObjectId duy nhất (UUID dạng hex) cho document đó.
Để minh họa, hãy tưởng tượng Collection users chứa các Document đại diện cho tài khoản người dùng. Mỗi Document có thể có các trường khác nhau (ví dụ: người dùng A có trường avatar, người dùng B không có), điều này gọi là Schema-less.
Khởi động MongoDB Shell để thao tác
Bước đầu tiên là truy cập vào MongoDB qua dòng lệnh (Terminal) để bắt đầu các thao tác CRUD. Bạn cần kết nối với MongoDB server đã cài đặt trong các phần trước.
Chạy lệnh sau để mở MongoDB Shell (mongosh). Đảm bảo bạn đang đăng nhập bằng user admin hoặc user có quyền truy cập.
mongosh -u admin -p your_password --authenticationDatabase admin
Kết quả mong đợi: Bạn thấy prompt Current Mongosh Log ID: ... và dòng Mongosh: version X.X.X, sau đó là prompt test> hoặc admin> tùy vào database mặc định.
Tạo Database và Collection mới
Chuyển đổi và tạo Database
MongoDB không yêu cầu lệnh "CREATE DATABASE" tường minh. Database sẽ được tự động tạo ngay khi bạn chèn document đầu tiên vào collection thuộc database đó. Tuy nhiên, để chuẩn hóa, chúng ta dùng lệnh use để chuyển ngữ cảnh.
Chúng ta sẽ tạo một database tên là ecommerce_db để lưu trữ dữ liệu bán hàng mẫu.
use ecommerce_db
Kết quả mong đợi: Xuất hiện dòng thông báo switched to db ecommerce_db. Lưu ý: Database chưa thực sự tồn tại trên disk cho đến khi có dữ liệu.
Tạo Collection thủ công với Schema Validation
Mặc dù MongoDB không bắt buộc schema, trong môi trường sản xuất (Production), chúng ta nên sử dụng Schema Validation để đảm bảo tính nhất quán của dữ liệu. Chúng ta sẽ tạo collection products với quy tắc: mỗi document phải có trường name (string) và price (number).
Chạy lệnh createCollection với tham số validator để định nghĩa schema.
db.createCollection("products", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "price", "category"],
properties: {
name: { bsonType: "string", description: "Product name is required and must be a string" },
price: { bsonType: "number", minimum: 0, description: "Price must be a number and cannot be negative" },
category: { bsonType: "string", enum: ["Electronics", "Clothing", "Food"], description: "Category must be Electronics, Clothing, or Food" }
}
}
},
validationLevel: "strict",
validationAction: "error"
})
Kết quả mong đợi: MongoDB trả về object { ok: 1 } xác nhận collection products đã được tạo thành công với quy tắc validation.
Verify kết quả tạo Database và Collection
Để kiểm tra database đã tồn tại và list các collection bên trong, dùng các lệnh sau.
show dbs
show collections
Kết quả mong đợi: Trong danh sách database, bạn thấy ecommerce_db (nếu đã có dữ liệu) hoặc chưa thấy. Trong danh sách collection, bạn chắc chắn thấy products.
Thực hiện thao tác Insert (Chèn dữ liệu)
Chèn một Document đơn lẻ
Chúng ta sẽ chèn sản phẩm đầu tiên vào collection products. MongoDB sẽ tự động tạo trường _id nếu bạn không cung cấp.
Lưu ý: Dữ liệu phải tuân thủ schema validation đã tạo ở phần trên (name, price, category hợp lệ), nếu không lệnh sẽ lỗi.
db.products.insertOne({
name: "iPhone 15 Pro",
price: 25000000,
category: "Electronics",
stock: 50,
createdAt: new Date()
})
Kết quả mong đợi: Trả về object chứa acknowledged: true và insertedId (ObjectId của document vừa tạo). Nếu dữ liệu không khớp schema, bạn sẽ thấy lỗi Location1001: Validation failed.
Chèn nhiều Document cùng lúc
Để tăng tốc độ khởi tạo dữ liệu mẫu, chúng ta dùng insertMany để chèn hàng loạt document.
db.products.insertMany([
{ name: "Samsung Galaxy S24", price: 22000000, category: "Electronics", stock: 30 },
{ name: "Nike Air Force 1", price: 3500000, category: "Clothing", stock: 100 },
{ name: "Organic Coffee Beans", price: 150000, category: "Food", stock: 500 },
{ name: "Wireless Mouse", price: 500000, category: "Electronics", stock: 200 }
])
Kết quả mong đợi: Trả về object với acknowledged: true và mảng insertedIds chứa các ObjectId tương ứng cho từng document.
Verify dữ liệu đã chèn
Đếm số lượng document hiện có trong collection để xác nhận thao tác insert thành công.
db.products.countDocuments()
Kết quả mong đợi: Số nguyên 5 (1 từ insertOne + 4 từ insertMany).
Thực hiện thao tác Query (Lọc dữ liệu)
Query tất cả dữ liệu
Lệnh cơ bản nhất là lấy toàn bộ document trong collection. Dùng find() không có tham số.
db.products.find()
Kết quả mong đợi: Hiển thị danh sách tất cả 5 document dưới dạng JSON. Dữ liệu có thể dài nên cần cuộn hoặc xem từng phần.
Query với điều kiện đơn giản (EQ)
Lọc các sản phẩm thuộc danh mục "Electronics". Cú pháp: db.collection.find({ field: value }).
db.products.find({ category: "Electronics" })
Kết quả mong đợi: Chỉ hiển thị 3 document (iPhone, Samsung, Wireless Mouse). Các document khác bị ẩn.
Query với điều kiện phức tạp (Operators)
Chúng ta cần lọc các sản phẩm có giá lớn hơn 500.000 VÀ số lượng tồn kho nhỏ hơn 100. Sử dụng các toán tử $gt (greater than) và $lt (less than).
db.products.find({
price: { $gt: 500000 },
stock: { $lt: 100 }
})
Kết quả mong đợi: Hiển thị các document thỏa mãn cả 2 điều kiện (iPhone, Samsung, Nike). Coffee Beans bị loại vì giá thấp, Wireless Mouse bị loại vì stock cao.
Query với toán tử OR và Limit/Sort
Lọc các sản phẩm có giá > 20.000.000 HOẶC stock < 50. Sau đó sắp xếp giảm dần theo giá và chỉ lấy 2 kết quả đầu tiên.
db.products.find({
$or: [
{ price: { $gte: 20000000 } },
{ stock: { $lt: 50 } }
]
}).sort({ price: -1 }).limit(2)
Kết quả mong đợi: Trả về tối đa 2 document, được sắp xếp từ giá cao nhất xuống thấp nhất, trong đó có chứa các sản phẩm thỏa mãn ít nhất một điều kiện OR.
Verify kết quả Query
Để kiểm tra query có đúng ý nghĩa không, hãy so sánh số lượng kết quả trả về.
db.products.find({ category: "Clothing" }).count()
Kết quả mong đợi: Số nguyên 1 (chỉ có Nike Air Force 1).
Thực hiện thao tác Update và Delete
Update một Document duy nhất
Sử dụng updateOne để cập nhật một document dựa trên điều kiện lọc (filter). Chúng ta sẽ tăng giá của "iPhone 15 Pro" lên 27.000.000.
Sử dụng toán tử $set để chỉ cập nhật các trường cụ thể mà không ghi đè toàn bộ document.
db.products.updateOne(
{ name: "iPhone 15 Pro" },
{ $set: { price: 27000000, updatedDate: new Date() } }
)
Kết quả mong đợi: Object trả về với matchedCount: 1 (tìm thấy 1 document) và modifiedCount: 1 (đã sửa 1 document).
Update nhiều Document cùng lúc
Giảm giá 10% cho tất cả các sản phẩm thuộc danh mục "Electronics". Sử dụng updateMany kết hợp với toán tử $mul (nhân).
db.products.updateMany(
{ category: "Electronics" },
{ $mul: { price: 0.9 } }
)
Kết quả mong đợi: Object với matchedCount: 3 (tìm thấy 3 sản phẩm Electronics) và modifiedCount: 3 (đã cập nhật 3 sản phẩm). Giá của các sản phẩm này đã giảm.
Delete một Document
Xóa sản phẩm "Organic Coffee Beans" khỏi database. Dùng deleteOne để xóa an toàn (chỉ xóa document đầu tiên thỏa mãn).
db.products.deleteOne({ name: "Organic Coffee Beans" })
Kết quả mong đợi: Object với deletedCount: 1.
Delete nhiều Document (Cẩn thận)
Xóa tất cả các sản phẩm có giá dưới 500.000. Dùng deleteMany. Chú ý: Nếu không có filter, deleteMany({}) sẽ xóa toàn bộ collection.
db.products.deleteMany({ price: { $lt: 500000 } })
Kết quả mong đợi: Object với deletedCount tương ứng số lượng sản phẩm giá thấp đã bị xóa.
Verify kết quả Update và Delete
Kiểm tra lại dữ liệu hiện tại trong collection để đảm bảo giá đã thay đổi và sản phẩm đã bị xóa.
db.products.find().pretty()
Kết quả mong đợi: Danh sách hiển thị các sản phẩm còn lại. Giá của "iPhone 15 Pro" và các sản phẩm Electronics đã giảm. Sản phẩm "Organic Coffee Beans" không còn xuất hiện.
db.products.countDocuments()
Kết quả mong đợi: Số lượng document giảm so với ban đầu (ví dụ từ 5 còn 4 hoặc 3 tùy vào bước delete).
Điều hướng series:
Mục lục: Series: Triển khai Database Document với MongoDB và Ubuntu 24.04
« Phần 3: Cấu hình bảo mật, xác thực và quản lý user MongoDB
Phần 5: Tối ưu hóa hiệu năng với Indexing và Query Plan »