Няма опция за връщане назад (връщането има различно значение в контекста на MongoDB) и, строго погледнато, няма поддържан начин за връщане на тези документи - предпазните мерки, които можете/трябва да предприемете, са обхванати в коментарите. С казаното обаче, ако изпълнявате набор от реплики, дори набор от реплики с един възел, тогава имате oplog
. С oplog
което обхваща кога са били поставени документите, може да успеете да ги възстановите.
Най-лесният начин да илюстрирате това е с пример. Ще използвам опростен пример само със 100 изтрити документа, които трябва да бъдат възстановени. За да надхвърлите това (огромен брой документи или може би искате да възстановите само селективно и т.н.), вие или ще искате да промените кода, за да итерирате над курсора, или да го напишете, като използвате избрания от вас език извън обвивката на MongoDB. Основната логика остава същата.
Първо, нека създадем нашата примерна колекция foo
в базата данни dropTest
. Ще вмъкнем 100 документа без name
поле и 100 документа с идентично name
поле, за да могат да бъдат премахнати по погрешка по-късно:
use dropTest;
for(i=0; i < 100; i++){db.foo.insert({_id : i})};
for(i=100; i < 200; i++){db.foo.insert({_id : i, name : "some_x_name"})};
Сега нека симулираме случайното премахване на нашите 100 name
документи:
> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })
Тъй като работим в набор от реплики, все още имаме запис на тези документи в oplog
(вмъкват се) и за щастие тези вложки (все още) не са паднали от края на oplog
(oplog
е ограничена колекция запомни) . Да видим дали ще ги намерим:
use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100
Преброяването изглежда правилно, изглежда, че документите ни са все още. От опит знам, че единствената част от oplog
записът, който ще ни трябва тук, е o
поле, така че нека добавим проекция, за да върне само това (изходът е изрязан за краткост, но разбирате идеята):
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1});
{ "o" : { "_id" : 100, "name" : "some_x_name" } }
{ "o" : { "_id" : 101, "name" : "some_x_name" } }
{ "o" : { "_id" : 102, "name" : "some_x_name" } }
{ "o" : { "_id" : 103, "name" : "some_x_name" } }
{ "o" : { "_id" : 104, "name" : "some_x_name" } }
За да вмъкнем отново тези документи, можем просто да ги съхраним в масив, след което да повторим през масива и да вмъкнем съответните части. Първо, нека създадем нашия масив:
var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100
След това си напомняме, че сега имаме само 100 документа в колекцията, след това преглеждаме 100-те вмъквания и накрая проверяваме отново броя си:
use dropTest;
db.foo.count();
100
// simple for loop to re-insert the relevant elements
for (var i = 0; i < deletedDocs.length; i++) {
db.foo.insert({_id : deletedDocs[i].o._id, name : deletedDocs[i].o.name});
}
// check total and name counts again
db.foo.count();
200
db.foo.count({name : "some_x_name"})
100
И ето го, с някои предупреждения:
- Това не е предназначено да бъде истинска стратегия за възстановяване, вижте резервните копия (MMS, други), забавени вторични елементи за това, както е споменато в коментарите
- Няма да е особено бързо да заявявате документите извън oplog (всяка заявка за oplog е сканиране на таблица) в голяма натоварена система.
- Документите могат да остареят извън oplog по всяко време (можете, разбира се, да направите копие на oplog за по-късна употреба, за да ви даде повече време)
- В зависимост от вашето работно натоварване може да се наложи да премахнете дублирането на резултатите, преди да ги поставите отново
- По-големите набори от документи ще бъдат твърде големи за масив, както е показано, така че вместо това ще трябва да преглеждате курсор
- Форматът на
oplog
се счита за вътрешен и може да се промени по всяко време (без предизвестие), така че използвайте на свой собствен риск