Interview Prep
PracticePortfolio

Categories

  • HTML & CSS41
  • JavaScript40
  • TypeScript42
  • ReactJS40
  • Next.js44
  • Node.js41
  • NestJS44
  • SQL & Database43
  • MongoDB40
  • Git40
  • Docker44
  • GraphQL47
Interview PrepMongoDB

MongoDB

40 questions

20 / 40 questions

1

MongoDB là một NoSQL document database: dữ liệu lưu dưới dạng document giống JSON thay vì hàng-cột như SQL. Nó schema-flexible (không cần định nghĩa cấu trúc cố định trước), scale ngang tốt qua sharding, và tối ưu cho dữ liệu bán cấu trúc, thay đổi nhanh. Phù hợp khi model dữ liệu linh hoạt và cần đọc/ghi nhanh theo document.

2

Document là đơn vị lưu trữ cơ bản trong MongoDB — một tập cặp field: value, tương tự object JSON. Trên đĩa và trên đường truyền, MongoDB dùng BSON (Binary JSON): dạng nhị phân của JSON, hỗ trợ thêm kiểu dữ liệu mà JSON không có như Date, ObjectId, Binary, Decimal128, Int32/Int64. BSON đọc/ghi và duyệt (traverse) nhanh hơn nhờ có prefix độ dài field.

3

Collection ≈ table, document ≈ row, field ≈ column. Khác biệt cốt lõi: các document trong cùng collection không bắt buộc cùng schema — mỗi document có thể có field khác nhau. Ngoài ra document lồng nhau (nested) và mảng nằm ngay trong một document, thay vì tách ra nhiều bảng và join như SQL.

4

Mỗi document bắt buộc có field _id làm primary key, unique trong collection. Nếu không cung cấp, MongoDB tự sinh một ObjectId — 12 byte gồm timestamp (4 byte) + random (5 byte) + counter (3 byte). Vì phần đầu là timestamp nên ObjectId gần như tăng dần theo thời gian và có thể suy ra thời điểm tạo bằng _id.getTimestamp().

5

SQL là relational (schema cố định, chuẩn hóa, join, giao dịch mạnh); MongoDB là document-oriented (schema linh hoạt, dữ liệu thường denormalize/nhúng, scale ngang tự nhiên). SQL dùng JOIN nhiều bảng, MongoDB ưu tiên gom dữ liệu liên quan vào một document để đọc một lần. Chọn MongoDB khi model thay đổi nhanh và pattern truy cập rõ ràng; chọn SQL khi cần quan hệ phức tạp và tính toàn vẹn giao dịch chặt.

6

Embedding (nhúng document con vào cha) hợp khi dữ liệu đi cùng nhau, đọc chung và ít khi tồn tại độc lập — đọc 1 lần là đủ. Referencing (lưu _id trỏ sang collection khác) hợp khi dữ liệu lớn, dùng lại ở nhiều nơi, hoặc quan hệ nhiều-nhiều. Nguyên tắc: data cùng truy cập thì để cùng chỗ.

7

One-to-many có 3 mức theo số lượng 'many': one-to-few nhúng mảng con thẳng vào cha; one-to-many vừa phải cho phía con giữ reference parentId; one-to-squillions (log...) bắt buộc reference từ con sang cha để tránh document phình quá 16MB. Many-to-many lưu mảng reference ở một hoặc cả hai phía (ví dụ student.courseIds và/hoặc course.studentIds) — chỉ nhân đôi khi cần truy vấn cả hai chiều để tránh phải đồng bộ hai nơi.

8

Create với insertOne / insertMany; Read với find / findOne; Update với updateOne / updateMany / replaceOne; Delete với deleteOne / deleteMany.

db.users.insertOne({ name: "Hoang", age: 28 });
db.users.find({ age: { $gt: 18 } });
db.users.updateOne({ name: "Hoang" }, { $set: { age: 29 } });
db.users.deleteOne({ name: "Hoang" });
9

So sánh: $gt / $gte / $lt / $lte (lớn/nhỏ hơn), $ne (khác), $in / $nin (nằm trong / không nằm trong mảng giá trị). Logic: $or, $and, $nor, $not. Còn $exists kiểm tra field tồn tại, $regex khớp chuỗi theo pattern. Ví dụ: db.products.find({ price: { $gte: 100, $lte: 500 }, tag: { $in: ["hot", "new"] } }).

10

$regex khớp chuỗi theo biểu thức chính quy: db.users.find({ name: { $regex: /^Ho/, $options: "i" } }). Chỉ pattern neo đầu chuỗi (^abc) mới dùng được index; regex ở giữa chuỗi hoặc không phân biệt hoa thường (i) thường phải quét toàn bộ (collection scan). Với tìm kiếm văn bản nên cân nhắc dùng text index thay vì regex.

11

$set gán giá trị field; $inc tăng/giảm số; $push thêm phần tử vào mảng; $pull xóa phần tử khớp điều kiện khỏi mảng; $addToSet thêm vào mảng chỉ khi chưa tồn tại (giữ unique). Còn $unset (xóa field), $rename, $pop.

db.users.updateOne(
  { _id: id },
  { $inc: { loginCount: 1 }, $addToSet: { roles: "admin" } }
);
12

Projection chọn field trả về để giảm băng thông và bộ nhớ. Truyền document thứ hai vào find: 1 để lấy, 0 để bỏ, ví dụ db.users.find({ active: true }, { name: 1, email: 1, _id: 0 }). _id luôn trả về trừ khi tắt tường minh. Không được trộn include và exclude trong cùng projection (ngoại trừ _id).

13

Upsert = update or insert: nếu tìm thấy document khớp thì cập nhật, không thì tạo mới. Bật bằng option { upsert: true }. Hữu ích cho counter, cache, hoặc đồng bộ dữ liệu mà không cần kiểm tra tồn tại trước.

db.stats.updateOne(
  { day: "2026-07-01" },
  { $inc: { visits: 1 } },
  { upsert: true }
);
14

Dot notation cho phép truy cập field lồng trong sub-document hoặc phần tử mảng bằng chuỗi "parent.child", đặt trong dấu nháy. Ví dụ db.users.find({ "address.city": "Hanoi" }) hoặc cập nhật { $set: { "profile.age": 30 } }. Với mảng có thể dùng chỉ số ("items.0.qty") hoặc toán tử vị trí $.

15

Aggregation pipeline là chuỗi các stage xử lý document tuần tự, output của stage này là input của stage sau — giống pipe trong Unix. Dùng để lọc, nhóm, biến đổi, join, tính toán phức tạp mà find không làm được. Mỗi stage bắt đầu bằng toán tử $ như $match, $group, $project. Stage $facet còn cho phép chạy nhiều pipeline con song song để lấy nhiều thống kê trong một lần truy vấn (hợp cho dashboard/search).

db.orders.aggregate([
  { $match: { status: "paid" } },
  { $group: { _id: "$userId", total: { $sum: "$amount" } } }
]);
16

$match lọc document (giống WHERE) — nên đặt sớm để giảm dữ liệu. $group gom theo _id và tính tổng hợp ($sum, $avg, $max, $min) giống GROUP BY. $project định hình lại document: chọn/đổi tên/tạo field tính toán. $sort sắp xếp (1 tăng, -1 giảm), $limit giới hạn số document — MongoDB tối ưu gộp $sort + $limit để chỉ giữ top-N thay vì sắp xếp toàn bộ. Thứ tự stage quyết định hiệu năng và kết quả.

17

$unwind trải mảng: biến một document có field mảng N phần tử thành N document, mỗi document ứng với một phần tử. Thường dùng trước $group để tổng hợp theo từng phần tử mảng. Option preserveNullAndEmptyArrays: true giữ lại document có mảng rỗng/null thay vì loại bỏ.

db.orders.aggregate([
  { $unwind: "$items" },
  { $group: { _id: "$items.sku", qty: { $sum: "$items.qty" } } }
]);
18

$lookup thực hiện left outer join giữa hai collection trong aggregation — khớp field local với foreign và gắn kết quả vào một mảng. Khác SQL JOIN: kết quả lồng vào document (thường cần $unwind sau đó), và $lookup thường kém hiệu quả hơn JOIN của RDBMS, nhất là khi thiếu index. Vì vậy MongoDB khuyến khích thiết kế để hạn chế join.

db.orders.aggregate([
  { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } }
]);
19

$expr cho phép dùng aggregation expression bên trong query filter (find, $match), nhờ đó có thể so sánh hai field trong cùng document. Query thường không so field-với-field được, $expr giải quyết điều đó.

db.orders.find({ $expr: { $gt: ["$spent", "$budget"] } });

Lưu ý: $expr có thể không tận dụng index tốt như query operator thông thường.

20

Index là cấu trúc B-tree lưu trước giá trị field đã sắp xếp để tìm kiếm nhanh, tránh collection scan (quét toàn bộ). Không có index, truy vấn phải đọc mọi document. Đánh đổi: index tăng tốc đọc nhưng làm chậm ghi (phải cập nhật index) và tốn dung lượng. _id luôn có index mặc định.

db.users.createIndex({ email: 1 }, { unique: true });