sql >> Databáze >  >> NoSQL >> MongoDB

Jednodušší způsob aktualizace pole pomocí MongoDB

Pokud vám „záleží“ na tom, abyste sem přidali trochu více funkcí (velmi doporučeno) a omezili režii aktualizací, kde opravdu nemusíte vracet upravený dokument, nebo i když ano, je vždy lepší použít atomické operátory s poli jako $push a $addToSet .

„Dodatečná funkčnost“ je také v tom, že při použití polí v úložišti je opravdu moudrá praxe ukládat „délku“ nebo „počet“ položek. To se stává užitečným v dotazech a lze k němu efektivně přistupovat pomocí „indexu“, na rozdíl od jiných metod získávání „počtu“ pole nebo používání tohoto „počtu/délky“ pro účely filtrování.

Zde je lepší konstrukce použít "hromadné" operace protože testování přítomných prvků pole se dobře neslučuje s konceptem „upserts“, takže pokud chcete funkčnost upsert a testování pole, je lepší ve dvou operacích. Ale protože "hromadné" operace mohou být odeslány na server s "jedním požadavkem" a také dostanete "jednu odpověď", pak to snižuje skutečnou režii při tom.

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
});

Funguje to tak, že se při prvním pokusu o nalezení dokumentu, kde prvek pole, který má být přidán, již v poli nenachází. Zde se neprovádí žádný pokus o "upsert", protože nechcete vytvořit nový dokument, pokud jediným důvodem, proč neodpovídá dokumentu, je nepřítomnost prvku pole. Ale kde je shoda, pak se nový člen přidá do pole a aktuální "počet" se "zvýší" o 1 prostřednictvím $inc , který zachovává celkový počet nebo délku.

Druhý příkaz bude tedy odpovídat pouze dokumentu, a proto používá "upsert", protože pokud dokument není nalezen pro klíčové pole, bude vytvořen. Protože všechny operace jsou uvnitř $setOnInsert pak nebude provedena žádná operace, pokud dokument již existuje.

Všechno je to opravdu jen jeden požadavek a odpověď serveru, takže neexistuje žádné "tam a zpět" pro zahrnutí dvou aktualizačních operací, a díky tomu je to efektivní.

Odstranění položky pole je v podstatě obrácené, až na to, že tentokrát není potřeba „vytvářet“ nový dokument, pokud nebyl nalezen:

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
});

Nyní tedy stačí otestovat, kde je prvek pole přítomen a kde je potom $pull odpovídající prvek z obsahu pole a zároveň "snížením" "počtu" o 1, aby odpovídalo odstranění.

Nyní "můžete" použít $addToSet místo toho zde, protože se pouze podívá na obsah pole a pokud nebude člen nalezen, bude přidán a ze stejných důvodů není potřeba testovat existující prvek pole při použití $pull protože to prostě neudělá nic, pokud tam prvek není. Dále $addToSet v tomto kontextu lze použít přímo v "upsert", pokud se "nekřížíte cesty", protože není povoleno zkoušet a používat více operátorů aktualizace na stejné cestě s MongoDB:

FollowModel.update(
    { "facebookId": req.user.facebookId },
    {
        "$setOnInsert": {
            "fans": []
        },
        "$addToSet": { "players": req.body.idToFollow }
    },
    { "upsert": true },
    function(err,numAffected) {
        // handling in here
    }
);

Ale to by bylo "špatné":

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
    }
);

Tím však ztratíte funkci "počítání", protože takové operace se pouze dokončují bez ohledu na to, co tam skutečně je nebo zda bylo něco "přidáno" nebo "odebráno".

Udržování „počítadel“ je opravdu dobrá věc, a i když pro ně právě teď nemáte okamžité využití, pak je v určité fázi životního cyklu vaší aplikace pravděpodobně budete chtít. Má tedy velký smysl porozumět související logice a implementovat ji hned. Malá cena, kterou nyní zaplatíte za mnoho výhod později.

Rychlá vedlejší poznámka, protože obecně doporučuji „hromadné“ operace, kde je to možné. Při použití prostřednictvím .collection accessor v mongoose, pak si musíte být vědomi toho, že se jedná o nativní metody ovladače, a proto se chovají jinak než metody "mongoose".

Pozoruhodné je, že všechny „mongoose“ metody mají vestavěnou „kontrolu“, která ukazuje, že připojení k databázi je aktuálně aktivní. Tam, kde tomu tak není, je operace účinně „řazena do fronty“, dokud není navázáno spojení. Při použití nativních metod již tato „kontrola“ neexistuje. Proto si buď musíte být jisti, že je již přítomno připojení z metody „mongoose“, která provedla „první“, nebo alternativně zabalit celou logiku aplikace do konstrukce, která „čeká“ na vytvoření připojení:

mongoose.connection.on("open",function(err) {
    // All app logic or start in here
});

Tímto způsobem máte jistotu, že existuje připojení a že metody mohou vrátit a použít správné objekty. Ale žádné připojení a „hromadné“ operace selžou.



  1. Řešení MongoDB pro dokument větší než 16 MB?

  2. Automatické doplňování s java, Redis, Elastic Search, Mongo

  3. Nelze se připojit k MongoDB

  4. Vícejazyčné datové modelování na MongoDB