sql >> Databasteknik >  >> NoSQL >> MongoDB

Enklare sätt att uppdatera en array med MongoDB

Om du "bryr dig" om att lägga till lite mer funktionalitet här (rekommenderas mycket) och begränsa omkostnader för uppdateringar där du verkligen inte behöver returnera det modifierade dokumentet, eller även om du gör det är det alltid bättre att använda atomoperatorer med arrayer som $push och $addToSet .

Den "ytterligare funktionaliteten" ligger också i att när man använder arrayer i lagring, är det en riktigt klok praxis att lagra "längden" eller "antal" av objekt. Detta blir användbart i frågor och kan nås effektivt med ett "index", i motsats till andra metoder för att få "räknevärdet" för en array eller använda det "antal/längd" för filtreringsändamål.

Den bättre konstruktionen här är att använda "Bulk"-operationer eftersom testning för närvarande arrayelement inte blandas väl med konceptet "upserts", så där du vill ha upsert-funktionalitet och array-testning är det bättre i två operationer. Men eftersom "Bulk"-operationer kan skickas till servern med "en begäran" och du också får "ett svar", så minskar detta eventuella verkliga omkostnader.

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

Sättet detta fungerar är att det första försöket där försöker hitta ett dokument där arrayelementet som ska läggas inte redan finns i arrayen. Inget försök görs till en "upsert" här eftersom du inte vill skapa ett nytt dokument om den enda anledningen till att det inte matchade ett dokument är att arrayelementet inte finns. Men när den matchas läggs den nya medlemmen till i arrayen och det aktuella "antal" "ökas" med 1 via $inc , som behåller det totala antalet eller längden.

Den andra satsen kommer därför endast att matcha dokumentet och använder därför en "upsert" eftersom om dokumentet inte hittas för nyckelfältet kommer det att skapas. Eftersom alla operationer är inuti $setOnInsert då kommer ingen operation att utföras om dokumentet redan finns.

Allt är egentligen bara en serverbegäran och ett svar, så det finns inget "fram och tillbaka" för att inkludera två uppdateringsoperationer, och det gör detta effektivt.

Att ta bort en array-post är i princip det omvända, förutom att den här gången finns det inget behov av att "skapa" ett nytt dokument om det inte hittades:

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

Så nu behöver du bara testa var arrayelementet finns och var det sedan är $pull det matchade elementet från arrayinnehållet, samtidigt som "minska" "räkningen" med 1 för att återspegla borttagningen.

Nu "kunde" du använda $addToSet istället här eftersom det bara kommer att titta på arrayinnehållet och om medlemmen inte hittas så kommer den att läggas till, och av ungefär samma anledningar finns det inget behov av att testa för arrayelementet som finns när du använder $pull eftersom det bara gör ingenting om elementet inte är där. Dessutom $addToSet i det sammanhanget kan användas direkt i en "upsert", så länge du inte "korsar vägar" eftersom det inte är tillåtet att försöka använda flera uppdateringsoperatorer på samma väg med MongoDB:

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

Men detta skulle vara "fel":

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

Men genom att göra det förlorar du "räkne"-funktionaliteten eftersom sådana operationer bara slutförs utan hänsyn till vad som faktiskt finns där eller om något "läggs till" eller "togs bort".

Att hålla "räknare" är en riktigt bra sak, och även om du inte har någon omedelbar användning för dem just nu, kommer du förmodligen att vilja ha dem i något skede av din applikations livscykel. Så det är mycket vettigt att förstå logiken som är involverad och implementera dem nu. Litet pris att betala nu för mycket nytta senare.

Notera snabbt här eftersom jag generellt rekommenderar "Bulk"-operationer där det är möjligt. När du använder detta via .collection accessor i mongoose, då måste du vara medveten om att dessa är inbyggda drivrutiner och därför beter sig annorlunda än "mongoose"-metoderna.

Noterbart är att alla "mongoose"-metoder har en inbyggd "check" för att se att anslutningen till databasen för närvarande är aktiv. Där den inte är det, är operationen i praktiken "köad" tills anslutningen görs. Med hjälp av de ursprungliga metoderna är denna "check" inte längre närvarande. Därför måste du antingen vara säker på att en anslutning redan finns från en "mongoose"-metod som har körts "först", eller alternativt linda in hela applikationslogiken i en konstruktion som "väntar" på att anslutningen ska göras:

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

På så sätt är du säker på att det finns en anslutning och att rätt objekt kan returneras och användas av metoderna. Men ingen anslutning, och "Bulk"-operationerna kommer att misslyckas.



  1. Är det möjligt att casta i en MongoDB-Query?

  2. Redis Config Set med Node jS

  3. Konstruera ett personligt Facebook-liknande nyhetsflöde:SQL, MongoDB?

  4. Omvänd paginering genom en Redis sorterad uppsättning