In base alle tue esigenze, uno degli approcci potrebbe essere quello di progettare il tuo schema, in modo tale che ogni documento abbia la capacità per contenere più di un documento e fungere di per sé da contenitore chiuso .
{
"_id":Number,
"doc":Array
}
Ogni documento nella raccolta fungerà da contenitore con limite e i documenti verranno archiviati come array nel doc
campo. Il doc
essendo un array, manterrà l'ordine di inserimento. Puoi limitare il numero di documenti a n
. Quindi il _id
il campo di ogni documento contenitore sarà incrementale di n
, che indica il numero di documenti che può contenere un documento contenitore.
In questo modo eviti aggiungendo extra fields
al documento, extra indices
, unnecessary sorts
.
Inserimento del primo record
cioè quando la raccolta è vuota.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Inserimento di record successivi
- Identifica il
_id
dell'ultimo documento contenitore e ilnumber
dei documenti che detiene. - Se il numero di documenti che contiene è inferiore a
n
, quindi aggiorna thecontainer document con il nuovo documento, altrimenti crea un nuovo documento contenitore.
Supponiamo che ogni container document
può contenere 5
documenti al massimo, e vogliamo inserire un nuovo documento.
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 ]
});
}
})
Nota che l'aggregation
, funziona lato server ed è molto efficiente, si noti inoltre che l'aggregation
ti restituirebbe un documento anziché un cursore nelle versioni previous to 2.6
. Quindi dovresti modificare il codice sopra per selezionare semplicemente da un singolo documento anziché scorrere un cursore.
Inserimento di un nuovo documento tra i documenti
Ora, se desideri inserire un nuovo documento tra i documenti 1
e 2
, sappiamo che il documento dovrebbe rientrare nel contenitore con _id=0
e dovrebbe essere inserito nel second
posizione nel doc
array di quel contenitore.
quindi, utilizziamo il $each
e $position
operatori per l'inserimento in posizioni specifiche.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Gestione del flusso eccessivo
Ora dobbiamo occuparci dei documenti overflowing
in ogni container
, supponiamo di inserire un nuovo documento nel mezzo, nel contenitore con _id=0
. Se il contenitore ha già 5
documenti, dobbiamo move the last document to the next container
e fallo fino a quando tutti i contenitori conterranno i documenti nella loro capacità, se necessario, infine, dobbiamo creare un contenitore per contenere i documenti traboccanti.
Questa complessa operazione dovrebbe essere fatto sul lato server . Per gestirlo, possiamo creare uno script come quello qui sotto e register
con 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);
}
}
In modo che after every insertion in between
, possiamo invocare questa function
passando l'id del contenitore, handleOverFlow(containerId)
.
Recupero di tutti i record in ordine
Usa semplicemente il $unwind
operatore nella aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Riordino dei documenti
Puoi archiviare ogni documento in un contenitore chiuso con un campo "_id":
.."doc":[{"_id":0,","name":"xyz",...}..]..
Procurati l'array "doc" del contenitore con tappo di cui desideri riordinare gli articoli.
var docArray = db.col.find({"_id":0})[0];
Aggiorna i loro ID in modo che dopo l'ordinamento l'ordine dell'articolo cambi.
Ordina l'array in base ai loro _id.
docArray.sort( function(a, b) {
return a._id - b._id;
});
aggiorna nuovamente il contenitore con tappo, con il nuovo array di documenti.
Ma poi di nuovo, tutto si riduce a quale approccio è fattibile e si adatta meglio alle tue esigenze.
Venendo alle tue domande:
Documenti come array.
usa il $each
e $position
operatori nel db.collection.update()
funzione come illustrato nella mia risposta.
Sì. Avrebbe un impatto sulle prestazioni, a meno che la raccolta non contenga molto meno dati.
Sì. Con le raccolte limitate, potresti perdere dati.