Op basis van uw vereisten zou een van de benaderingen kunnen zijn om uw schema zo te ontwerpen dat elk document de mogelijkheid heeft om meer dan één document te bevatten en op zichzelf te fungeren als een afgedekte container .
{
"_id":Number,
"doc":Array
}
Elk document in de verzameling fungeert als een afgedekte container , en de documenten worden opgeslagen als een array in de doc
veld. De doc
veld een array is, behoudt de volgorde van invoeging. U kunt het aantal documenten beperken tot n
. Dus de _id
veld van elk containerdocument zal toenemen met n
, waarmee wordt aangegeven hoeveel documenten een containerdocument kan bevatten.
Door dit te doen vermijdt u toevoegen van extra fields
naar het document, extra indices
, unnecessary sorts
.
Het allereerste record invoegen
d.w.z. wanneer de verzameling leeg is.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Volgende records invoegen
- Identificeer de
_id
van het laatste containerdocument , en hetnumber
documenten die het bevat. - Als het aantal documenten dat het bevat kleiner is dan
n
, dan bijwerken thecontainer document met het nieuwe document, anders maak een nieuw containerdocument.
Stel dat elk container document
kan 5
bevatten hoogstens documenten en we willen een nieuw document invoegen.
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 ]
});
}
})
Merk op dat de aggregation
, draait op de server en is zeer efficiënt, merk ook op dat de aggregation
zou u een document terugsturen in plaats van een cursor in versies previous to 2.6
. U zou dus de bovenstaande code moeten wijzigen om slechts uit een enkel document te selecteren in plaats van een cursor te herhalen.
Een nieuw document tussen documenten invoegen
Als u nu een nieuw document tussen documenten wilt invoegen 1
en 2
, weten we dat het document in de container met _id=0
. moet vallen en moet in de second
. worden geplaatst positie in het doc
array van die container.
dus maken we gebruik van de $each
en $position
operators voor het invoegen in specifieke posities.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Overstroming afhandelen
Nu moeten we zorgen voor documenten die overflowing
in elke container
, stel dat we er een nieuw document tussen invoegen, in container met _id=0
. Als de container al 5
. heeft documenten, moeten we move the last document to the next container
en doe dit totdat alle containers de documenten binnen hun capaciteit bevatten, indien nodig moeten we eindelijk een container maken om de overvolle documenten te bewaren.
Deze complexe operatie moet worden gedaan aan de serverkant . Om dit aan te pakken, kunnen we een script maken zoals hieronder en register
het met 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);
}
}
Zodat after every insertion in between
, kunnen we deze function
aanroepen door de container-id door te geven, handleOverFlow(containerId)
.
Alle records in volgorde ophalen
Gebruik gewoon de $unwind
operator in de aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Documenten opnieuw bestellen
U kunt elk document opslaan in een afgesloten container met een "_id"-veld:
.."doc":[{"_id":0,","name":"xyz",...}..]..
Pak de "doc"-array van de afgedekte container waarvan u items opnieuw wilt ordenen.
var docArray = db.col.find({"_id":0})[0];
Werk hun ID bij zodat na het sorteren de volgorde van het item zal veranderen.
Sorteer de array op basis van hun _id's.
docArray.sort( function(a, b) {
return a._id - b._id;
});
update de afgedekte container terug, met de nieuwe doc-array.
Maar nogmaals, alles komt neer op welke aanpak haalbaar is en het beste aansluit bij uw behoefte.
Komend op uw vragen:
Documenten als arrays.
gebruik de $each
en $position
operators in de db.collection.update()
functioneren zoals afgebeeld in mijn antwoord.
Ja. Het zou de prestaties beïnvloeden, tenzij de verzameling zeer weinig gegevens bevat.
Ja. Met Capped Collections kunt u gegevens verliezen.