Nếu bạn "thực sự quan tâm" đến việc thêm một chút chức năng ở đây (rất được khuyên) và hạn chế chi phí cập nhật mà bạn thực sự không cần phải trả lại tài liệu đã sửa đổi, hoặc ngay cả khi bạn làm vậy thì tốt hơn hết là sử dụng toán tử nguyên tử với các mảng như $ push
và $ addToSet
.
"Chức năng bổ sung" cũng nằm ở chỗ khi sử dụng mảng trong lưu trữ, việc lưu trữ "chiều dài" hoặc "số lượng" của các mục là một cách thực sự khôn ngoan. Điều này trở nên hữu ích trong các truy vấn và có thể được truy cập hiệu quả bằng "chỉ mục", trái ngược với các phương pháp khác để lấy "số lượng" của một mảng hoặc sử dụng "số lượng / độ dài" đó cho mục đích lọc.
Cách tốt hơn ở đây là sử dụng hoạt động "Hàng loạt" vì kiểm tra các phần tử mảng hiện tại không kết hợp tốt với khái niệm "cảnh báo", vì vậy nếu bạn muốn nâng cấp chức năng thì kiểm tra mảng sẽ tốt hơn trong hai thao tác. Nhưng vì các hoạt động "Hàng loạt" có thể được gửi đến máy chủ với "một yêu cầu" và bạn cũng nhận được "một phản hồi", nên điều này sẽ giảm thiểu mọi chi phí thực sự khi làm như vậy.
var bulk = FollowModel.collection.initializeOrderedBulkOp();
// Try to add where not found in array
bulk.find({
"facebookId": req.user.facebookId,
"players": { "$ne": req.body.idToFollow }
}).updateOne({
"$push": { "players": req.body.idToFollow },
"$inc": { "playerCount": 1 }
});
// Otherwise create the document if not matched
bulk.find({
"facebookId": req.user.facebookId,
}).upsert().updateOne({
"$setOnInsert": {
"players": [req.body.idToFollow]
"playerCount": 1,
"fans": [],
"fanCount": 0
}
})
bulk.execute(function(err,result) {
// Handling in here
});
Cách này hoạt động là nỗ lực đầu tiên cố gắng tìm một tài liệu mà phần tử mảng được thêm vào chưa có trong mảng. Không có nỗ lực nào được thực hiện ở "upert" ở đây vì bạn không muốn tạo một tài liệu mới nếu lý do duy nhất mà nó không khớp với một tài liệu là vì phần tử mảng không có mặt. Nhưng nếu được khớp, thì thành viên mới được thêm vào mảng và "số lượng" hiện tại được "tăng" 1 qua $ inc
, giữ tổng số hoặc độ dài.
Do đó, câu lệnh thứ hai sẽ chỉ khớp với tài liệu và do đó sẽ sử dụng "upert" vì nếu tài liệu không được tìm thấy cho trường khóa thì nó sẽ được tạo. Vì tất cả các thao tác đều nằm trong $ setOnInsert
thì sẽ không có thao tác nào được thực hiện nếu tài liệu đã tồn tại.
Tất cả chỉ thực sự là một yêu cầu và phản hồi của máy chủ, vì vậy không có "qua lại" cho việc bao gồm hai hoạt động cập nhật và điều đó làm cho việc này trở nên hiệu quả.
Việc xóa mục nhập mảng về cơ bản là ngược lại, ngoại trừ việc lần này không cần phải "tạo" một tài liệu mới nếu nó không được tìm thấy:
var bulk = FollowModel.collection.initializeOrderedBulkOp();
// Try to remove where found in array
bulk.find({
"facebookId": req.user.facebookId,
"players": req.body.idToFollow
}).updateOne({
"$pull": { "players": req.body.idToFollow },
"$inc": { "playerCount": -1 }
});
bulk.execute(function(err,result) {
// Handling in here
});
Vì vậy, bây giờ bạn chỉ cần kiểm tra xem phần tử mảng hiện diện ở đâu và sau đó nó ở đâu $ pull
phần tử được so khớp từ nội dung mảng, đồng thời với việc "giảm" "số lượng" đi 1 để phản ánh việc loại bỏ.
Bây giờ bạn "có thể" sử dụng $ addToSet
thay vào đó ở đây vì nó sẽ chỉ xem xét nội dung mảng và nếu thành viên không được tìm thấy thì nó sẽ được thêm vào và vì nhiều lý do tương tự nên không cần kiểm tra phần tử mảng hiện có khi sử dụng $ pull vì nó sẽ không làm gì nếu phần tử không có ở đó. Futhermore
$ addToSet
trong ngữ cảnh đó có thể được sử dụng trực tiếp trong một "upert", miễn là bạn không "cắt ngang" vì nó không được phép thử và sử dụng nhiều toán tử cập nhật trên cùng một đường dẫn với MongoDB:
FollowModel.update(
{ "facebookId": req.user.facebookId },
{
"$setOnInsert": {
"fans": []
},
"$addToSet": { "players": req.body.idToFollow }
},
{ "upsert": true },
function(err,numAffected) {
// handling in here
}
);
Nhưng điều này sẽ là "sai":
FollowModel.update(
{ "facebookId": req.user.facebookId },
{
"$setOnInsert": {
"players": [], // <-- This is a conflict
"fans": []
},
"$addToSet": { "players": req.body.idToFollow }
},
{ "upsert": true },
function(err,numAffected) {
// handling in here
}
);
Tuy nhiên, bằng cách làm đó, bạn mất chức năng "đếm" vì các hoạt động như vậy chỉ đang hoàn thành mà không quan tâm đến những gì thực sự ở đó hoặc nếu bất kỳ điều gì được "thêm" hoặc "xóa".
Giữ "bộ đếm" là một điều thực sự tốt, và ngay cả khi bạn không sử dụng chúng ngay bây giờ, thì ở một số giai đoạn trong vòng đời của ứng dụng, bạn có thể sẽ muốn chúng. Vì vậy, sẽ rất có ý nghĩa khi hiểu logic liên quan và thực hiện chúng ngay bây giờ. Cái giá nhỏ phải trả ngay bây giờ để có nhiều lợi ích sau này.
Ghi chú nhanh ở đây vì tôi thường đề xuất các thao tác "Hàng loạt" nếu có thể. Khi sử dụng điều này qua .collection
truy cập trong mongoose, thì bạn cần lưu ý rằng đây là các phương thức trình điều khiển gốc và do đó hoạt động khác với các phương thức "mongoose".
Đáng chú ý, tất cả các phương thức "mongoose" đều có "kiểm tra" buit-in để xem kết nối với cơ sở dữ liệu hiện đang hoạt động. Nếu không, hoạt động được "xếp hàng" hiệu quả cho đến khi kết nối được thực hiện. Sử dụng các phương pháp gốc, "kiểm tra" này không còn nữa. Do đó, bạn cần phải chắc chắn rằng một kết nối đã có từ một phương thức "mongoose" đã thực thi "đầu tiên" hoặc luân phiên quấn toàn bộ logic ứng dụng của bạn trong một cấu trúc "chờ" kết nối được thực hiện:
mongoose.connection.on("open",function(err) {
// All app logic or start in here
});
Bằng cách đó, bạn chắc chắn rằng có một kết nối hiện tại và các đối tượng chính xác có thể được trả về và sử dụng bởi các phương thức. Nhưng không có kết nối và các hoạt động "Hàng loạt" sẽ không thành công.