MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

So speichern Sie einen geordneten Satz von Dokumenten in MongoDB, ohne eine begrenzte Sammlung zu verwenden

Basierend auf Ihren Anforderungen könnte einer der Ansätze darin bestehen, Ihr Schema so zu entwerfen, dass jedes Dokument die Fähigkeit besitzt um mehr als ein Dokument zu speichern und selbst als gedeckelter Container zu fungieren .

{
  "_id":Number,
  "doc":Array
}

Jedes Dokument in der Sammlung fungiert als gedeckelter Container , und die Dokumente werden als Array im doc gespeichert aufstellen. Das doc Da es sich bei dem Feld um ein Array handelt, wird die Reihenfolge des Einfügens beibehalten. Sie können die Anzahl der Dokumente auf n begrenzen . Also die _id Feld jedes Containerdokuments wird um n inkrementiert , die die Anzahl der Dokumente angibt, die ein Containerdokument enthalten kann.

Dadurch vermeiden Sie Hinzufügen von extra fields zum Dokument, extra indices , unnecessary sorts .

Einfügen des allerersten Datensatzes

d.h. wenn die Sammlung leer ist.

var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});

Einfügen nachfolgender Datensätze

  • Identifizieren Sie die _id des letzten Containerdokuments , und die number von Dokumenten, die es besitzt.
  • Wenn die Anzahl der enthaltenen Dokumente kleiner als n ist , dann aktualisieren Containerdokument mit dem neuen Dokument, sonst erstellen ein neues Containerdokument.

Sprich, dass jedes container document kann 5 enthalten höchstens Dokumente, und wir möchten ein neues Dokument einfügen.

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 ]
        });
    }
})

Beachten Sie, dass die aggregation , läuft serverseitig und ist sehr effizient, beachten Sie auch, dass die aggregation würde Ihnen ein Dokument zurücksenden statt eines Cursors in Versionen previous to 2.6 . Sie müssten also den obigen Code ändern, um nur aus einem einzelnen Dokument auszuwählen, anstatt einen Cursor zu iterieren.

Ein neues Dokument zwischen Dokumenten einfügen

Wenn Sie nun ein neues Dokument zwischen den Dokumenten 1 einfügen möchten und 2 , wissen wir, dass das Dokument in den Container mit _id=0 fallen sollte und sollte in der second platziert werden Position im doc Array dieses Containers.

also verwenden wir den $each und $position Operatoren zum Einfügen an bestimmten Positionen.

var record = {"name" : "insertInMiddle"};

db.col.update(
{
    "_id" : 0
}, {
    $push : {
        "doc" : {
            $each : [record],
            $position : 1
        }
    }
}
);

Umgang mit Flow

Jetzt müssen wir uns um overflowing Dokumente kümmern in jedem container , sagen wir, wir fügen ein neues Dokument dazwischen ein, im Container mit _id=0 . Wenn der Container bereits 5 hat Dokumente, müssen wir move the last document to the next container und tun Sie dies, bis alle Container Dokumente innerhalb ihrer Kapazität enthalten. Wenn erforderlich, müssen wir zuletzt einen Container erstellen, um die überfließenden Dokumente aufzunehmen.

Diese komplexe Operation sollte auf der Serverseite erfolgen . Um dies zu handhaben, können wir ein Skript wie das folgende erstellen und register es mit 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);
}
}

Damit after every insertion in between , können wir diese function aufrufen indem Sie die Container-ID handleOverFlow(containerId) übergeben .

Alle Datensätze der Reihe nach abrufen

Verwenden Sie einfach den $unwind -Operator in der aggregate pipeline .

db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);

Dokumente nachbestellen

Sie können jedes Dokument in einem mit einer Kappe versehenen Container mit einem "_id"-Feld speichern:

.."doc":[{"_id":0,","name":"xyz",...}..]..

Holen Sie sich das "doc"-Array des verschlossenen Containers, dessen Artikel Sie neu anordnen möchten.

var docArray = db.col.find({"_id":0})[0];

Aktualisieren Sie ihre IDs, damit sich nach dem Sortieren die Reihenfolge der Elemente ändert.

Sortieren Sie das Array basierend auf ihren _ids.

docArray.sort( function(a, b) {
    return a._id - b._id;
});

aktualisiere den Container mit der Kappe zurück, mit dem neuen Doc-Array.

Aber andererseits läuft alles darauf hinaus, welcher Ansatz machbar ist und am besten zu Ihren Anforderungen passt.

Kommen wir zu Ihren Fragen:

Dokumente als Arrays.

Verwenden Sie den $each und $position Operatoren in db.collection.update() Funktion wie in meiner Antwort dargestellt.

Ja. Dies würde die Leistung beeinträchtigen, es sei denn, die Sammlung enthält sehr wenige Daten.

Ja. Bei begrenzten Sammlungen können Sie Daten verlieren.