Jak skopiować kolekcję z jednej bazy danych do innej w MongoDB

221

Czy istnieje prosty sposób to zrobić?

EasonBlack
źródło
40
Przyjęta odpowiedź była prawdopodobnie najlepszą metodą w 2012 roku, ale teraz db.cloneCollection () jest często lepszym rozwiązaniem. Jest tu kilka najnowszych odpowiedzi, które odnoszą się do tego, więc jeśli przyszedłeś tutaj z Google (tak jak ja), spójrz na wszystkie odpowiedzi!
Kelvin
4
Przeczytaj także pozostałe odpowiedzi, aby upewnić się, że odpowiadają Twoim potrzebom, a nie tylko @kelvin w jego / jej sytuacji
PW Kad

Odpowiedzi:

206

W tej chwili nie ma polecenia w MongoDB, które by to zrobiło. Zwróć uwagę na bilet JIRA z powiązanym żądaniem funkcji .

Możesz zrobić coś takiego:

db.<collection_name>.find().forEach(function(d){ db.getSiblingDB('<new_database>')['<collection_name>'].insert(d); });

Zauważ, że dzięki temu dwie bazy danych musiałyby współdzielić tego samego mongod, aby to działało.

Poza tym możesz zrobić mongodump kolekcji z jednej bazy danych, a następnie mongorestore kolekcji w drugiej bazie danych.

Jason McCay
źródło
13
Zauważ, że jeśli skopiujesz w powłoce JS, dokumenty BSON są dekodowane do JSON podczas procesu, więc niektóre dokumenty mogą powodować zmiany typu. mongodump / mongorestore są ogólnie lepszym podejściem.
Stennie,
1
Zgoda. To była bardziej zabawna propozycja zabawy z muszlą. Ponadto nie przyniosłoby indeksów. Gdybym to robił, robiłbym mongodump / mongorestore za każdym razem.
Jason McCay
2
Dzięki. Pamiętaj, że w kodzie jest literówka, która nie zamyka funkcji getSiblingDB. Oto poprawiony kod: db. <nazwa_kolekcji> .find (). ForEach (funkcja (d) {db.getSiblingDB ('<nowa baza danych>') ['<nazwa_kolekcji>'] .insert (d);});
Flaviu
1
działało to dobrze w przypadku resetowania mongodb testowego ze złotej kopii między uruchomieniami testowymi. zamiast na stałe kodować nazwy kolekcji, możesz wykonać pętlę for nad wszystkimi nazwami kolekcji, które chcesz skopiować za pomocą db.getCollection (name) .find (). forEach i podaj funkcję, która ma db.getSiblingDB („otherdb”). getCollection (name) .insert (d).
simbo1905
2
czy jest to skuteczne w przypadku kolekcji o dużych rozmiarach?
Khalil Awada,
284

Najlepszym sposobem jest zrobienie mongodump, a następnie mongorestore.

Możesz wybrać kolekcję poprzez:

mongodump -d some_database -c some_collection

[Opcjonalnie zip dump ( zip some_database.zip some_database/* -r) i scpgdzie indziej]

Następnie przywróć:

mongorestore -d some_other_db -c some_or_other_collection dump/some_collection.bson

Istniejące dane some_or_other_collectionzostaną zachowane. W ten sposób możesz „dołączyć” kolekcję z jednej bazy danych do drugiej.

Przed wersją 2.4.3 trzeba będzie również dodać indeksy po skopiowaniu danych. Począwszy od wersji 2.4.3, proces ten jest automatyczny i można go wyłączyć za pomocą --noIndexRestore.

Ben
źródło
Wygląda na to, że mongodump nie działa, jeśli masz chronioną hasłem instancję mongo (i powinieneś!)
Luciano Camilo
3
Działa na DB chronionych PW, wystarczy przekazać auth w params
Ben
2
Jest to o wiele szybsze niż find / forEach / insert, w moim przypadku 2 minuty vs 2 godziny
Juraj Paulo
Podaj nazwę użytkownika dla bazy danych z opcją --username, ale nie --password, aby wyświetlić monit o hasło. Najlepiej nie wpisywać hasła w wierszu poleceń (w końcu zapisywać je w historii .bash lub podobnej)
Chanoch,
Drobne: Znalazłem plik w podfolderze o nazwie some_database, więc to działa dla mnie: mongorestore -d some_other_db -c some_or_other_collection dump / some_database / some_collection.bson
Aviko
88

Faktycznie, nie jest rozkaz, aby przenieść kolekcję z jednej bazy do drugiej. To po prostu nie nazywa się „przenieś” lub „skopiuj”.

Aby skopiować kolekcję, możesz sklonować ją na tym samym pliku db, a następnie przenieść klon.

Klonować:

> use db1
> db.source_collection.find().forEach( function(x){db.collection_copy.insert(x)} );

Przenieść:

> use admin
switched to db admin
> db.runCommand({renameCollection: 'db1.source_collection', to: 'db2.target_collection'}) // who'd think rename could move?

Inne odpowiedzi są lepsze do kopiowania kolekcji, ale jest to szczególnie przydatne, jeśli chcesz ją przenieść.

Anuj Gupta
źródło
3
Dzięki działa świetnie! Potrzebuję tylko apostrofu zamykającego'db1.source_collection'
2015
4
Zamiast „użyj administratora”, po którym następuje „db.runCommand (...” Możesz wykonać tylko jedną komendę, „db.adminCommand (...”
Hamid,
25

Nadużywam funkcji łączenia w mongo cli mongo doc . oznacza to, że możesz nawiązać jedno lub więcej połączeń. jeśli chcesz skopiować kolekcję klientów z testu na test2 na tym samym serwerze. najpierw zaczniesz mongo shell

use test
var db2 = connect('localhost:27017/test2')

wykonaj normalne wyszukiwanie i skopiuj pierwsze 20 rekordów do testu2.

db.customer.find().limit(20).forEach(function(p) { db2.customer.insert(p); });

lub filtruj według niektórych kryteriów

db.customer.find({"active": 1}).forEach(function(p) { db2.customer.insert(p); });

wystarczy zmienić localhost na IP lub nazwę hosta, aby połączyć się ze zdalnym serwerem. Używam tego do kopiowania danych testowych do testowej bazy danych w celu przetestowania.

Wayne
źródło
4
Jak skomentowałem sugestię Jasona, pamiętaj, że jeśli kopiujesz w powłoce JS, dokumenty BSON są dekodowane do JSON podczas procesu, więc niektóre dokumenty mogą powodować zmiany typu. Istnieją podobne względy do Ograniczeń ewaluacji i będzie to wolniejszy proces kopiowania znacznych ilości danych między bazami danych (szczególnie na tym samym serwerze). Więc mongodump / mongorestore FTW :).
Stennie,
19

Jeśli pomiędzy dwoma zdalnymi instancjami mongod, użyj

{ cloneCollection: "<collection>", from: "<hostname>", query: { <query> }, copyIndexes: <true|false> } 

Zobacz http://docs.mongodb.org/manual/reference/command/cloneCollection/

es kolonia
źródło
Pole copyIndexesopcji faktycznie nie jest przestrzegane. Indeksy są zawsze kopiowane. Patrz SERWER-11418
Gianfranco P.
6
Zapakuj to w db.runCommand () tj. Db.runCommand ({cloneCollection: "<collection>", from: "<nazwa hosta>", zapytanie: {<query>}})
Daniel de Zwaan
Jak można tego użyć do przyrostowych aktualizacji z jednego zdalnego mongo na inny?
nishant
Mam dane użytkownika dodawane do jednego wystąpienia mongo w ciągu dnia. Na koniec dnia muszę przenieść nowo dodane wiersze do innej instancji mongo. Jak można to osiągnąć?
nishant
@NishantKumar próbuje ustawić w zapytaniu: {} ten kod: $ gdzie: funkcja () {dzisiaj = nowa data (); // Today.setHours (0,0,0,0); return (this._id.getTimestamp ()> = dzisiaj). Zobacz stackoverflow.com/questions/42456375/… .
es cologne
18

Zwykle robiłbym:

use sourcedatabase;
var docs=db.sourcetable.find();
use targetdatabase;
docs.forEach(function(doc) { db.targettable.insert(doc); });
ffflabs
źródło
11

dla kolekcji o dużych rozmiarach można użyć Bulk.insert ()

var bulk = db.getSiblingDB(dbName)[targetCollectionName].initializeUnorderedBulkOp();
db.getCollection(sourceCollectionName).find().forEach(function (d) {
    bulk.insert(d);
});
bulk.execute();

Zaoszczędzi to dużo czasu . W moim przypadku kopiuję kolekcję z 1219 dokumentami: iter vs Bulk (67 sekund vs 3 sekundy)

nametal
źródło
jest to o wiele lepsze, bardziej wydajne, młotkuje mniej db, działa dla dowolnego rozmiaru zestawu danych.
Jeremie
Jeśli robisz to z ponad 300 000 rekordów, być może będziesz musiał dodać .limit (300000) po znalezieniu i przed foreach. W przeciwnym razie system może się zablokować. Ze względów bezpieczeństwa zwykle ograniczam masowe zmiany do około 100 tys. Zawijanie całej rzeczy w pętli for na podstawie liczby i limitu.
triunenatura
6

Możesz użyć struktury agregacji, aby rozwiązać problem

db.oldCollection.aggregate([{$out : "newCollection"}])

Należy zauważyć, że indeksy z oldCollection nie będą kopiowane w newCollection.

Alexander Makarov
źródło
5

Wiem, że na to pytanie udzielono odpowiedzi, ale ja osobiście nie odpowiedziałbym na @JasonMcCays z powodu faktu, że kursory przesyłają strumieniowo, co może powodować nieskończoną pętlę kursora, jeśli kolekcja jest nadal używana. Zamiast tego użyłbym snapshot ():

http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database

Odpowiedź @bens jest również dobra i działa dobrze na gorące kopie zapasowe kolekcji nie tylko to, ale sklep mongorestore nie musi dzielić tego samego mongod.

Sammaye
źródło
5

Może to być tylko szczególny przypadek, ale w przypadku zbioru 100 000 dokumentów z dwoma losowymi polami ciągów (długość wynosi 15-20 znaków) użycie głupej redukcji map jest prawie dwa razy szybsze niż znajdowanie-wstawianie / kopiowanie

db.coll.mapReduce(function() { emit(this._id, this); }, function(k,vs) { return vs[0]; }, { out : "coll2" })
Vajk Hermecz
źródło
5

Używając pymongo, musisz mieć obie bazy danych na tym samym mongod, zrobiłem następujące:


db = oryginalna baza danych
db2 = baza danych do skopiowania

cursor = db["<collection to copy from>"].find()
for data in cursor:
    db2["<new collection>"].insert(data)
vbhakta
źródło
1
zajęłoby to dużo czasu, jeśli rozmiar danych jest ogromny. Alternatywnie możesz użyć opcji bulk_insert
nishant
1
Tak, to był tylko szybki i brudny sposób, który dla mnie działał, moja baza danych nie była zbyt duża, ale też nie była mała i nie trwała zbyt długo, ale tak, masz rację.
vbhakta
2

To nie rozwiąże twojego problemu, ale powłoka mongodb ma copyTometodę, która kopiuje kolekcję do innej w tej samej bazie danych :

db.mycoll.copyTo('my_other_collection');

To także tłumaczy z BSON na JSON, więc mongodump/ mongorestoresą najlepszą drogą, jak mówili inni.

Roberto
źródło
Doskonały. Niestety odwołanie do powłoki Mongo nie wspomina o tej metodzie.
pgl
Tak, wiem, ale powłoka MongoDB jest niesamowita, jeśli wpiszesz db.collname. [TAB] zobaczysz wszystkie dostępne metody na obiekcie kolekcji. ta wskazówka działa dla wszystkich innych obiektów.
Roberto
Problemem jest brak pomocy dla tych poleceń! Warto zobaczyć kod, pomijając pareny w wywołaniu metody.
pgl
2
Niestety, to polecenie jest przestarzałe od wersji 3.0.
Harry
2

Jeśli pamięć RAM nie stanowi problemu, użycie insertManyjest znacznie szybsze niż forEachpętla.

var db1 = connect('<ip_1>:<port_1>/<db_name_1>')
var db2 = connect('<ip_2>:<port_2>/<db_name_2>')

var _list = db1.getCollection('collection_to_copy_from').find({})
db2.collection_to_copy_to.insertMany(_list.toArray())
Uday Krishna
źródło
1

W przypadku, gdy niektórzy użytkownicy Heroku natkną się tutaj i tak jak ja chcą skopiować niektóre dane z bazy pomostowej do produkcyjnej bazy danych lub odwrotnie, oto jak to zrobić bardzo wygodnie (NB Mam nadzieję, że nie ma tam literówek, nie mogę sprawdzić bankomatu., Spróbuję potwierdzić poprawność kodu jak najszybciej):

to_app="The name of the app you want to migrate data to"
from_app="The name of the app you want to migrate data from"
collection="the collection you want to copy"
mongohq_url=`heroku config:get --app "$to_app" MONGOHQ_URL`
parts=(`echo $mongohq_url | sed "s_mongodb://heroku:__" | sed "s_[@/]_ _g"`)
to_token=${parts[0]}; to_url=${parts[1]}; to_db=${parts[2]}
mongohq_url=`heroku config:get --app "$from_app" MONGOHQ_URL`
parts=(`echo $mongohq_url | sed "s_mongodb://heroku:__" | sed "s_[@/]_ _g"`)
from_token=${parts[0]}; from_url=${parts[1]}; from_db=${parts[2]}
mongodump -h "$from_url" -u heroku -d "$from_db" -p"$from_token" -c "$collection" -o col_dump
mongorestore -h "$prod_url" -u heroku -d "$to_app" -p"$to_token" --dir col_dump/"$col_dump"/$collection".bson -c "$collection"
Timo
źródło
1

Zawsze możesz użyć Robomongo. Od wersji 0.8.3 istnieje narzędzie, które może to zrobić, klikając kolekcję prawym przyciskiem myszy i wybierając polecenie „Kopiuj kolekcję do bazy danych”

Aby uzyskać szczegółowe informacje, zobacz http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Ta funkcja została usunięta w wersji 0.8.5 ze względu na jej wadliwy charakter, więc będziesz musiał użyć wersji 0.8.3 lub 0.8.4, jeśli chcesz ją wypróbować.

żużel
źródło
6
Ta funkcja Robomongo jest wciąż niestabilna. Jest szansa 50/50, aby działał.
thedp
2
Wydaje się, że zostało to usunięte z wersji 0.8.5
Carasel
0

W moim przypadku musiałem użyć podzbioru atrybutów ze starej kolekcji w mojej nowej kolekcji. Wybrałem więc te atrybuty, wywołując insert w nowej kolekcji.

db.<sourceColl>.find().forEach(function(doc) { 
    db.<newColl>.insert({
        "new_field1":doc.field1,
        "new_field2":doc.field2,
        ....
    })
});`
dranga
źródło
0

użyj „Studio3T for MongoDB”, które mają narzędzia do eksportowania i importowania, klikając bazę danych, kolekcje lub link do pobrania konkretnej kolekcji: https://studio3t.com/download/

Ahmad Hamzavi
źródło