Ако ви е „интересно“ да добавите малко повече функционалност тук (много препоръчително) и да ограничите режийните разходи за актуализации, където наистина няма нужда да връщате модифицирания документ, или дори да го направите, винаги е по-добре да използвате атомарни оператори с масиви като $push
и $addToSet
.
„Допълнителната функционалност“ също е в това, че когато използвате масиви в хранилището, е наистина разумна практика да съхранявате „дължината“ или „броя“ на елементите. Това става полезно при заявки и може да бъде достъпно ефективно с "индекс", за разлика от други методи за получаване на "броя" на масив или използване на този "брой/дължина" за целите на филтриране.
По-добрата конструкция тук е да използвате "Групови" операции тъй като тестването за налични елементи на масив не се смесва добре с концепцията за "upserts", така че когато искате функция upsert, тестване на масив е по-добро в две операции. Но тъй като „Груповите“ операции могат да бъдат изпратени до сървъра с „една заявка“ и вие също получавате „един отговор“, тогава това смекчава всички реални режийни разходи при това.
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
});
Начинът, по който това работи, е, че първият опит там се опитва да намери документ, където елементът на масива, който трябва да се добави, вече не присъства в масива. Тук не се прави опит за "upsert", тъй като не искате да създавате нов документ, ако единствената причина, поради която не съответства на документ, е, че елементът на масива не присъства. Но когато има съвпадение, новият член се добавя към масива и текущият „брой“ се „увеличава“ с 1 чрез $inc
, което запазва общия брой или дължина.
Следователно второто изявление ще съответства само на документа и следователно използва "upsert", тъй като ако документът не бъде намерен за ключовото поле, той ще бъде създаден. Тъй като всички операции са вътре в $setOnInsert
тогава няма да се извърши операция, ако документът вече съществува.
Всичко е просто една заявка и отговор на сървъра, така че няма „напред и назад“ за включването на две операции за актуализиране и това прави това ефективно.
Премахването на запис в масив е основно обратното, с изключение на това, че този път няма нужда да „създавате“ нов документ, ако не е намерен:
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
});
Така че сега трябва само да тествате къде присъства елементът на масива и къде е след това $pull
съвпадащият елемент от съдържанието на масива, в същото време като „намалява“ „броя“ с 1, за да отрази премахването.
Сега можете да използвате $addToSet
вместо това тук, тъй като просто ще разгледа съдържанието на масива и ако членът не бъде намерен, той ще бъде добавен и по почти същите причини няма нужда да се тества за съществуващия елемент на масива, когато се използва $pullкод> тъй като просто няма да направи нищо, ако елементът не е там. Освен това
$addToSet
в този контекст може да се използва директно в рамките на „upsert“, стига да не „пресичате пътеки“, тъй като не е позволено да се опитвате и използвате множество оператори за актуализиране по един и същ път с MongoDB:
FollowModel.update(
{ "facebookId": req.user.facebookId },
{
"$setOnInsert": {
"fans": []
},
"$addToSet": { "players": req.body.idToFollow }
},
{ "upsert": true },
function(err,numAffected) {
// handling in here
}
);
Но това би било "погрешно":
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
}
);
Правейки това обаче, вие губите функционалността за „броене“, тъй като такива операции просто завършват, без да се вземат предвид какво всъщност има или дали нещо е „добавено“ или „премахнато“.
Поддържането на „броячи“ е наистина добро нещо и дори да не ги използвате незабавно в момента, тогава на някакъв етап от жизнения цикъл на вашето приложение вероятно ще ги искате. Така че има много смисъл да разберете включената логика и да ги приложите сега. Малка цена, която трябва да платите сега за много ползи по-късно.
Бърза бележка тук, тъй като обикновено препоръчвам „Групови“ операции, когато е възможно. Когато използвате това чрез .collection
accessor в mongoose, тогава трябва да сте наясно, че това са собствени методи на драйвер и следователно се държат различно от методите на "mongoose".
Трябва да се отбележи, че всички "mongoose" методи имат вградена "проверка", за да видите дали връзката към базата данни е активна в момента. Когато не е, операцията е на практика "на опашка", докато връзката бъде осъществена. При използване на собствените методи тази "проверка" вече не присъства. Следователно или трябва да сте сигурни, че връзката вече е налице от метод "mongoose", който е изпълнил "first", или алтернативно да обвиете цялата си логика на приложението в конструкция, която "чака" връзката да бъде осъществена:
mongoose.connection.on("open",function(err) {
// All app logic or start in here
});
По този начин сте сигурни, че има налична връзка и правилните обекти могат да бъдат върнати и използвани от методите. Но няма връзка и „Груповите“ операции ще се провалят.