Cara menyalin koleksi dari satu database ke yang lain di MongoDB


221

Apakah ada cara sederhana untuk melakukan hal ini?


40
Jawaban yang diterima adalah metode terbaik pada tahun 2012, tetapi sekarang db.cloneCollection () seringkali merupakan solusi yang lebih baik. Ada beberapa jawaban yang lebih baru di sini yang merujuk pada ini, jadi jika Anda datang ke sini dari Google (seperti yang saya lakukan) lihat semua jawabannya!
Kelvin

4
Pastikan untuk membaca jawaban lain juga untuk memastikan bahwa itu sesuai dengan kebutuhan Anda, bukan hanya @kelvin dalam situasi mereka
PW Kad

Jawaban:


206

Saat ini tidak ada perintah di MongoDB yang akan melakukan ini. Harap perhatikan tiket JIRA dengan permintaan fitur terkait .

Anda dapat melakukan sesuatu seperti:

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

Harap dicatat bahwa dengan ini, kedua database harus berbagi mongod yang sama agar ini dapat berfungsi.

Selain itu, Anda bisa melakukan mongodump koleksi dari satu database dan kemudian mongorestore koleksi ke database lain.


13
Perhatikan bahwa jika Anda menyalin di shell JS dokumen BSON diterjemahkan ke JSON selama proses sehingga beberapa dokumen mungkin mengalami perubahan jenis. mongodump / mongorestore umumnya merupakan pendekatan yang lebih baik.
Stennie

1
Sepakat. Itu lebih dari sekadar saran menyenangkan untuk bermain-main dengan shell. Plus, itu tidak akan membawa indeks. Jika saya melakukan ini, saya akan melakukan mongodump / mongorestore setiap waktu.
Jason McCay

2
Terima kasih. Harap dicatat bahwa Anda memiliki salah ketik dalam kode, tidak menutup fungsi getSiblingDB. Berikut kode yang diperbaiki: db. <collection_name> .find (). ForEach (fungsi (d) {db.getSiblingDB ('<new_database>') ['<collection_name>'] .insert (d);});
Flaviu

1
ini bekerja dengan baik untuk me-reset mongodb uji dari salinan emas antara tes berjalan. daripada mengkode nama koleksi dengan susah payah Anda dapat melakukan for loop atas semua nama koleksi yang ingin Anda salin dengan db.getCollection (nama) .find (). forEach dan berikan fungsi yang memiliki db.getSiblingDB ("otherdb"). getCollection (nama) .insert (d).
simbo1905

2
apakah ini efisien untuk koleksi ukuran besar?
Khalil Awada

284

Cara terbaik adalah melakukan mongodump lalu mongorestore.

Anda dapat memilih koleksi melalui:

mongodump -d some_database -c some_collection

[Secara opsional, masukkan zip dump ( zip some_database.zip some_database/* -r) dan di scptempat lain]

Kemudian kembalikan:

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

Data yang ada di some_or_other_collectionakan disimpan. Dengan begitu Anda dapat "menambahkan" koleksi dari satu basis data ke basis data lainnya.

Sebelum versi 2.4.3, Anda juga perlu menambahkan kembali indeks Anda setelah Anda menyalin data Anda. Dimulai dengan 2.4.3, proses ini otomatis, dan Anda dapat menonaktifkannya dengan --noIndexRestore.


Tampaknya mongodump tidak berfungsi jika Anda memiliki kata sandi mongo yang dilindungi kata sandi (dan Anda harus!)
Luciano Camilo

3
Ini bekerja pada DB yang dilindungi PW, Anda hanya perlu melewati auth di params
Ben

2
Ini jauh lebih cepat daripada find / forEach / insert, dalam kasus saya 2 menit vs 2 jam
Juraj Paulo

Masukkan nama pengguna untuk basis data dengan --username tetapi tidak --password untuk mendapatkan konfirmasi kata sandi. Cara terbaik adalah tidak meletakkan kata sandi pada baris perintah Anda (berakhir dengan menyimpannya ke .bash_history atau serupa)
Chanoch

Kecil: Saya menemukan file dalam subfolder bernama oleh some_database sehingga ini berfungsi untuk saya: mongorestore -d some_other_db -c some_or_other_collection dump / some_database / some_collection.bson
Aviko

88

Sebenarnya, ada adalah perintah untuk memindahkan koleksi dari satu database ke yang lain. Hanya saja tidak disebut "pindah" atau "salin".

Untuk menyalin koleksi, Anda dapat mengkloningnya pada db yang sama, lalu memindahkan klonnya.

Untuk mengkloning:

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

Untuk bergerak:

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

Jawaban lain lebih baik untuk menyalin koleksi, tetapi ini sangat berguna jika Anda ingin memindahkannya.


3
Terima kasih banyak! Hanya butuh tanda kutip penutup di'db1.source_collection'
andrrs

4
Alih-alih "menggunakan admin" diikuti oleh "db.runCommand (..." Anda dapat melakukan hanya satu perintah, "db.adminCommand (..."
Hamid

25

Saya akan menyalahgunakan fungsi connect di mongo cli mongo doc . jadi itu berarti Anda dapat memulai satu atau lebih koneksi. jika Anda ingin menyalin koleksi pelanggan dari test ke test2 di server yang sama. pertama Anda mulai mongo shell

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

lakukan pencarian normal dan salin 20 catatan pertama ke test2.

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

atau filter berdasarkan beberapa kriteria

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

cukup ubah localhost ke IP atau nama host untuk terhubung ke server jarak jauh. Saya menggunakan ini untuk menyalin data uji ke database uji untuk pengujian.


4
Saat saya mengomentari saran Jason, perlu diketahui bahwa jika Anda menyalin di shell JS dokumen BSON diterjemahkan ke JSON selama proses sehingga beberapa dokumen mungkin mengalami perubahan jenis. Ada pertimbangan serupa dengan Keterbatasan eval dan ini akan menjadi proses yang lebih lambat untuk menyalin sejumlah besar data antara database (terutama di server yang sama). Jadi mongodump / mongorestore FTW :).
Stennie

19

Jika di antara dua instance mongod jarak jauh, gunakan

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

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


Bidang copyIndexesopsi sebenarnya tidak dihormati. Indeks selalu disalin. Lihat SERVER-11418
Gianfranco P.

6
Bungkus itu dalam db.runCommand () yaitu db.runCommand ({cloneCollection: "<collection>", dari: "<hostname>", permintaan: {<query>}})
Daniel de Zwaan

Bagaimana ini bisa digunakan untuk pembaruan tambahan dari satu mongo jarak jauh ke yang lain?
nishant

Saya memiliki data pengguna yang ditambahkan ke satu instance mongo sepanjang hari. Pada akhir hari saya perlu mentransfer baris yang baru ditambahkan ke instance mongo lainnya. Bagaimana ini bisa dicapai?
nishant

@NishantKumar mencoba menetapkan dalam kueri: {} kode ini: $ where: function () {today = new Date (); // today.setHours (0,0,0,0); return (this._id.getTimestamp ()> = hari ini). Lihat stackoverflow.com/questions/42456375/… .
es cologne

18

Saya biasanya melakukan:

use sourcedatabase;
var docs=db.sourcetable.find();
use targetdatabase;
docs.forEach(function(doc) { db.targettable.insert(doc); });

11

untuk koleksi ukuran besar, Anda dapat menggunakan Bulk.insert ()

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

Ini akan menghemat banyak waktu . Dalam kasus saya, saya menyalin koleksi dengan 1.219 dokumen: iter vs Massal (67 detik vs 3 detik)


ini cara yang lebih baik, lebih efisien, palu lebih sedikit db, bekerja untuk semua ukuran dataset.
Jeremie

Jika Anda melakukan ini dengan lebih dari 300 ribu catatan, Anda mungkin perlu menambahkan .limit (300000) setelah ditemukan, dan sebelum pemeriksaan. Jika tidak, sistem mungkin terkunci. Saya biasanya membatasi perubahan massal sekitar 100rb untuk keamanan. Membungkus seluruh hal dalam for for berdasarkan hitungan dan batas.
triunenature

6

Anda dapat menggunakan kerangka agregasi untuk menyelesaikan masalah Anda

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

Harus dicatat, bahwa indeks dari oldCollection tidak akan disalin di newCollection.


5

Saya tahu pertanyaan ini telah dijawab namun secara pribadi saya tidak akan menjawab @JasonMcCays karena fakta bahwa kursor mengalir dan ini dapat menyebabkan loop kursor tak terbatas jika koleksi masih digunakan. Sebagai gantinya saya akan menggunakan snapshot ():

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

jawaban @bens juga bagus dan berfungsi baik untuk cadangan panas koleksi tidak hanya itu tetapi mongorestore tidak perlu berbagi mongod yang sama.


5

Ini mungkin hanya kasus khusus, tetapi untuk koleksi dokumen 100k dengan dua bidang string acak (panjangnya 15-20 karakter), menggunakan mapreduce bodoh hampir dua kali lebih cepat dari find-insert / copyTo:

db.coll.mapReduce(function() { emit(this._id, this); }, function(k,vs) { return vs[0]; }, { out : "coll2" })

5

Menggunakan pymongo, Anda harus memiliki kedua database di mongod yang sama, saya melakukan hal berikut:


db = database asli
db2 = database yang akan disalin

cursor = db["<collection to copy from>"].find()
for data in cursor:
    db2["<new collection>"].insert(data)

1
ini akan memakan banyak waktu jika ukuran data sangat besar. Atau Anda dapat menggunakan bulk_insert
nishant

1
Ya, ini hanya cara cepat dan kotor yang saya temukan bekerja untuk saya, basis data saya tidak terlalu besar, tetapi juga tidak kecil dan tidak terlalu lama, tetapi ya Anda benar.
vbhakta

2

Ini tidak akan menyelesaikan masalah Anda tetapi shell mongodb memiliki copyTometode yang menyalin koleksi ke koleksi lain di database yang sama :

db.mycoll.copyTo('my_other_collection');

Ini juga menerjemahkan dari BSON ke JSON, jadi mongodump/ mongorestoreadalah cara terbaik untuk pergi, seperti yang orang lain katakan.


Luar biasa. Sayangnya referensi shell Mongo tampaknya tidak menyebutkan metode ini.
pgl

Ya, saya tahu, tetapi shell MongoDB mengagumkan, jika Anda mengetik db.collname. [TAB] Anda akan melihat semua metode yang tersedia pada objek koleksi. tip ini berfungsi untuk semua objek lainnya.
Roberto

Masalahnya adalah kurangnya bantuan untuk perintah-perintah itu! Sangat berguna untuk dapat melihat kode, meskipun dengan menghilangkan parens ke pemanggilan metode.
pgl

2
Sayangnya, perintah ini sekarang sudah tidak digunakan lagi sejak versi 3.0.
Harry

2

Jika RAM bukan masalah menggunakan insertManycara lebih cepat dari forEachloop.

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())

1

Jika beberapa pengguna heroku tersandung di sini dan seperti saya ingin menyalin beberapa data dari pementasan basis data ke basis data produksi atau sebaliknya inilah cara Anda melakukannya dengan sangat nyaman (NB Saya harap tidak ada kesalahan ketik di sana, tidak dapat memeriksanya atm., Saya akan mencoba mengonfirmasi validitas kode secepatnya):

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"

1

Anda selalu dapat menggunakan Robomongo. Pada v0.8.3 ada alat yang dapat melakukan ini dengan mengklik kanan koleksi dan memilih "Salin Koleksi ke Database"

Untuk detailnya, lihat http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Fitur ini telah dihapus di 0.8.5 karena sifatnya yang buggy sehingga Anda harus menggunakan 0.8.3 atau 0.8.4 jika Anda ingin mencobanya.


6
Fitur Robomongo ini masih tidak stabil. Ini adalah peluang 50/50 untuk membuatnya bekerja.
thedp

2
Ini sepertinya telah dihapus dari 0.8.5
Carasel

0

Dalam kasus saya, saya harus menggunakan subset atribut dari koleksi lama di koleksi baru saya. Jadi saya akhirnya memilih atribut-atribut itu sambil memanggil masukkan pada koleksi baru.

db.<sourceColl>.find().forEach(function(doc) { 
    db.<newColl>.insert({
        "new_field1":doc.field1,
        "new_field2":doc.field2,
        ....
    })
});`


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.