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 dienumber
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.