sql >> Database teknologi >  >> NoSQL >> MongoDB

Nemmere måde at opdatere et array med MongoDB

Hvis du er ligeglad med at tilføje lidt mere funktionalitet her (anbefales meget) og begrænse overhead af opdateringer, hvor du virkelig ikke behøver at returnere det ændrede dokument, eller selvom du gør det, er det altid bedre at bruge atomoperatorer med arrays som $push og $addToSet .

Den "yderligere funktionalitet" ligger også i, at når du bruger arrays i lager, er det en rigtig klog praksis at gemme "længden" eller "antal" af elementer. Dette bliver nyttigt i forespørgsler og kan tilgås effektivt med et "indeks", i modsætning til andre metoder til at få "antal" af en matrix eller bruge denne "tælle/længde" til filtreringsformål.

Den bedre konstruktion her er at bruge "Bulk"-operationer da test for tilstedeværende array-elementer ikke blander sig godt med begrebet "upserts", så hvor du ønsker upsert-funktionalitet og array-test, er det bedre i to operationer. Men da "Bulk"-operationer kan sendes til serveren med "one request", og du også får "one response", så mindsker dette enhver reel overhead ved at gøre det.

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

Måden dette fungerer på er, at det første forsøg der forsøger at finde et dokument, hvor det array-element, der skal tilføjes, ikke allerede er til stede i arrayet. Der gøres ikke forsøg på en "upsert" her, da du ikke ønsker at oprette et nyt dokument, hvis den eneste grund til, at det ikke matchede et dokument, er fordi array-elementet ikke er til stede. Men hvor det matches, tilføjes det nye medlem til arrayet, og det aktuelle "antal" "inkrementeres" med 1 via $inc , som holder det samlede antal eller længde.

Den anden sætning vil derfor kun matche dokumentet og bruger derfor en "upsert", da hvis dokumentet ikke findes for nøglefeltet, vil det blive oprettet. Da alle operationer er inde i $setOnInsert så udføres der ingen handling, hvis dokumentet allerede eksisterer.

Det hele er i virkeligheden kun én serveranmodning og et svar, så der er ingen "frem og tilbage" for medtagelsen af ​​to opdateringsoperationer, og det gør dette effektivt.

Fjernelse af en array-indgang er dybest set det omvendte, bortset fra at denne gang er det ikke nødvendigt at "oprette" et nyt dokument, hvis det ikke blev fundet:

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 skal du kun teste, hvor array-elementet er til stede, og hvor det så er $pull det matchede element fra matrixindholdet, samtidig med at "tælleren" "nedsættes" med 1 for at afspejle fjernelsen.

Nu "kunne" du bruge $addToSet i stedet her, da det kun vil se på array-indholdet, og hvis medlemmet ikke findes, vil det blive tilføjet, og af stort set samme årsager er det ikke nødvendigt at teste for det eksisterende array-element, når du bruger $pull da det bare ikke gør noget, hvis elementet ikke er der. Desuden $addToSet i den sammenhæng kan bruges direkte i en "upsert", så længe du ikke "krydser stier", da det ikke er tilladt at forsøge at bruge flere opdateringsoperatører på samme sti med MongoDB:

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

Men dette ville være "forkert":

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 ved at gøre det mister du "tælle"-funktionaliteten, da sådanne operationer bare afsluttes uden hensyntagen til, hvad der faktisk er der, eller om noget blev "tilføjet" eller "fjernet".

At holde "tællere" er en rigtig god ting, og selvom du ikke umiddelbart har brug for dem lige nu, så vil du sandsynligvis på et tidspunkt i din ansøgnings livscyklus have dem. Så det giver god mening at forstå logikken og implementere dem nu. Lille pris at betale nu for en masse fordele senere.

Hurtig sidenote her, da jeg generelt anbefaler "Bulk"-operationer, hvor det er muligt. Når du bruger dette via .collection accessor i mongoose, så skal du være opmærksom på, at disse er native driver-metoder og derfor opfører sig anderledes end "mongoose"-metoderne.

Det er bemærkelsesværdigt, at alle "mongoose"-metoder har et indbygget "check" for at se, at forbindelsen til databasen i øjeblikket er aktiv. Hvor den ikke er det, er operationen reelt "kø", indtil forbindelsen er oprettet. Ved at bruge de native metoder er denne "check" ikke længere til stede. Derfor skal du enten være sikker på, at en forbindelse allerede er til stede fra en "mongoose"-metode, der har udført "først", eller alternativt pakke hele din applikationslogik ind i en konstruktion, der "venter" på, at forbindelsen bliver oprettet:

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

På den måde er du sikker på, at der er en forbindelse til stede, og de korrekte objekter kan returneres og bruges af metoderne. Men ingen forbindelse, og "Bulk"-operationerne vil mislykkes.



  1. MongoDb unikke begrænsninger på et datointerval

  2. Deaktiver Transparent Huge Pages fra Kubernetes

  3. Opgradering af ældre mongo-database efter utilsigtet mongo-versionsopgradering

  4. mongodb:skal jeg altid bruge indstillingen 'sikker' på opdateringer