Com base em seus requisitos, uma das abordagens pode ser projetar seu esquema, de forma que cada documento possa ter a capacidade para conter mais de um documento e por si só atuar como um contêiner tampado .
{
"_id":Number,
"doc":Array
}
Cada documento na coleção atuará como um contêiner tampado , e os documentos serão armazenados como array no
doc
campo. O doc
campo sendo uma matriz, manterá a ordem de inserção. Você pode limitar o número de documentos a n
. Portanto, o _id
campo de cada documento de contêiner será incremental por n
, indicando o número de documentos que um documento de contêiner pode conter. Ao fazer isso, você evita adicionando
extra fields
ao documento, extra indices
, unnecessary sorts
. Inserindo o primeiro registro
ou seja, quando a coleção está vazia.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Inserindo registros subsequentes
- Identifique o
_id
do último documento de contêiner , e onumber
de documentos que possui. - Se o número de documentos que ele contém for menor que
n
, em seguida, atualize thecontainer document com o novo documento, senão criar um novo documento de contêiner.
Digamos que cada
container document
pode conter 5
documentos no máximo, e queremos inserir um novo 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 ]
});
}
})
Observe que a
aggregation
, é executado no lado do servidor e é muito eficiente, observe também que a aggregation
retornaria um documento em vez de um cursor nas versões previous to 2.6
. Portanto, você precisaria modificar o código acima para selecionar apenas de um único documento, em vez de iterar um cursor. Inserindo um novo documento entre documentos
Agora, se você quiser inserir um novo documento entre documentos
1
e 2
, sabemos que o documento deve estar dentro do contêiner com _id=0
e deve ser colocado no second
posição no doc
matriz desse recipiente. então, fazemos uso do
$each
e $position
operadores para inserção em posições específicas. var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Como lidar com o fluxo
Agora, precisamos cuidar dos documentos
overflowing
em cada container
, digamos que inserimos um novo documento no meio, no contêiner com _id=0
. Se o contêiner já tiver 5
documentos, precisamos move the last document to the next container
e fazê-lo até que todos os contêineres contenham documentos dentro de sua capacidade, se necessário, finalmente, precisamos criar um contêiner para armazenar os documentos transbordantes. Essa operação complexa deve ser feito no lado do servidor . Para lidar com isso, podemos criar um script como o abaixo e
register
com 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);
}
}
Para que
after every insertion in between
, podemos invocar esta function
passando o ID do contêiner, handleOverFlow(containerId)
. Buscando todos os registros em ordem
Basta usar o
$unwind
operador no aggregate pipeline
. db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Reordenar documentos
Você pode armazenar cada documento em um contêiner tampado com um campo "_id":
.."doc":[{"_id":0,","name":"xyz",...}..]..
Obtenha a matriz "doc" do contêiner tampado do qual você deseja reordenar os itens.
var docArray = db.col.find({"_id":0})[0];
Atualize seus ids para que, após a classificação, a ordem do item seja alterada.
Classifique a matriz com base em seus _ids.
docArray.sort( function(a, b) {
return a._id - b._id;
});
atualize o contêiner tampado de volta, com a nova matriz doc.
Mas, novamente, tudo se resume a qual abordagem é viável e se adapta melhor às suas necessidades.
Vamos às suas perguntas:
Documentos como Arrays.
use o
$each
e $position
operadores no db.collection.update()
função conforme descrito na minha resposta. Sim. Isso afetaria o desempenho, a menos que a coleção tenha muito menos dados.
Sim. Com Coleções Limitadas, você pode perder dados.