Sửa lỗi 'Projection cannot have a mix of inclusion and exclusion' trong MongoDB

beginner🍃 MongoDB2026-07-06| MongoDB Server (Phiên bản 3.0+), Node.js MongoDB Driver, Mongoose, PyMongo, MongoDB Shell (mongosh).

Error Message

MongoServerError: Projection cannot have a mix of inclusion and exclusion
#mongodb#projection#backend#mongoose#database-errors

Thông báo lỗi

Bạn đang chạy một truy vấn, nhưng thay vì nhận được dữ liệu, bảng điều khiển lại hiển thị thông báo lỗi khó chịu này:

MongoServerError: Projection cannot have a mix of inclusion and exclusion

Điều này xảy ra vì MongoDB yêu cầu một chiến lược rõ ràng để lọc các trường. Bạn phải cho cơ sở dữ liệu biết chính xác những gì bạn muốn hoặc chính xác những gì bạn không muốn. Nó sẽ không cho phép bạn thực hiện cả hai cùng một lúc.

Tại sao lỗi này xảy ra

Projection là cách bạn định hình kết quả của mình. Trong một truy vấn find(), đối số thứ hai là đối tượng projection. MongoDB thực thi một logic nghiêm ngặt: bạn phải chọn giữa whitelist (danh sách trắng) hoặc blacklist (danh sách đen).

  • Inclusion (Bao gồm - Whitelist): Bạn liệt kê các trường mình cần với giá trị 1. MongoDB sẽ bỏ qua mọi thứ khác. Ví dụ: { username: 1, email: 1 }.
  • Exclusion (Loại trừ - Blacklist): Bạn liệt kê các trường muốn ẩn với giá trị 0. MongoDB sẽ trả về tất cả các trường còn lại. Ví dụ: { password: 0, ssn: 0 }.

Xung đột phát sinh khi bạn cố gắng trộn lẫn hai loại này, chẳng hạn như { username: 1, password: 0 }. MongoDB dừng truy vấn vì nó không biết nên coi các trường không được liệt kê là được bao gồm hay bị loại trừ.

Ngoại lệ duy nhất: Trường _id

Trường _id là một ngoại lệ. Vì MongoDB mặc định bao gồm _id, bạn được phép loại trừ nó một cách rõ ràng trong khi vẫn đưa các trường khác vào whitelist. Một projection như { email: 1, _id: 0 } là hoàn toàn hợp lệ và rất phổ biến trong phát triển API.

Cách khắc phục truy vấn

Bước 1: Xác định xung đột

Kiểm tra mã truy vấn của bạn. Trong môi trường Node.js hoặc Mongoose điển hình, lỗi thường nằm ở một dòng như thế này:

// ❌ Lệnh này bị lỗi vì trộn lẫn 1 và 0
const user = await db.collection('users').find({}, { 
  projection: { name: 1, hashed_password: 0 } 
});

Bước 2: Chọn một chiến lược

Quyết định phương pháp nào phù hợp hơn cho trường hợp sử dụng cụ thể của bạn. Thông thường, whitelist là lựa chọn tốt hơn về mặt bảo mật.

Tùy chọn A: Chỉ sử dụng Inclusion

Nếu bạn chỉ cần ba trường từ một tài liệu có năm mươi trường, chỉ cần liệt kê ba trường đó. Nó sẽ sạch sẽ và nhanh hơn.

// ✅ Chính xác: Chỉ trả về name và email
db.collection('users').find({}, { 
  projection: { name: 1, email: 1, _id: 0 } 
});

Tùy chọn B: Chỉ sử dụng Exclusion

Sử dụng cách này khi bạn muốn lấy hầu hết dữ liệu nhưng cần ẩn các trường nội bộ nhạy cảm như token hoặc giá trị salt.

// ✅ Chính xác: Trả về mọi thứ NGOẠI TRỪ các trường nhạy cảm
db.collection('users').find({}, { 
  projection: { password: 0, internal_notes: 0, __v: 0 } 
});

Aggregations và giai đoạn $project

Logic tương tự cũng áp dụng cho các aggregation pipeline của bạn. Nếu giai đoạn $project của bạn trộn lẫn 1 và 0, toàn bộ pipeline sẽ thất bại. Đây là cách sửa một aggregation bị lỗi:

// ❌ Aggregation bị lỗi
{ $project: { total_price: 1, 'customer.address': 0 } }

// ✅ Đã sửa (Chỉ dùng Inclusion)
{ $project: { total_price: 1, order_date: 1, items: 1 } }

Mẹo thực tế cho môi trường Production

  • Bảo mật là trên hết: Luôn ưu tiên inclusion (whitelist). Nếu sau này bạn thêm một trường nhạy cảm mới vào schema, một truy vấn dựa trên exclusion sẽ vô tình làm rò rỉ trường mới đó cho đến khi bạn cập nhật blacklist một cách thủ công.
  • Tăng hiệu suất: Projection không chỉ là để làm sạch dữ liệu. Nếu projection của bạn chỉ bao gồm các trường là một phần của index, MongoDB có thể trả về kết quả mà không cần đọc toàn bộ tài liệu từ đĩa. Điều này được gọi là "Covered Query".
  • Phím tắt trong Mongoose: Nếu bạn sử dụng Mongoose, bạn có thể sử dụng cú pháp chuỗi như .select('name email -_id'). Mongoose đủ thông minh để xử lý các giá trị 1 và 0 cho bạn ở hậu trường.

Tóm tắt nhanh

Để ngăn chặn lỗi này, hãy nhìn vào đối tượng projection của bạn. Nếu bạn thấy 10 trong cùng một khối, hãy xóa một trong hai. Giữ lại các số 1 để xác định chính xác những gì bạn muốn, hoặc giữ lại các số 0 để xác định những gì cần ẩn. Chỉ cần nhớ rằng _id: 0 là ngoại lệ duy nhất được phép.

Related Error Notes