sql >> Base de Datos >  >> NoSQL >> MongoDB

Cómo almacenar un conjunto ordenado de documentos en MongoDB sin usar una colección limitada

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 el number 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.