Como copiar uma coleção de um banco de dados para outro no MongoDB


221

Existe uma maneira simples de fazer isso?


40
A resposta aceita foi sem dúvida o melhor método em 2012, mas agora o db.cloneCollection () geralmente é uma solução melhor. Existem algumas respostas mais recentes aqui que se referem a isso. Portanto, se você veio do Google (como eu), dê uma olhada em todas as respostas!
Kelvin

4
Certifique-se de ler as outras respostas bem que para se certificar de que se adapta às suas necessidades, não apenas @kelvin 's em seu / sua situação
PW Kad

Respostas:


206

No momento, não há nenhum comando no MongoDB que faça isso. Observe o ticket JIRA com a solicitação de recurso relacionada .

Você poderia fazer algo como:

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

Observe que, com isso, os dois bancos de dados precisariam compartilhar o mesmo mongod para que isso funcionasse.

Além disso, você pode fazer um mongodump de uma coleção de um banco de dados e depois armazenar a coleção no outro banco de dados.


13
Observe que, se você copiar no shell JS, os documentos BSON serão decodificados para JSON durante o processo, portanto, alguns documentos poderão sofrer alterações de tipo. mongodump / mongorestore geralmente são a melhor abordagem.
217 Stennie

1
Acordado. Essa foi mais uma sugestão divertida de brincar com a concha. Além disso, não traria os índices. Se eu estivesse fazendo isso, faria o mongodump / mongorestore toda vez.
Jason McCay

2
Obrigado. Observe que você tem um erro de digitação no código, não fechando a função getSiblingDB. Aqui está o código corrigido: db. <Nome da coleção> .find (). ForEach (function (d) {db.getSiblingDB ('<new_database>') ['<nome da coleção>']. Inserção (d);});
Flaviu

1
isso funcionou bem para redefinir um mongodb de teste de uma cópia dourada entre as execuções de teste. em vez de codificar os nomes da coleção, você pode executar um loop for sobre todos os nomes da coleção que deseja copiar com db.getCollection (name) .find (). forEach e forneça uma função que tenha db.getSiblingDB ("otherdb"). getCollection (nome). inserção (d).
simbo1905

2
isso é eficiente para coleções de tamanho enorme?
Khalil Awada

284

A melhor maneira é fazer um mongodump e depois o mongorestore.

Você pode selecionar a coleção via:

mongodump -d some_database -c some_collection

[Opcionalmente, zip o dump ( zip some_database.zip some_database/* -r) e em scpoutro lugar]

Em seguida, restaure-o:

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

Os dados existentes some_or_other_collectionserão preservados. Dessa forma, você pode "anexar" uma coleção de um banco de dados para outro.

Antes da versão 2.4.3, você também precisará adicionar novamente seus índices depois de copiar seus dados. A partir do 2.4.3, esse processo é automático e você pode desativá-lo com --noIndexRestore.


Parece que o mongodump não funciona se você tiver uma instância de mongo protegida por senha (e você deve!) #
Luciano Camilo

3
Funciona em bancos de dados protegidos por PW, você só precisa passar a autenticação nos parâmetros #
Ben Ben

2
Isto é muito mais rápido do que find / forEach / inserção, no meu caso 2 minutos vs 2 horas
Juraj Paulo

Passe o nome de usuário para o banco de dados com --username, mas não --password para obter um prompt para a senha. É melhor não colocar a senha no seu linha de comando (que termina salvando-o em .bash_history ou similar)
Chanoch

Menor: Encontrei o arquivo na subpasta nomeada por some_database, portanto isso funciona para mim: mongorestore -d some_other_db -c some_or_other_collection dump / some_database / some_collection.bson
Aviko

88

Na verdade, não é um comando para mover uma coleção de um banco para outro. Apenas não é chamado de "mover" ou "copiar".

Para copiar uma coleção, você pode cloná-la no mesmo banco de dados e depois movê-lo.

Clonar:

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

Mover:

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

As outras respostas são melhores para copiar a coleção, mas isso é especialmente útil se você deseja movê-la.


3
Thx funciona muito bem! Só precisa de um apóstrofo final em'db1.source_collection'
andrrs

4
Em vez de "use admin" seguido por "db.runCommand (..." Você pode executar apenas um comando ", db.adminCommand (...")
Hamid:

25

Eu abusaria da função connect no mongo cli mongo doc . então isso significa que você pode iniciar uma ou mais conexões. se você deseja copiar a coleção de clientes de teste para test2 no mesmo servidor. primeiro você começa mongo shell

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

faça uma localização normal e copie o primeiro registro 20 para test2.

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

ou filtrar por alguns critérios

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

basta alterar o host local para IP ou nome do host para conectar-se ao servidor remoto. Eu uso isso para copiar dados de teste em um banco de dados de teste para teste.


4
Como eu comentei a sugestão de Jason, esteja ciente de que, se você copiar no shell JS, os documentos BSON serão decodificados para JSON durante o processo, portanto, alguns documentos poderão sofrer alterações de tipo. Há considerações semelhantes às Limitações de avaliação e este será um processo mais lento para copiar quantidades significativas de dados entre bancos de dados (particularmente no mesmo servidor). Então mongodump / mongorestore FTW :).
31512 Stennie

19

Se entre duas instâncias remotas do mongod, use

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

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


O copyIndexescampo de opção, na verdade, não é respeitado. Os índices são sempre copiados. Veja SERVER-11418
Gianfranco P.

6
Envoltório que em db.runCommand () ou seja db.runCommand ({cloneCollection: "<collection>", a partir de: "<hostname>", consulta: {<query>}})
Daniel de Zwaan

Como isso pode ser usado para atualizações incrementais de um mongo remoto para outro?
Nishant 18/01/19

Eu tenho dados de usuário sendo adicionados a uma instância do mongo ao longo do dia. No final do dia, preciso transferir as linhas recém-adicionadas para outra instância do mongo. Como isso pode ser alcançado?
Nishant

@NishantKumar tenta definir na consulta: {} este código: $ where: function () {today = new Date (); // today.setHours (0,0,0,0); return (this._id.getTimestamp ()> = hoje). Consulte stackoverflow.com/questions/42456375/… .
es cologne

18

Eu normalmente faria:

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

11

para coleções de tamanho enorme, você pode usar Bulk.insert ()

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

Isso economizará muito tempo . No meu caso, estou copiando a coleção com 1219 documentos: iter vs Bulk (67 segundos vs 3 segundos)


isso é muito melhor, mais eficiente, reduz menos o banco de dados, funciona para qualquer tamanho de conjunto de dados.
Jeremie

Se você estiver fazendo isso com mais de 300 mil registros, talvez seja necessário adicionar um .limit (300000) após a localização e antes do foreach. Caso contrário, o sistema pode travar. Normalmente, limite as alterações em massa a cerca de 100 mil por segurança. Embrulhando a coisa inteira em um loop for com base na contagem e no limite.
Triunenature

6

Você pode usar a estrutura de agregação para resolver seu problema

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

Deve-se observar que os índices de oldCollection não serão copiados em newCollection.


5

Eu sei que essa pergunta foi respondida, mas eu pessoalmente não faria a resposta @JasonMcCays devido ao fato de que os cursores fluem e isso pode causar um loop infinito do cursor se a coleção ainda estiver sendo usada. Em vez disso, eu usaria um snapshot ():

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

A resposta do @bens também é boa e funciona bem para backups quentes de coleções, não apenas isso, mas o mongorestore não precisa compartilhar o mesmo mongod.


5

Pode ser apenas um caso especial, mas para uma coleção de 100k documentos com dois campos aleatórios (o comprimento é de 15 a 20 caracteres), o uso de um mapreduce idiota é quase duas vezes mais rápido que o find-insert / copyTo:

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

5

Usando o pymongo, você precisa ter os dois bancos de dados no mesmo mongod, fiz o seguinte:


db = banco de dados original
db2 = banco de dados a ser copiado para

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

1
isso levaria muito tempo se o tamanho dos dados fosse enorme. Como alternativa, você pode usar bulk_insert
nishant

1
Sim, essa foi apenas uma maneira rápida e suja que encontrei para trabalhar para mim, meu banco de dados não era muito grande, mas também não era pequeno e não demorou muito, mas sim, você está correto.
vbhakta

2

Isso não resolverá o seu problema, mas o shell mongodb possui um copyTométodo que copia uma coleção para outra no mesmo banco de dados :

db.mycoll.copyTo('my_other_collection');

Também traduz de BSON para JSON, então mongodump/ mongorestoresão o melhor caminho a percorrer, como outros já disseram.


Excelente. Infelizmente, a referência do shell Mongo não parece mencionar esse método.
Pg 23/09

Sim, eu sei, mas o shell do MongoDB é incrível, se você digitar db.collname. [TAB] você verá todos os métodos disponíveis no objeto de coleção. essa dica funciona para todos os outros objetos.
Roberto

O problema é a falta de ajuda para esses comandos! É útil poder ver o código, embora omitindo os parênteses em uma chamada de método.
PGL

2
Infelizmente, este comando foi descontinuado desde a versão 3.0.
Harry

2

Se a RAM não é um problema, o uso insertManyé muito mais rápido que o 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

No caso de alguns usuários do heroku tropeçarem aqui e como eu quererem copiar alguns dados do banco de dados de teste para o banco de dados de produção ou vice-versa, veja como você o faz de maneira muito conveniente (NB: espero que não haja erros de digitação, não é possível verificar isso., Vou tentar confirmar a validade do código o mais rápido possível):

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

Você sempre pode usar o Robomongo. A partir da v0.8.3, existe uma ferramenta que pode fazer isso clicando com o botão direito do mouse na coleção e selecionando "Copiar Coleção para o Banco de Dados"

Para detalhes, consulte http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Esse recurso foi removido no 0.8.5 devido à sua natureza de buggy, portanto você precisará usar o 0.8.3 ou 0.8.4 se quiser experimentá-lo.


6
Esse recurso do Robomongo ainda é instável. É uma chance de 50/50 de fazê-lo funcionar.
thedp 23/07

2
Isso parece ter sido removido do 0.8.5
Carasel 7/15

0

No meu caso, eu tive que usar um subconjunto de atributos da coleção antiga em minha nova coleção. Então, acabei escolhendo esses atributos enquanto chamava insert na nova coleção.

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


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.