Según sus requisitos, uno de los enfoques podría ser diseñar su esquema, de tal manera que cada documento tenga la capacidad para contener más de un documento y en sí mismo actuar como un contenedor tapado .
{
"_id":Number,
"doc":Array
}
Cada documento de la colección actuará como un contenedor tapado , y los documentos se almacenarán como una matriz en el doc
campo. El doc
siendo un campo una matriz, mantendrá el orden de inserción. Puede limitar el número de documentos a n
. Entonces el _id
el campo de cada documento contenedor será incremental por n
, que indica el número de documentos que puede contener un documento contenedor.
Al hacer esto, evita agregando extra fields
al documento, extra indices
, unnecessary sorts
.
Insertar el primer registro
es decir, cuando la colección está vacía.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Insertar registros posteriores
- Identifique el
_id
del último documento contenedor y elnumber
de documentos que contiene. - Si el número de documentos que contiene es inferior a
n
, luego actualizar el documento contenedor con el nuevo documento, de lo contrario crear un nuevo documento contenedor.
Digamos que cada container document
puede contener 5
documentos como máximo, y queremos insertar un nuevo 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 ]
});
}
})
Tenga en cuenta que la aggregation
, se ejecuta en el lado del servidor y es muy eficiente, también tenga en cuenta que la aggregation
le devolvería un documento en lugar de un cursor en versiones previous to 2.6
. Por lo tanto, deberá modificar el código anterior para seleccionar de un solo documento en lugar de iterar un cursor.
Insertar un nuevo documento entre documentos
Ahora, si desea insertar un nuevo documento entre los documentos 1
y 2
, sabemos que el documento debe caer dentro del contenedor con _id=0
y debe colocarse en el second
posición en el doc
matriz de ese contenedor.
entonces, hacemos uso de $each
y $position
operadores para insertar en posiciones específicas.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Manejo de desbordamiento
Ahora, debemos ocuparnos de los documentos overflowing
en cada container
, digamos que insertamos un nuevo documento en el medio, en un contenedor con _id=0
. Si el contenedor ya tiene 5
documentos, necesitamos move the last document to the next container
y hágalo hasta que todos los contenedores contengan documentos dentro de su capacidad, si es necesario, al final necesitamos crear un contenedor para contener los documentos desbordados.
Esta compleja operación debería hacerse en el lado del servidor . Para manejar esto, podemos crear un script como el de abajo y 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);
}
}
De modo que after every insertion in between
, podemos invocar esta function
pasando la identificación del contenedor, handleOverFlow(containerId)
.
Obteniendo todos los registros en orden
Solo usa el $unwind
operador en la aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Reordenación de documentos
Puede almacenar cada documento en un contenedor tapado con un campo "_id":
.."doc":[{"_id":0,","name":"xyz",...}..]..
Obtenga la matriz "doc" del contenedor tapado del que desea reordenar elementos.
var docArray = db.col.find({"_id":0})[0];
Actualice sus ID para que, después de ordenar, cambie el orden del artículo.
Ordene la matriz según sus _ids.
docArray.sort( function(a, b) {
return a._id - b._id;
});
actualice el contenedor tapado nuevamente, con la nueva matriz de documentos.
Pero, de nuevo, todo se reduce a qué enfoque es factible y se adapta mejor a sus necesidades.
En cuanto a sus preguntas:
Documentos como matrices.
usa el $each
y $position
operadores en db.collection.update()
función como se muestra en mi respuesta.
Sí. Afectaría el rendimiento, a menos que la colección tenga muy pocos datos.
Sí. Con Colecciones limitadas, puede perder datos.