MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Come archiviare un set ordinato di documenti in MongoDB senza utilizzare una raccolta limitata

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