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
_id
posledního kontejnerového dokumentu anumber
dokumentů, které má. - Pokud je počet dokumentů, které obsahuje, menší než
n
a 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.