Na základě vašeho požadavku by jedním z přístupů mohlo být navrhnout schéma tak, aby každý dokument měl možnosti pojme více než jeden dokument a sám o sobě funguje jako uzavřený kontejner .
{
"_id":Number,
"doc":Array
}
Každý dokument v kolekci bude fungovat jako uzavřený kontejner a dokumenty budou uloženy jako pole v doc pole. doc pole je pole, zachová pořadí vkládání. Počet dokumentů můžete omezit na n . Tedy _id pole každého kontejnerového dokumentu bude přírůstkové o n , uvádějící počet dokumentů, které kontejnerový dokument pojme.
Tím se vyhnete přidání extra fields do dokumentu extra indices , unnecessary sorts .
Vložení úplně prvního záznamu
tj. když je kolekce prázdná.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Vkládání následných záznamů
- Určete
_idposledního kontejnerového dokumentu anumberdokumentů, které má. - Pokud je počet dokumentů, které obsahuje, menší než
na poté aktualizovat kontejnerový dokument s novým dokumentem, jinak vytvořte nový kontejnerový dokument.
Řekněme, že každý container document může obsahovat 5 dokumentů a my chceme vložit nový dokument.
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 ]
});
}
})
Všimněte si, že aggregation , běží na straně serveru a je velmi efektivní, všimněte si také aggregation vám vrátí dokument spíše než kurzor ve verzích previous to 2.6 . Takže byste museli upravit výše uvedený kód tak, abyste vybírali pouze z jednoho dokumentu a ne iterovali kurzor.
Vložení nového dokumentu mezi dokumenty
Nyní, pokud chcete vložit nový dokument mezi dokumenty 1 a 2 , víme, že dokument by měl spadat do kontejneru s _id=0 a měl by být umístěn v second pozici v doc pole tohoto kontejneru.
takže používáme $each a $position operátory pro vkládání do konkrétních pozic.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Manipulace s přetečením
Nyní se musíme postarat o to, aby dokumenty overflowing v každém container , řekněme, že mezi to vložíme nový dokument do kontejneru s _id=0 . Pokud kontejner již obsahuje 5 dokumenty, musíme move the last document to the next container a udělejte tak, dokud všechny kontejnery neobsahují dokumenty v rámci jejich kapacity, pokud to bude potřeba, musíme konečně vytvořit kontejner, do kterého se vejdou přetékající dokumenty.
Tato složitá operace by měla provést na straně serveru . Abychom to zvládli, můžeme vytvořit skript, jako je ten níže, a register to s 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);
}
}
Takže after every insertion in between , můžeme vyvolat tuto function předáním ID kontejneru, handleOverFlow(containerId) .
Načítání všech záznamů v pořadí
Stačí použít $unwind operátor v aggregate pipeline .
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Znovu objednání dokumentů
Každý dokument můžete uložit do uzavřeného kontejneru s polem "_id":
.."doc":[{"_id":0,","name":"xyz",...}..]..
Získejte pole „doc“ uzavřeného kontejneru, v němž chcete změnit pořadí položek.
var docArray = db.col.find({"_id":0})[0];
Aktualizujte jejich ID, aby se po seřazení změnilo pořadí položky.
Seřaďte pole podle jejich _id.
docArray.sort( function(a, b) {
return a._id - b._id;
});
aktualizujte uzavřený kontejner zpět pomocí nového pole dokumentů.
Ale zase se vše scvrkává na to, který přístup je proveditelný a nejlépe vyhovuje vašim požadavkům.
K vašim otázkám:
Dokumenty jako pole.
použijte $each a $position operátory v db.collection.update() funkce, jak je znázorněno v mé odpovědi.
Ano. Ovlivnilo by to výkon, ledaže by kolekce obsahovala méně dat.
Ano. S omezenými kolekcemi můžete přijít o data.