Come copiare una raccolta da un database all'altro in MongoDB


221

C'è un modo semplice per fare questo?


40
La risposta accettata era probabilmente il metodo migliore nel 2012, ma ora db.cloneCollection () è spesso una soluzione migliore. Ci sono un paio di risposte più recenti qui che si riferiscono a questo, quindi se sei venuto qui da Google (come ho fatto io) dai un'occhiata a tutte le risposte!
Kelvin,

4
Assicurati di leggere anche le altre risposte per assicurarti che corrisponda alle tue esigenze, non solo a quello di @kelvin nella sua situazione
PW Kad,

Risposte:


206

Al momento non esiste alcun comando in MongoDB che lo farebbe. Si prega di notare il biglietto JIRA con relativa richiesta di funzionalità .

Potresti fare qualcosa del tipo:

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

Si noti che con questo, i due database dovrebbero condividere lo stesso mongod per farlo funzionare.

Oltre a ciò, è possibile eseguire una mongodump di una raccolta da un database e quindi salvare la raccolta nell'altro database.


13
Se si copia nella shell JS, i documenti BSON vengono decodificati in JSON durante il processo, pertanto alcuni documenti potrebbero subire modifiche al tipo. mongodump / mongorestore sono generalmente l'approccio migliore.
Stennie,

1
Concordato. Quello era più solo un suggerimento divertente per giocare con la conchiglia. Inoltre, non porterebbe sugli indici. Se lo facessi, farei il mongodump / mongorestore ogni volta.
Jason McCay,

2
Grazie. Si noti che nel codice è presente un refuso, che non chiude la funzione getSiblingDB. Ecco il codice corretto: db. <collection_name> .find (). ForEach (function (d) {db.getSiblingDB ('<new_database>') ['<collection_name>'] .insert (d);});
Flaviu,

1
questo ha funzionato bene per ripristinare un test mongodb da una copia d'oro tra le prove. anziché codificare i nomi delle raccolte, è possibile eseguire un ciclo for su tutti i nomi delle raccolte che si desidera copiare con db.getCollection (name) .find (). forEach e fornire una funzione che ha db.getSiblingDB ("otherdb"). GetCollection (nome) .Insert (d).
simbo1905,

2
è efficace per collezioni di grandi dimensioni?
Khalil Awada,

284

Il modo migliore è fare una mongodump e poi una mongorestore.

È possibile selezionare la raccolta tramite:

mongodump -d some_database -c some_collection

[Facoltativamente, comprimi il dump ( zip some_database.zip some_database/* -r) e scpaltrove]

Quindi ripristinalo:

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

I dati esistenti some_or_other_collectionverranno conservati. In questo modo è possibile "aggiungere" una raccolta da un database a un altro.

Prima della versione 2.4.3, sarà necessario aggiungere nuovamente gli indici dopo aver copiato i dati. A partire dalla 2.4.3, questo processo è automatico e puoi disabilitarlo con --noIndexRestore.


Sembra che mongodump non funzioni se hai un'istanza mongo protetta da password (e dovresti!)
Luciano Camilo,

3
Funziona su DB protetti da PW, devi solo passare l'autenticazione nei parametri
Ben

2
Questo è molto più veloce di find / forEach / insert, nel mio caso 2 minuti contro 2 ore
Juraj Paulo

Passare il nome utente per il database con --username ma non --password per ottenere una richiesta per la password. È meglio non mettere la password sulla riga di comando (finendo per salvarla in .bash_history o simile)
Chanoch

Minore: ho trovato il file nella sottocartella chiamato da some_database quindi questo funziona per me: mongorestore -d some_other_db -c some_or_other_collection dump / some_database / some_collection.bson
Aviko

88

In realtà, c'è un comando per spostare una raccolta da un database a un altro. Non si chiama semplicemente "sposta" o "copia".

Per copiare una raccolta, puoi clonarla sullo stesso db, quindi spostare il clone.

Per clonare:

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

Spostare:

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

Le altre risposte sono migliori per copiare la raccolta, ma ciò è particolarmente utile se stai cercando di spostarla.


3
Grazie funziona alla grande! Ho solo bisogno di un apostrofo di chiusura in'db1.source_collection'
andrrs

4
Invece di "usa admin" seguito da "db.runCommand (..." Puoi eseguire un solo comando, "db.adminCommand (..."
Hamid

25

Vorrei abusare della funzione di connessione in mongo cli mongo doc . quindi ciò significa che è possibile avviare una o più connessioni. se si desidera copiare la raccolta clienti da test a test2 nello stesso server. per prima cosa inizi a mongo shell

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

fare una ricerca normale e copiare i primi 20 record in test2.

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

o filtrare in base ad alcuni criteri

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

basta cambiare localhost in IP o nome host per connettersi al server remoto. Lo uso per copiare i dati di test in un database di test per i test.


4
Come ho commentato il suggerimento di Jason, tieni presente che se copi nella shell JS i documenti BSON vengono decodificati in JSON durante il processo, quindi alcuni documenti potrebbero subire cambiamenti di tipo. Esistono considerazioni simili a Limitazioni di eval e questo sarà un processo più lento per la copia di quantità significative di dati tra database (in particolare sullo stesso server). Quindi mongodump / mongorestore FTW :).
Stennie,

19

Se tra due istanze mongod remote, utilizzare

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

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


Il copyIndexescampo delle opzioni in realtà non è rispettato. Gli indici vengono sempre copiati. Vedi SERVER-11418
Gianfranco P.

6
Riempilo in db.runCommand () cioè db.runCommand ({cloneCollection: "<collection>", da: "<hostname>", query: {<query>}})
Daniel de Zwaan,

Come può essere utilizzato per aggiornamenti incrementali da un mongo remoto a un altro?
nishant,

Ho i dati degli utenti aggiunti a un'istanza di mongo durante il giorno. Alla fine del giorno ho bisogno di trasferire le righe appena aggiunte in un'altra istanza di mongo. Come si può ottenere questo?
nishant,

@NishantKumar prova a impostare nella query: {} questo codice: $ dove: function () {today = new Date (); // today.setHours (0,0,0,0); return (this._id.getTimestamp ()> = oggi). Vedi stackoverflow.com/questions/42456375/… .
es Colonia,

18

Di solito farei:

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

11

per raccolte di grandi dimensioni, puoi usare Bulk.insert ()

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

Ciò farà risparmiare molto tempo . Nel mio caso, sto copiando la raccolta con 1219 documenti: iter vs Bulk (67 secondi contro 3 secondi)


questo è molto meglio, più efficiente, martella meno il db, funziona per qualsiasi dimensione del set di dati.
Jeremie,

Se lo stai facendo con più di 300.000 record, potrebbe essere necessario aggiungere un .limit (300000) dopo la ricerca e prima della ricerca. Altrimenti il ​​sistema potrebbe bloccarsi. Di solito limito le modifiche in blocco a circa 100k per sicurezza. Avvolgendo il tutto in un ciclo for basato su conteggio e limite.
triunenatura,

6

È possibile utilizzare il framework di aggregazione per risolvere il problema

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

Va notato che gli indici di oldCollection non verranno copiati in newCollection.


5

So che a questa domanda è stata data una risposta, ma personalmente non risponderei @JasonMcCays a causa del fatto che il flusso di cursori e questo potrebbe causare un ciclo di cursore infinito se la raccolta è ancora in uso. Invece vorrei usare uno snapshot ():

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

Anche la risposta @bens è buona e funziona bene per i backup a caldo delle raccolte non solo, ma mongorestore non ha bisogno di condividere lo stesso mongod.


5

Questo potrebbe essere solo un caso speciale, ma per una raccolta di 100.000 documenti con due campi di stringa casuali (la lunghezza è di 15-20 caratteri), l'utilizzo di un mapreduce muto è quasi due volte più veloce di find-insert / copy.

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

5

Usando pymongo, devi avere entrambi i database sullo stesso mongod, ho fatto quanto segue:


db = database originale
db2 = database da copiare

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

1
ciò richiederebbe molto tempo se la dimensione dei dati è enorme. In alternativa puoi usare bulk_insert
nishant il

1
Sì, questo è stato solo un modo rapido e sporco che ho trovato funzionare per me, il mio database non era troppo grande, ma neanche piccolo e non ha impiegato troppo tempo, ma sì, hai ragione.
Vbhakta,

2

Questo non risolverà il tuo problema ma la shell mongodb ha un copyTometodo che copia una collezione in un'altra nello stesso database :

db.mycoll.copyTo('my_other_collection');

Si traduce anche da BSON a JSON, quindi mongodump/ mongorestoresono il modo migliore per andare, come altri hanno già detto.


Eccellente. Purtroppo il riferimento alla shell Mongo non sembra menzionare questo metodo.
pgl,

Sì, lo so, ma la shell MongoDB è fantastica, se digiti db.collname. [TAB] vedrai tutti i metodi disponibili sull'oggetto collection. questo suggerimento funziona per tutti gli altri oggetti.
Roberto,

Il problema è la mancanza di aiuto per quei comandi! È utile essere in grado di vedere il codice, anche se omettendo le parentesi a una chiamata del metodo.
pgl,

2
Purtroppo, questo comando è ora obsoleto dalla versione 3.0.
Harry

2

Se la RAM non è un problema, l'utilizzo insertManyè molto più veloce del 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

Nel caso in cui alcuni utenti di heroku si imbattessero qui e come me volessero copiare alcuni dati dal database di staging al database di produzione o viceversa ecco come lo fai in modo molto conveniente (NB Spero che non ci siano errori di battitura lì dentro, non riesco a controllarli., Proverò a confermare la validità del codice al più presto):

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

Puoi sempre usare Robomongo. A partire dalla v0.8.3 esiste uno strumento che può farlo facendo clic con il tasto destro del mouse sulla raccolta e selezionando "Copia raccolta nel database"

Per i dettagli, consultare http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Questa funzione è stata rimossa in 0.8.5 a causa della sua natura buggy, quindi dovrai usare 0.8.3 o 0.8.4 se vuoi provarlo.


6
Questa funzione di Robomongo è ancora instabile. È una probabilità 50/50 di farlo funzionare.
thedp

2
Questo sembra essere stato rimosso dalla 0.8.5
Carasel

0

Nel mio caso, ho dovuto utilizzare un sottoinsieme di attributi della vecchia raccolta nella mia nuova raccolta. Così ho finito per scegliere quegli attributi mentre chiamavo insert sulla nuova collezione.

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.