W zależności od wymagań jednym z podejść może być zaprojektowanie schematu w taki sposób, aby każdy dokument posiadał odpowiednie możliwości do przechowywania więcej niż jednego dokumentu i sam w sobie działa jak zamknięty pojemnik .
{
"_id":Number,
"doc":Array
}
Każdy dokument w kolekcji będzie działał jak zamknięty kontener , a dokumenty będą przechowywane jako tablica w doc
pole. doc
pole będące tablicą, zachowa kolejność wstawiania. Możesz ograniczyć liczbę dokumentów do n
. Więc _id
pole każdego dokumentu kontenera będzie zwiększane o n
, wskazując liczbę dokumentów, które może przechowywać dokument kontenera.
Robiąc to, unikasz dodanie extra fields
do dokumentu, extra indices
, unnecessary sorts
.
Wstawianie pierwszego rekordu
tj. gdy kolekcja jest pusta.
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
Wstawianie kolejnych rekordów
- Zidentyfikuj
_id
ostatniego dokumentu kontenera inumber
posiadanych dokumentów. - Jeśli liczba przechowywanych dokumentów jest mniejsza niż
n
, a następnie zaktualizuj dokument kontenera z nowym dokumentem, w przeciwnym razie utwórz nowy dokument kontenera.
Powiedz, że każdy container document
może pomieścić 5
co najwyżej dokumentów i chcemy wstawić nowy dokument.
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 ]
});
}
})
Zauważ, że aggregation
, działa po stronie serwera i jest bardzo wydajny, należy również pamiętać, że aggregation
zwróci Ci dokument zamiast kursora w wersjach previous to 2.6
. Więc musiałbyś zmodyfikować powyższy kod, aby po prostu wybierać z jednego dokumentu, zamiast iterować kursor.
Wstawianie nowego dokumentu między dokumentami
Teraz, jeśli chcesz wstawić nowy dokument między dokumentami 1
i 2
, wiemy, że dokument powinien znajdować się w kontenerze z _id=0
i powinien być umieszczony w second
pozycja w doc
tablica tego kontenera.
więc używamy $each
i $position
operatorów do wstawiania w określone pozycje.
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
Obsługa przepływu
Teraz musimy zająć się dokumentami overflowing
w każdym container
, powiedzmy, że wstawiamy nowy dokument pomiędzy, w kontenerze z _id=0
. Jeśli kontener ma już 5
dokumenty, musimy move the last document to the next container
i rób to, dopóki wszystkie pojemniki nie pomieszczą dokumentów w swoim zakresie, w razie potrzeby w końcu musimy utworzyć pojemnik do przechowywania przepełnionych dokumentów.
Ta złożona operacja powinna być zrobione po po stronie serwera . Aby sobie z tym poradzić, możemy stworzyć skrypt taki jak ten poniżej i register
to z 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);
}
}
Tak więc after every insertion in between
, możemy wywołać tę function
przekazując identyfikator kontenera, handleOverFlow(containerId)
.
Pobieranie wszystkich rekordów w kolejności
Po prostu użyj $unwind
operator w aggregate pipeline
.
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
Ponowne zamawianie dokumentów
Każdy dokument możesz przechowywać w zamkniętym kontenerze z polem „_id”:
.."doc":[{"_id":0,","name":"xyz",...}..]..
Zdobądź tablicę „doc” zamkniętego kontenera, którego elementy chcesz zmienić.
var docArray = db.col.find({"_id":0})[0];
Zaktualizuj ich identyfikatory, aby po sortowaniu zmieniła się kolejność elementów.
Sortuj tablicę na podstawie ich identyfikatorów.
docArray.sort( function(a, b) {
return a._id - b._id;
});
zaktualizuj zamknięty kontener z powrotem za pomocą nowej tablicy dokumentów.
Ale z drugiej strony wszystko sprowadza się do tego, które podejście jest wykonalne i najlepiej odpowiada Twoim wymaganiom.
Przechodząc do twoich pytań:
Dokumenty jako tablice.
użyj $each
i $position
operatory w db.collection.update()
działają zgodnie z opisem w mojej odpowiedzi.
TAk. Miałoby to wpływ na wydajność, chyba że kolekcja zawiera bardzo mniej danych.
TAk. W przypadku ograniczonych kolekcji możesz utracić dane.