Tìm kiếm sâu 2 cấp độ với $where (JavaScript)
$where cho phép chạy JavaScript tùy ý để tìm document. Dùng khi cấu trúc lồng không cố định hoặc phức tạp.
1. Dữ liệu: Object lồng 2 cấp (FirstPosition, SecondPosition)
> db.demo468.insertOne({
_id: new ObjectId(),
FirstPosition: { StudentName: "Chris", StudentAge: 23 },
SecondPosition: { StudentName: "David", StudentAge: 20 }
});
> db.demo468.insertOne({
_id: new ObjectId(),
FirstPosition: { StudentName: "Carol", StudentAge: 21 },
SecondPosition: { StudentName: "John", StudentAge: 22 }
});
2. Tìm document có StudentName = "John" ở bất kỳ cấp nào
> db.demo468.find({
$where: function() {
for (var key in this) {
if (this[key] && this[key]["StudentName"] == "John") {
return true;
}
}
return false;
}
});
3. Kết quả
{
"_id": ObjectId("5e804fb0b0f3fa88e227906a"),
"FirstPosition": { "StudentName": "Carol", "StudentAge": 21 },
"SecondPosition": { "StudentName": "John", "StudentAge": 22 }
}
⚠️ Cảnh báo quan trọng về $where
| Vấn đề | Mô tả |
|---|---|
| 🚫 Không dùng index | Quét toàn bộ collection (COLLSCAN) |
| 🚫 Chạy JavaScript engine | Chậm hơn operator gốc hàng chục lần |
| 🚫 Rủi ro injection | Nếu input từ user không sanitize |
| 🚫 Không hoạt động sharded cluster | Giới hạn môi trường production |
Giải pháp thay thế TỐT HƠN
Cách 1: $or với dot notation (nếu biết tên trường)
db.demo468.find({
$or: [
{ "FirstPosition.StudentName": "John" },
{ "SecondPosition.StudentName": "John" }
]
});
Cách 2: Schema design tốt hơn - flatten mảng
// Thay vì object lồng, dùng mảng positions
{
positions: [
{ type: "FirstPosition", StudentName: "Chris", StudentAge: 23 },
{ type: "SecondPosition", StudentName: "David", StudentAge: 20 }
]
}
// Truy vấn đơn giản, dùng index
db.collection.find({ "positions.StudentName": "John" });
Cách 3: $expr + $objectToArray (MongoDB 3.6+)
db.demo468.find({
$expr: {
$in: [
"John",
{ $map: {
input: { $objectToArray: "$$ROOT" },
as: "field",
in: "$$field.v.StudentName"
}}
]
}
});
Tóm lại: Chỉ dùng $where khi bất khả kháng. Luôn ưu tiên redesign schema hoặc dùng operator gốc.