Wstawianie partii Mongoose (mongodb)?

114

Czy Mongoose v3.6 + obsługuje teraz wstawianie wsadowe? Szukałem przez kilka minut, ale wszystko, co pasuje do tego zapytania, ma kilka lat, a odpowiedź była jednoznaczna nie.

Edytować:

Na przyszłość odpowiedzią jest użycie Model.create(). create()akceptuje tablicę jako pierwszy argument, więc możesz przekazać swoje dokumenty do wstawienia jako tablicę.

Zobacz dokumentację Model.create ()

Geuis
źródło
Zobacz tę odpowiedź na poprzednie pytanie.
JohnnyHK
Dzięki. To właśnie znalazłem po opublikowaniu.
Geuis
@Geuis, dodaj swoją zmianę jako odpowiedź i zaakceptuj ją, aby rozwiązać swoje pytanie.
Filip Dupanović
Model.create () jest powolny i jeśli rozważasz wstawianie ogromnej liczby dokumentów, lepiej zamiast tego zastosować to podejście .
Lucio Paiva

Odpowiedzi:

162

Model.create () vs Model.collection.insert (): szybsze podejście

Model.create()to zły sposób wykonywania wkładek, jeśli masz do czynienia z bardzo dużą masą. Będzie to bardzo powolne . W takim przypadku powinieneś użyć Model.collection.insert, który działa znacznie lepiej . W zależności od wielkości masy Model.create()nawet się rozbije! Próbowałem z milionem dokumentów, bez powodzenia. Korzystanie z Model.collection.insertniego zajęło zaledwie kilka sekund.

Model.collection.insert(docs, options, callback)
  • docs jest tablicą dokumentów do wstawienia;
  • optionsjest opcjonalnym obiektem konfiguracyjnym - zobacz dokumentację
  • callback(err, docs)zostanie wywołana po zapisaniu wszystkich dokumentów lub wystąpieniu błędu. W przypadku sukcesu, docs to szereg utrwalonych dokumentów.

Jako autor Mongoose Punkty się tutaj metoda ta ominie wszelkich procedur walidacji i dostępu kierowca Mongo bezpośrednio. Jest to kompromis, który musisz zrobić, ponieważ obsługujesz dużą ilość danych, w przeciwnym razie nie byłbyś w stanie w ogóle wstawić ich do bazy danych (pamiętaj, że mówimy tutaj o setkach tysięcy dokumentów).

Prosty przykład

var Potato = mongoose.model('Potato', PotatoSchema);

var potatoBag = [/* a humongous amount of potato objects */];

Potato.collection.insert(potatoBag, onInsert);

function onInsert(err, docs) {
    if (err) {
        // TODO: handle error
    } else {
        console.info('%d potatoes were successfully stored.', docs.length);
    }
}

Aktualizacja 2019-06-22 : chociaż insert()nadal można go używać dobrze, został wycofany na korzyść insertMany(). Parametry są dokładnie takie same, więc możesz go po prostu użyć jako zamiennika typu drop-in i wszystko powinno działać dobrze (cóż, wartość zwracana jest nieco inna, ale prawdopodobnie i tak jej nie używasz).

Odniesienie

Lucio Paiva
źródło
1
groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds Mówi wszystko naprawdę.
arcseldon
Proszę podać przykład z Mongoose.
Steve K
15
Od Model.collection przechodzi bezpośrednio przez sterownik Mongo, tracisz wszystkie zgrabne rzeczy, w tym sprawdzanie poprawności i haczyki. Tylko coś do zapamiętania. Model.createtraci haczyki, ale nadal przechodzi przez weryfikację. Jeśli chcesz tego wszystkiego, musisz iterować inew MyModel()
Pier-Luc Gendreau
1
@ Pier-LucGendreau Masz całkowitą rację, ale jest to kompromis, który musisz zrobić, gdy zaczniesz zajmować się ogromną ilością danych.
Lucio Paiva,
1
Uważaj na nowych czytelników: „Zmieniono w wersji 2.6: funkcja insert () zwraca obiekt, który zawiera stan operacji”. Żadnych więcej dokumentów.
Mark Ni
117

Mongoose 4.4.0 obsługuje teraz wstawianie zbiorcze

Mongoose 4.4.0 wprowadza --true-- zbiorcze wstawianie z metodą modelu .insertMany(). Jest to znacznie szybsze niż zapętlanie .create()lub udostępnianie tablicy.

Stosowanie:

var rawDocuments = [/* ... */];

Book.insertMany(rawDocuments)
    .then(function(mongooseDocuments) {
         /* ... */
    })
    .catch(function(err) {
        /* Error handling */
    });

Lub

Book.insertMany(rawDocuments, function (err, mongooseDocuments) { /* Your callback function... */ });

Możesz go śledzić na:

Derek
źródło
2
W tej chwili ta metoda nie obsługuje opcji.
Amri,
Dziękuję za Twoją odpowiedź. Masz pojęcie, jakie powinno być analizowanie surowych dokumentów? Wypróbowałem to z tablicą obiektów Json i wszystko, co wstawił, to tylko ich identyfikatory. :(
Ondrej Tokar
4
Czym się to różni od bulkWrite? Zobacz tutaj: stackoverflow.com/questions/38742475/…
Ondrej Tokar
insertMany nie działa dla mnie. Mam fatal error allocation failed. Ale jeśli używam collection.insert Działa idealnie.
John
Czy to zadziała z dodatkowymi rzeczami, które zapewnia schemat Mangusta? ex doda dane, jeśli nie istniejedateCreated : { type: Date, default: Date.now },
gniazdo puste
22

Rzeczywiście, możesz użyć metody „tworzenia” Mongoose, może ona zawierać tablicę dokumentów, zobacz ten przykład:

Candy.create({ candy: 'jelly bean' }, { candy: 'snickers' }, function (err, jellybean, snickers) {
});

Funkcja wywołania zwrotnego zawiera wstawione dokumenty. Nie zawsze wiesz, ile elementów należy wstawić (stała długość argumentów, jak powyżej), więc możesz je przeglądać w pętli:

var insertedDocs = [];
for (var i=1; i<arguments.length; ++i) {
    insertedDocs.push(arguments[i]);
}

Aktualizacja: lepsze rozwiązanie

Lepszym rozwiązaniem byłoby użycie Candy.collection.insert()zamiast Candy.create()- użyte w powyższym przykładzie - ponieważ jest szybsze ( create()wywołuje Model.save()każdy element, więc jest wolniejszy).

Więcej informacji można znaleźć w dokumentacji Mongo: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

(dzięki arcseldonowi za wskazanie tego)

benske
źródło
groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds - w zależności od tego, co chcesz, link ma lepszą opcję.
arcseldon
Czy nie masz na myśli {type:'jellybean'}zamiast {type:'jelly bean'}? Przy okazji. jakie to dziwne typy? Czy są częścią Mongoose API?
Steve K
2
Cóż, to zły wybór nazewnictwa, ponieważ typejest zwykle zarezerwowany w Mongoose do określania ADT obiektu bazy danych.
Steve K,
2
@sirbenbenji Zmieniłem to, ale był to przykład obecny również w oficjalnej dokumentacji. Myślę, że nie było potrzeby głosowania przeciw temu.
benske
1
Poprzez zajęcie mienia .collection jesteś pominięciem Mongoose (walidacji, metody 'pre' ...)
Derek
4

Możesz wykonać zbiorcze wstawianie przy użyciu powłoki mongoDB, wstawiając wartości do tablicy.

db.collection.insert([{values},{values},{values},{values}]);
SUNDARRAJAN K
źródło
czy jest sposób w mangusta na wkład luzem?
SUNDARRAJAN K
1
YourModel.collection.insert()
Bill Dami
Adresując właściwość .collection, omijasz Mongoose (sprawdzanie poprawności, metody `` pre '' ...)
Derek
To nie jest mangusta, a surowa collection.insertodpowiedź została udzielona kilka tygodni przed tą odpowiedzią i wyjaśniona znacznie bardziej szczegółowo.
Dan Dascalescu
4

Możesz wykonać zbiorcze wstawianie za pomocą mangusty, jako odpowiedzi o najwyższym wyniku. Ale przykład nie może działać, powinien być:

/* a humongous amount of potatos */
var potatoBag = [{name:'potato1'}, {name:'potato2'}];

var Potato = mongoose.model('Potato', PotatoSchema);
Potato.collection.insert(potatoBag, onInsert);

function onInsert(err, docs) {
    if (err) {
        // TODO: handle error
    } else {
        console.info('%d potatoes were successfully stored.', docs.length);
    }
}

Nie używaj instancji schematu do wstawiania zbiorczego, powinieneś użyć zwykłego obiektu mapy.

user2582680
źródło
Pierwsza odpowiedź nie jest zła, ma po prostu walidację
Luca Steeb
1
Adresując właściwość .collection, omijasz Mongoose (sprawdzanie poprawności, metody 'pre' ...)
Derek
4

Oto oba sposoby zapisywania danych za pomocą insertMany i save

1) Mongoose zapisuje insertManymasowo szereg dokumentów

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const data = [/* array of object which data need to save in db */];

    Potato.insertMany(data)  
    .then((result) => {
            console.log("result ", result);
            res.status(200).json({'success': 'new documents added!', 'data': result});
    })
    .catch(err => {
            console.error("error ", err);
            res.status(400).json({err});
    });
})

2) Mongoose zapisuje tablicę dokumentów w formacie .save()

Dokumenty te będą zapisywać się równolegle.

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const saveData = []
    const data = [/* array of object which data need to save in db */];
    data.map((i) => {
        console.log(i)
        var potato = new Potato(data[i])
        potato.save()
        .then((result) => {
            console.log(result)
            saveData.push(result)
            if (saveData.length === data.length) {
                res.status(200).json({'success': 'new documents added!', 'data': saveData});
            }
        })
        .catch((err) => {
            console.error(err)
            res.status(500).json({err});
        })
    })
})
Arpit
źródło
3

Wygląda na to, że przy używaniu mangusty istnieje limit ponad 1000 dokumentów

Potato.collection.insert(potatoBag, onInsert);

Możesz użyć:

var bulk = Model.collection.initializeOrderedBulkOp();

async.each(users, function (user, callback) {
    bulk.insert(hash);
}, function (err) {
    var bulkStart = Date.now();
    bulk.execute(function(err, res){
        if (err) console.log (" gameResult.js > err " , err);
        console.log (" gameResult.js > BULK TIME  " , Date.now() - bulkStart );
        console.log (" gameResult.js > BULK INSERT " , res.nInserted)
      });
});

Ale jest to prawie dwa razy szybsze w przypadku testowania na 10000 dokumentów:

function fastInsert(arrOfResults) {
var startTime = Date.now();
    var count = 0;
    var c = Math.round( arrOfResults.length / 990);

    var fakeArr = [];
    fakeArr.length = c;
    var docsSaved = 0

    async.each(fakeArr, function (item, callback) {

            var sliced = arrOfResults.slice(count, count+999);
            sliced.length)
            count = count +999;
            if(sliced.length != 0 ){
                    GameResultModel.collection.insert(sliced, function (err, docs) {
                            docsSaved += docs.ops.length
                            callback();
                    });
            }else {
                    callback()
            }
    }, function (err) {
            console.log (" gameResult.js > BULK INSERT AMOUNT: ", arrOfResults.length, "docsSaved  " , docsSaved, " DIFF TIME:",Date.now() - startTime);
    });
}
ddennis
źródło
1
Adresując właściwość .collection, omijasz Mongoose (sprawdzanie poprawności, metody `` pre '' ...)
Derek
0

Udostępnianie działającego i odpowiedniego kodu z naszego projektu:

//documentsArray is the list of sampleCollection objects
sampleCollection.insertMany(documentsArray)  
    .then((res) => {
        console.log("insert sampleCollection result ", res);
    })
    .catch(err => {
        console.log("bulk insert sampleCollection error ", err);
    });
Zameer
źródło
.insertManyRozwiązanie zostało już podane (i wyjaśnić), w tym 2016 odpowiedzi .
Dan Dascalescu