Baserat på dina krav kan ett av tillvägagångssätten vara att utforma ditt schema på ett sådant sätt att varje dokument har kapaciteten att hålla mer än ett dokument och i sig själv fungera som en försedd behållare .
{
"_id":Number,
"doc":Array
}
Varje dokument i samlingen fungerar som en försedd behållare , och dokumenten kommer att lagras som array i doc
fält. doc
fältet är en array, kommer att bibehålla insättningsordningen. Du kan begränsa antalet dokument till n
. Så _id
fältet för varje containerdokument kommer att vara inkrementellt med n
, som anger antalet dokument som ett containerdokument kan innehålla.
Genom att göra dessa undviker lägga till extra fields
till dokumentet, extra indices
, unnecessary sorts
.
Infoga den allra första posten
dvs när samlingen är tom.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Infoga efterföljande poster
- Identifiera det sista behållardokumentets
_id
ochnumber
av dokument som den innehåller. - Om antalet dokument som den innehåller är mindre än
n
, sedan uppdatera behållardokumentet med det nya dokumentet, annars skapa ett nytt containerdokument.
Säg att varje container document
kan hålla 5
dokument som mest, och vi vill infoga ett nytt 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 ]
});
}
})
Observera att aggregation
, körs på serversidan och är mycket effektiv, notera också att aggregation
skulle returnera dig ett dokument snarare än en markör i versioner previous to 2.6
. Så du skulle behöva modifiera ovanstående kod för att bara välja från ett enda dokument istället för att iterera en markör.
Infoga ett nytt dokument mellan dokument
Om du nu vill infoga ett nytt dokument mellan dokument 1
och 2
, vi vet att dokumentet ska hamna i behållaren med _id=0
och bör placeras i second
position i doc
array av den behållaren.
så vi använder oss av $each
och $position
operatörer för att infoga i specifika positioner.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Hantera överflöde
Nu måste vi ta hand om dokument som overflowing
i varje container
, säg att vi infogar ett nytt dokument emellan, i behållare med _id=0
. Om behållaren redan har 5
dokument måste vi move the last document to the next container
och gör så tills alla containrar innehåller dokument inom sin kapacitet, om så krävs måste vi äntligen skapa en container för att hålla de överfulla dokumenten.
Denna komplexa operation bör göras på serversidan . För att hantera detta kan vi skapa ett skript som det nedan och register
det med 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);
}
}
Så att after every insertion in between
, kan vi anropa denna function
genom att skicka container-id, handleOverFlow(containerId)
.
Hämtar alla poster i ordning
Använd bara $unwind
operatör i aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Beställa om dokument
Du kan lagra varje dokument i en kapslad behållare med ett "_id"-fält:
.."doc":[{"_id":0,","name":"xyz",...}..]..
Få tag i "doc"-arrayen i den förseglade behållaren som du vill beställa om artiklar.
var docArray = db.col.find({"_id":0})[0];
Uppdatera deras ID så att ordningen på objektet ändras efter sortering.
Sortera arrayen baserat på deras _id.
docArray.sort( function(a, b) {
return a._id - b._id;
});
uppdatera tillbaka den täckta behållaren med den nya dokumentmatrisen.
Men återigen, allt handlar om vilket tillvägagångssätt som är genomförbart och som passar dina krav bäst.
Kommer till dina frågor:
Dokument som matriser.
använd $each
och $position
operatorer i db.collection.update()
fungerar som avbildat i mitt svar.
Ja. Det skulle påverka prestandan, om inte samlingen har mycket mindre data.
Ja. Med Capped Collections kan du förlora data.