MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Un moyen plus simple de mettre à jour un tableau avec MongoDB

Si vous "vous souciez" d'ajouter un peu plus de fonctionnalités ici (très conseillé) et de limiter la surcharge des mises à jour où vous n'avez vraiment pas besoin de renvoyer le document modifié, ou même si vous le faites, il est toujours préférable d'utiliser des opérateurs atomiques avec des tableaux comme $push et $addToSet .

La "fonctionnalité supplémentaire" réside également dans le fait que lors de l'utilisation de tableaux dans le stockage, il est très judicieux de stocker la "longueur" ou le "nombre" d'éléments. Cela devient utile dans les requêtes et peut être consulté efficacement avec un "index", par opposition à d'autres méthodes pour obtenir le "compte" d'un tableau ou utiliser ce "compte/longueur" à des fins de filtrage.

La meilleure construction ici est d'utiliser les opérations "Bulk" comme le test des éléments de tableau présents ne se mélange pas bien avec le concept de "upserts", donc là où vous voulez une fonctionnalité upsert, un test de tableau est préférable en deux opérations. Mais comme les opérations "en masse" peuvent être envoyées au serveur avec "une requête" et que vous obtenez également "une réponse", cela atténue toute surcharge réelle en le faisant.

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

La façon dont cela fonctionne est que la première tentative essaie de trouver un document où l'élément de tableau à ajouter n'est pas déjà présent dans le tableau. Aucune tentative de "upsert" n'est faite ici car vous ne voulez pas créer un nouveau document si la seule raison pour laquelle il ne correspond pas à un document est que l'élément de tableau n'est pas présent. Mais en cas de correspondance, le nouveau membre est ajouté au tableau et le "compte" actuel est "incrémenté" de 1 via $inc , qui conserve le nombre total ou la longueur.

La deuxième déclaration va donc correspondre au document uniquement, et utilise donc un "upsert" puisque si le document n'est pas trouvé pour le champ clé, il sera créé. Comme toutes les opérations sont à l'intérieur de $setOnInsert alors aucune opération ne sera effectuée si le document existe déjà.

Il s'agit en fait d'une seule demande et réponse du serveur, il n'y a donc pas de "va-et-vient" pour l'inclusion de deux opérations de mise à jour, ce qui rend cela efficace.

La suppression d'une entrée de tableau est fondamentalement l'inverse, sauf que cette fois, il n'est pas nécessaire de "créer" un nouveau document s'il n'a pas été trouvé :

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

Alors maintenant, il vous suffit de tester où l'élément de tableau est présent et où il se trouve alors $pull l'élément correspondant du contenu du tableau, tout en "décrémentant" le "compte" de 1 pour refléter la suppression.

Maintenant, vous "pourriez" utiliser $addToSet à la place ici, car il regardera simplement le contenu du tableau et si le membre n'est pas trouvé, il sera ajouté, et pour les mêmes raisons, il n'est pas nécessaire de tester l'élément de tableau existant lors de l'utilisation de $pull car il ne fera rien si l'élément n'est pas là. De plus $addToSet dans ce contexte peut être utilisé directement dans un "upsert", tant que vous ne "croisez pas" car il n'est pas autorisé d'essayer d'utiliser plusieurs opérateurs de mise à jour sur le même chemin avec MongoDB :

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

Mais ce serait "faux":

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

Cependant, en faisant cela, vous perdez la fonctionnalité "compter" puisque de telles opérations se terminent simplement sans tenir compte de ce qui est réellement là ou si quelque chose a été "ajouté" ou "supprimé".

Garder des "compteurs" est une très bonne chose, et même si vous n'en avez pas une utilisation immédiate pour le moment, alors à un certain stade du cycle de vie de votre application, vous en voudrez probablement. Il est donc très logique de comprendre la logique impliquée et de les mettre en œuvre maintenant. Petit prix à payer maintenant pour beaucoup d'avantages plus tard.

Sidenote rapide ici car je recommande généralement les opérations "en masse" lorsque cela est possible. Lors de l'utilisation via le .collection accessor dans mongoose, vous devez être conscient qu'il s'agit de méthodes de pilote natives et qu'elles se comportent donc différemment des méthodes "mongoose".

Notamment, toutes les méthodes "mongoose" ont une "vérification" intégrée pour voir que la connexion à la base de données est actuellement active. Si ce n'est pas le cas, l'opération est effectivement "mise en file d'attente" jusqu'à ce que la connexion soit établie. En utilisant les méthodes natives, cette "vérification" n'est plus présente. Par conséquent, vous devez soit vous assurer qu'une connexion est déjà présente à partir d'une méthode "mongoose" ayant été exécutée "en premier", soit envelopper alternativement toute la logique de votre application dans une construction qui "attend" que la connexion soit établie :

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

De cette façon, vous êtes sûr qu'une connexion est présente et que les objets corrects peuvent être renvoyés et utilisés par les méthodes. Mais pas de connexion, et les opérations "Bulk" échoueront.