Въз основа на вашето изискване един от подходите може да бъде да проектирате вашата схема по такъв начин, че всеки документ да има възможността да съдържа повече от един документ и сам по себе си да действа като затворен контейнер .
{
"_id":Number,
"doc":Array
}
Всеки документ в колекцията ще действа като затворен контейнер и документите ще бъдат съхранени като масив в doc
поле. doc
полето, което е масив, ще поддържа реда на вмъкване. Можете да ограничите броя на документите до n
. Така че _id
полето на всеки контейнерен документ ще бъде инкрементално с n
, указващ броя на документите, които контейнерният документ може да побере.
Като правите това, вие избягвате добавяне на extra fields
към документа, extra indices
, unnecessary sorts
.
Вмъкване на първия запис
когато колекцията е празна.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Вмъкване на последващи записи
- Идентифицирайте
_id
на последния контейнерен документ иnumber
на документите, които притежава. - Ако броят документи, които съдържа, е по-малък от
n
, след което актуализирайте документът на контейнера с новия документ, в противен случай създайте нов контейнерен документ.
Кажете, че всеки container document
може да побере 5
най-много документи и искаме да вмъкнем нов документ.
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 ]
});
}
})
Имайте предвид, че aggregation
, работи от страна на сървъра и е много ефективен, имайте предвид също, че aggregation
ще ви върне документ вместо курсор във версии previous to 2.6
. Така че ще трябва да промените горния код, за да избирате само от един документ, вместо да повтаряте курсора.
Вмъкване на нов документ между документите
Сега, ако искате да вмъкнете нов документ между документите 1
и 2
, знаем, че документът трябва да попадне в контейнера с _id=0
и трябва да се постави във second
позиция в doc
масив от този контейнер.
така че ние използваме $each
и $position
оператори за вмъкване в определени позиции.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Обработка на поток
Сега трябва да се погрижим за overflowing
на документи във всеки container
, да речем, че вмъкваме нов документ между тях, в контейнер с _id=0
. Ако контейнерът вече има 5
документи, трябва да move the last document to the next container
и правете това, докато всички контейнери поберат документи в рамките на техния капацитет, ако е необходимо, най-накрая трябва да създадем контейнер, който да побере препълващите документи.
Тази сложна оперия да се извърши от сървърната страна . За да се справим с това, можем да създадем скрипт като този по-долу и register
то с 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);
}
}
Така че after every insertion in between
, можем да извикаме тази function
чрез предаване на идентификатора на контейнера, handleOverFlow(containerId)
.
Извличане на всички записи по ред
Просто използвайте $unwind
оператор в aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Пренареждане на документи
Можете да съхранявате всеки документ в затворен контейнер с поле „_id“:
.."doc":[{"_id":0,","name":"xyz",...}..]..
Вземете масива „doc“ на затворения контейнер, в който искате да пренаредите елементи.
var docArray = db.col.find({"_id":0})[0];
Актуализирайте техните идентификатори, така че след сортиране редът на елемента да се промени.
Сортирайте масива въз основа на техните _ids.
docArray.sort( function(a, b) {
return a._id - b._id;
});
актуализирайте затворения контейнер обратно, с новия масив на документи.
Но отново всичко се свежда до това кой подход е осъществим и отговаря най-добре на вашите изисквания.
Стигаме до вашите въпроси:
Документи като масиви.
използвайте $each
и $position
оператори в db.collection.update()
функция, както е описано в моя отговор.
да Това би повлияло на производителността, освен ако колекцията има много по-малко данни.
да С ограничените колекции може да загубите данни.