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

Comment stocker un ensemble ordonné de documents dans MongoDB sans utiliser de collection limitée

En fonction de vos besoins, l'une des approches pourrait consister à concevoir votre schéma, de manière à ce que chaque document ait la capacité pour contenir plus d'un document et agir en soi comme un contenant plafonné .

{
  "_id":Number,
  "doc":Array
}

Chaque document de la collection agira comme un conteneur plafonné , et les documents seront stockés sous forme de tableau dans le doc champ. Le doc le champ étant un tableau, maintiendra l'ordre d'insertion.Vous pouvez limiter le nombre de documents à n . Donc le _id le champ de chaque document conteneur sera incrémenté de n , indiquant le nombre de documents qu'un document conteneur peut contenir.

En faisant cela, vous évitez ajouter des extra fields au document, extra indices , unnecessary sorts .

Insérer le tout premier enregistrement

c'est-à-dire lorsque la collection est vide.

var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});

Insérer les enregistrements suivants

  • Identifier le _id du dernier document conteneur , et le number des documents qu'il détient.
  • Si le nombre de documents qu'il contient est inférieur à n , puis mettre à jour le document conteneur avec le nouveau document, sinon créer un nouveau document conteneur.

Supposons que chaque container document peut contenir 5 documents au maximum, et nous voulons insérer un nouveau document.

var record = {"name" : "newlyAdded"};

// using aggregation, get the _id of the last inserted container, and the 
// number of record it currently holds.
db.col.aggregate( [ {
    $group : {
        "_id" : null,
        "max" : {
            $max : "$_id"
        },
        "lastDocSize" : {
            $last : "$doc"
        }
    }
}, {
    $project : {
        "currentMaxId" : "$max",
        "capSize" : {
            $size : "$lastDocSize"
        },
        "_id" : 0
    }
// once obtained, check if you need to update the last container or 
// create a new container and insert the document in it.
} ]).forEach( function(check) {
    if (check.capSize < 5) {
        print("updating");
        // UPDATE
        db.col.update( {
            "_id" : check.currentMaxId
        }, {
            $push : {
                "doc" : record
            }
        });
    } else {
        print("inserting");
        //insert
        db.col.insert( {
            "_id" : check.currentMaxId + 5,
            "doc" : [ record ]
        });
    }
})

Notez que l'aggregation , s'exécute côté serveur et est très efficace, notez également que l'aggregation vous renverrait un document plutôt qu'un curseur dans les versions previous to 2.6 . Vous devrez donc modifier le code ci-dessus pour sélectionner un seul document plutôt que d'itérer un curseur.

Insérer un nouveau document entre des documents

Maintenant, si vous souhaitez insérer un nouveau document entre les documents 1 et 2 , nous savons que le document doit tomber à l'intérieur du conteneur avec _id=0 et doit être placé dans le second position dans le doc tableau de ce conteneur.

donc, nous utilisons le $each et $position opérateurs d'insertion dans des positions spécifiques.

var record = {"name" : "insertInMiddle"};

db.col.update(
{
    "_id" : 0
}, {
    $push : {
        "doc" : {
            $each : [record],
            $position : 1
        }
    }
}
);

Gestion du flux

Maintenant, nous devons nous occuper des documents overflowing dans chaque container , disons que nous insérons un nouveau document entre les deux, dans le conteneur avec _id=0 . Si le conteneur a déjà 5 documents, nous devons move the last document to the next container et faites-le jusqu'à ce que tous les conteneurs contiennent des documents dans leur capacité, si nécessaire, nous devons enfin créer un conteneur pour contenir les documents qui débordent.

Cette opération complexe devrait être fait du côté du serveur . Pour gérer cela, nous pouvons créer un script tel que celui ci-dessous et register avec mongodb.

db.system.js.save( {
    "_id" : "handleOverFlow",
    "value" : function handleOverFlow(id) {
        var currDocArr = db.col.find( {
            "_id" : id
        })[0].doc;
        print(currDocArr);
        var count = currDocArr.length;
        var nextColId = id + 5;
        // check if the collection size has exceeded
    if (count <= 5)
        return;
    else {
        // need to take the last doc and push it to the next capped 
    // container's array
    print("updating collection: " + id);
    var record = currDocArr.splice(currDocArr.length - 1, 1);
    // update the next collection
    db.col.update( {
        "_id" : nextColId
    }, {
        $push : {
            "doc" : {
                $each : record,
                $position : 0
            }
        }
    });
    // remove from original collection
    db.col.update( {
        "_id" : id
    }, {
        "doc" : currDocArr
    });
    // check overflow for the subsequent containers, recursively.
    handleOverFlow(nextColId);
}
}

Ainsi, after every insertion in between , nous pouvons invoquer cette function en passant l'identifiant du conteneur, handleOverFlow(containerId) .

Récupérer tous les enregistrements dans l'ordre

Utilisez simplement le $unwind opérateur dans le aggregate pipeline .

db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);

Réorganiser les documents

Vous pouvez stocker chaque document dans un conteneur plafonné avec un champ "_id":

.."doc":[{"_id":0,","name":"xyz",...}..]..

Récupérez le tableau "doc" du conteneur cappé dont vous souhaitez réorganiser les articles.

var docArray = db.col.find({"_id":0})[0];

Mettez à jour leurs identifiants afin qu'après le tri, l'ordre de l'article change.

Triez le tableau en fonction de leurs _ids.

docArray.sort( function(a, b) {
    return a._id - b._id;
});

mettre à jour le conteneur plafonné, avec le nouveau tableau doc.

Mais là encore, tout se résume à l'approche qui est faisable et qui répond le mieux à vos besoins.

Pour en venir à vos questions :

Documents sous forme de tableaux.

utilisez le $each et $position opérateurs dans db.collection.update() fonctionnent comme décrit dans ma réponse.

Oui. Cela aurait un impact sur les performances, à moins que la collection ne contienne très peu de données.

Oui. Avec les collections plafonnées, vous risquez de perdre des données.