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 lenumber
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.