MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Como armazenar um conjunto ordenado de documentos no MongoDB sem usar uma coleção limitada


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