Comment copier une collection d'une base de données dans une autre dans MongoDB


221

Existe-t-il un moyen simple de procéder?


40
La réponse acceptée était sans doute la meilleure méthode en 2012, mais maintenant db.cloneCollection () est souvent une meilleure solution. Il y a quelques réponses plus récentes ici qui se réfèrent à cela, donc si vous êtes venu ici de Google (comme je l'ai fait) jetez un œil à toutes les réponses!
Kelvin

4
Assurez-vous de lire également les autres réponses pour vous assurer qu'elles correspondent à vos besoins, pas seulement à @kelvin dans sa situation
PW Kad

Réponses:


206

Pour le moment, il n'y a aucune commande dans MongoDB qui ferait cela. Veuillez noter le billet JIRA avec la demande de fonctionnalité associée .

Vous pourriez faire quelque chose comme:

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

Veuillez noter qu'avec cela, les deux bases de données devraient partager le même mongod pour que cela fonctionne.

En plus de cela, vous pouvez effectuer un mongodump d'une collection à partir d'une base de données, puis restaurer la collection dans l'autre base de données.


13
Notez que si vous copiez dans le shell JS, les documents BSON sont décodés en JSON pendant le processus, de sorte que certains documents peuvent entraîner des changements de type. mongodump / mongorestore sont généralement la meilleure approche.
Stennie

1
D'accord. C'était plus juste une suggestion amusante pour jouer avec la coquille. De plus, cela n'apporterait pas les indices. Si je faisais cela, je ferais le mongodump / mongorestore à chaque fois.
Jason McCay

2
Merci. Veuillez noter que vous avez une faute de frappe dans le code et que vous ne fermez pas la fonction getSiblingDB. Voici le code corrigé: db. <collection_name> .find (). ForEach (function (d) {db.getSiblingDB ('<new_database>') ['<collection_name>'] .insert (d);});
Flaviu

1
cela a bien fonctionné pour réinitialiser un mongodb de test à partir d'une copie dorée entre les tests. plutôt que de coder en dur les noms de collection, vous pouvez faire une boucle for sur tous les noms de collection que vous souhaitez copier avec db.getCollection (name) .find (). forEach et fournir une fonction qui a db.getSiblingDB ("otherdb"). getCollection (nom) .insert (d).
simbo1905

2
est-ce efficace pour les collections de grande taille?
Khalil Awada

284

La meilleure façon est de faire un mongodump puis mongorestore.

Vous pouvez sélectionner la collection via:

mongodump -d some_database -c some_collection

[Facultativement, zippez le dump ( zip some_database.zip some_database/* -r) et scpailleurs]

Ensuite, restaurez-le:

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

Les données existantes en some_or_other_collectionseront conservées. De cette façon, vous pouvez "ajouter" une collection d'une base de données à une autre.

Avant la version 2.4.3, vous devrez également rajouter vos index après avoir copié sur vos données. À partir de la version 2.4.3, ce processus est automatique et vous pouvez le désactiver avec --noIndexRestore.


Il semble que mongodump ne fonctionne pas si vous avez une instance de mongo protégée par mot de passe (et vous devriez!)
Luciano Camilo

3
Il fonctionne sur les bases de données protégées PW dont vous avez juste besoin de passer l'authentification dans les paramètres
Ben

2
C'est beaucoup plus rapide que find / forEach / insert, dans mon cas 2 minutes vs 2 heures
Juraj Paulo

Entrez le nom d'utilisateur de la base de données avec --username mais pas --password pour obtenir une invite pour le mot de passe. Il est préférable de ne pas mettre le mot de passe sur votre ligne de commande (en finissant par l'enregistrer dans .bash_history ou similaire)
Chanoch

Mineur: j'ai trouvé le fichier dans un sous-dossier nommé par une_database, donc cela fonctionne pour moi: mongorestore -d une_autre_db -c une_une_autre_collection dump / une_database / une_collection.bson
Aviko

88

En fait, il existe une commande pour déplacer une collection d'une base de données vers une autre. Ce n'est pas appelé "déplacer" ou "copier".

Pour copier une collection, vous pouvez la cloner sur la même base de données, puis déplacer le clone.

Cloner:

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

Bouger:

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

Les autres réponses sont meilleures pour copier la collection, mais cela est particulièrement utile si vous cherchez à la déplacer.


3
Thx fonctionne très bien! A juste besoin d'une apostrophe de clôture dans'db1.source_collection'
andrrs

4
Au lieu de "utiliser admin" suivi de "db.runCommand (..." Vous ne pouvez faire qu'une seule commande, "db.adminCommand (..."
Hamid

25

J'abuserais de la fonction de connexion dans mongo cli mongo doc . cela signifie que vous pouvez démarrer une ou plusieurs connexions. si vous souhaitez copier la collection de clients de test vers test2 sur le même serveur. vous commencez d'abord mongo shell

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

faire une recherche normale et copier les 20 premiers enregistrements dans test2.

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

ou filtrer selon certains critères

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

changez simplement l'hôte local en IP ou nom d'hôte pour vous connecter au serveur distant. Je l'utilise pour copier les données de test dans une base de données de test pour les tests.


4
Comme j'ai commenté la suggestion de Jason, sachez que si vous copiez dans le shell JS, les documents BSON sont décodés en JSON pendant le processus, de sorte que certains documents peuvent entraîner des changements de type. Il existe des considérations similaires aux limitations de l'évaluation et cela va être un processus plus lent pour copier des quantités importantes de données entre les bases de données (en particulier sur le même serveur). Donc mongodump / mongorestore FTW :).
Stennie

19

Si entre deux instances de mongod distantes, utilisez

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

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


Le copyIndexeschamp d'option n'est en fait pas respecté. Les index sont toujours copiés. Voir SERVER-11418
Gianfranco P.

6
Enveloppez cela dans db.runCommand () ie db.runCommand ({cloneCollection: "<collection>", from: "<hostname>", query: {<query>}})
Daniel de Zwaan

Comment cela peut-il être utilisé pour les mises à jour incrémentielles d'un mongo distant à un autre?
nishant

J'ai des données utilisateur ajoutées à une instance de mongo tout au long de la journée. À la fin de la journée, je dois transférer les lignes nouvellement ajoutées vers une autre instance de mongo. Comment cela peut il etre accompli?
2018

@NishantKumar essaie de définir dans la requête: {} ce code: $ where: function () {today = new Date (); // today.setHours (0,0,0,0); return (this._id.getTimestamp ()> = today). Voir stackoverflow.com/questions/42456375/… .
es cologne

18

Je fais habituellement:

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

11

pour les collections de grande taille, vous pouvez utiliser Bulk.insert ()

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

Cela vous fera gagner beaucoup de temps . Dans mon cas, je copie une collection avec 1219 documents: iter vs Bulk (67 secs vs 3 secs)


c'est bien mieux, plus efficace, martèle moins la base de données, fonctionne pour n'importe quelle taille de jeu de données.
Jeremie

Si vous faites cela avec plus de 300k enregistrements, vous devrez peut-être ajouter un .limit (300000) après la recherche et avant la foreach. Sinon, le système peut se bloquer. Je limite généralement les modifications en masse à environ 100k pour des raisons de sécurité. Envelopper le tout dans une boucle for basée sur le nombre et la limite.
triunenature

6

Vous pouvez utiliser le cadre d'agrégation pour résoudre votre problème

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

Il convient de noter que les index de oldCollection ne seront pas copiés dans newCollection.


5

Je sais que cette question a été répondue, mais personnellement, je ne ferais pas de réponse @JasonMcCays en raison du fait que les curseurs diffusent et cela pourrait provoquer une boucle de curseur infinie si la collection est toujours utilisée. Au lieu de cela, j'utiliserais un instantané ():

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

@bens est également une bonne réponse et fonctionne bien pour les sauvegardes à chaud des collections non seulement cela, mais mongorestore n'a pas besoin de partager le même mongod.


5

Ce n'est peut-être qu'un cas spécial, mais pour une collection de documents de 100 000 avec deux champs de chaîne aléatoires (la longueur est de 15 à 20 caractères), l'utilisation d'un mapreduce stupide est presque deux fois plus rapide que find-insert / copy

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

5

En utilisant pymongo, vous devez avoir les deux bases de données sur le même mongod, j'ai fait ce qui suit:


db = base de données d'origine
db2 = base de données à copier

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

1
cela prendrait beaucoup de temps si la taille des données est énorme. Alternativement, vous pouvez utiliser bulk_insert
nishant

1
Oui, c'était juste un moyen rapide et sale de travailler pour moi, ma base de données n'était pas trop grande, mais pas petite non plus et ne prenait pas trop de temps, mais oui, vous avez raison.
vbhakta

2

Cela ne résoudra pas votre problème mais le shell mongodb a une copyTométhode qui copie une collection dans une autre dans la même base de données :

db.mycoll.copyTo('my_other_collection');

Il se traduit également de BSON en JSON, donc mongodump/ mongorestoresont la meilleure façon de procéder, comme d'autres l'ont dit.


Excellent. Malheureusement, la référence au shell Mongo ne semble pas mentionner cette méthode.
pgl

Oui, je sais, mais le shell MongoDB est génial, si vous tapez db.collname. [TAB] vous verrez toutes les méthodes disponibles sur l'objet de collection. cette astuce fonctionne pour tous les autres objets.
Roberto

Le problème est le manque d'aide pour ces commandes! Il est utile de pouvoir voir le code, bien qu'en omettant les parens à un appel de méthode.
pgl

2
Malheureusement, cette commande est désormais obsolète depuis la version 3.0.
Harry

2

Si la RAM n'est pas un problème, l'utilisation insertManyest beaucoup plus rapide que la forEachboucle.

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

Au cas où certains utilisateurs de heroku trébucheraient ici et, comme moi, voudraient copier des données de la base de données intermédiaire dans la base de données de production ou vice versa, voici comment vous le faites très facilement (NB j'espère qu'il n'y a pas de fautes de frappe là-dedans, je ne peux pas le vérifier atm., Je vais essayer de confirmer la validité du code dès que possible):

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

Vous pouvez toujours utiliser Robomongo. Depuis la v0.8.3, il existe un outil qui peut le faire en cliquant avec le bouton droit sur la collection et en sélectionnant "Copier la collection dans la base de données"

Pour plus de détails, voir http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Cette fonctionnalité a été supprimée en 0.8.5 en raison de sa nature de buggy, vous devrez donc utiliser 0.8.3 ou 0.8.4 si vous voulez l'essayer.


6
Cette fonctionnalité de Robomongo est toujours instable. C'est une chance à 50/50 de le faire fonctionner.
thedp

2
Cela semble avoir été supprimé de 0.8.5
Carasel

0

Dans mon cas, j'ai dû utiliser un sous-ensemble d'attributs de l'ancienne collection dans ma nouvelle collection. J'ai donc fini par choisir ces attributs lors de l'appel de insert sur la nouvelle collection.

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


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.