Skip to content
Monghoul

Copy a collection to another database

Same server: $out, which does not bring the source indexes with it. Another server: mongodump piped into mongorestore. Or copy it from the app.

Needs Pro, or the 14-day trial Checked against v1.14.0 Updated
The Copy Collection dialog copying Production / shop / orders into Staging / shop / orders, with a write mode of insert and a copy-indexes option
Copy · Production to Staging, with a write mode and a copy-indexes option, and no file between Pro

Where the two databases live decides the method. On one server an aggregation does it. Across servers you need the database tools or a client that holds both connections open. The old db.collection.copyTo() and cloneCollection are gone, which is why so many answers to this question no longer run.

On the same server: $out

db.getSiblingDB('shop').orders.aggregate([
  { $match: {} },
  { $out: { db: 'shop_archive', coll: 'orders' } }
])

$out takes a database name as well as a collection name, so the copy can cross databases without leaving the server. Three things about it are worth knowing before you run it on anything you care about:

  • It does not bring the source collection’s indexes with it. This is the one to read twice, because MongoDB’s wording invites the opposite reading. $out builds a temporary collection, copies the index definitions “from the existing collection”, inserts the documents, then renames the temporary collection over the destination. The existing collection there is the destination being replaced, not the source you are reading. So replacing a collection keeps that collection’s own indexes, and writing to a destination that does not exist yet gives you a collection with nothing but its _id index. Create the rest yourself afterwards.
  • It replaces the destination, atomically. If the target collection exists, it is dropped by the rename at the end. There is no merge and no upsert. If you want those, the stage is $merge.
  • It will not write to a sharded collection, cannot run inside a transaction, and cannot appear in a view definition or inside $lookup, $facet or $unionWith. A sharded destination is $merge again.

The $match: {} is there to make the shape obvious. Put a real filter in it and you have copied a subset.

Across servers: mongodump into mongorestore

mongodump --uri="mongodb://source:27017" \
  --db=shop --collection=orders --archive |
mongorestore --uri="mongodb://target:27017" \
  --nsFrom='shop.orders' --nsTo='shop_archive.orders' --archive

--archive writes one binary stream instead of a directory of BSON files, and with no filename it writes to standard output, so the two tools pipe into each other and nothing touches your disk. --nsFrom and --nsTo rename the namespace on the way in, which is what makes this a copy rather than a restore of the same name.

This route carries the index definitions and the collection options, and mongorestore rebuilds the indexes at the end unless you pass --noIndexRestore.

One caveat that catches people on a live system. A dump taken while writes are happening does not represent a single moment in time, and MongoDB’s documentation says so plainly.

The usual answer to that is --oplog, and it does not apply here: --oplog requires a full dump of a replica set member and fails outright if you pass --db, --collection, --query or --dumpDbUsersAndRoles. The copy above uses two of those. So for one collection off a busy system your options are to stop writing to it, to accept that the copy is smeared across the time the dump took, or to reconcile afterwards on a timestamp field.

In the app

Copying between instances is a Pro feature and it goes directly, without you exporting to a file and importing it back.

Copying lives inside the export flow, which is the part people do not guess.

  1. Right-click the source collection in the sidebar and choose Export Collection.
  2. In the dialog, set Format to Another Collection rather than a file format.
  3. Under Target, pick the Connection, the Database and the Collection. The collection can be a new name.
  4. Under Copy Options, set Write Mode: Insert, Upsert (matches on _id, and keeps a field the target has and the source lacks) or Insert only (skips documents the target already holds). Tick Copy indexes to carry the other indexes across.
  5. Click Start Copy. The dialog title reads Copy Collection.

Free shows Another Collection (PRO) in the list, and choosing it does nothing.

You can also drag a collection or a database onto a destination in the tree, which opens the same dialog with that destination already chosen. Only a destination the copy could actually use lights up: another connection, or another database on the same one. A copy onto the same connection, database and collection name is refused.

The copy builds a missing target with the source’s validator, capped settings, collation and TTL. A target that already exists keeps its own options. An empty source still creates the target. A view is copied as the documents it returns, into an ordinary collection.

When the copy ends, it reports how many documents landed and how many the target skipped. Open the target and compare the document counts. If you cancel, the documents already written stay in the target, and the copy does not roll them back.

Duplicating a database

Right-click the database and choose Export Database (Pro). Set Export Mode to Copy to Another Database, pick the collections, then set the target Connection and Database Name. The name starts as the source name with _copy added. The copy recreates views as views and builds each collection’s indexes after its documents. It leaves out the system.* collections.

What write protection does here

If the target connection, database or collection is write protected, a Copy Into a Protected Collection dialog asks you to confirm with Copy Anyway. This is the same confirmation any other destructive operation asks for. Copying out of a protected connection writes nothing to it, so the source is never questioned.

That matters because the direction is easy to get backwards, and the confirmation names the target. Read it.

Which one to use

Situation Use
Both databases on one server $out, or $merge if the destination must survive. Recreate the indexes either way
Two servers, both reachable from your machine mongodump piped into mongorestore, or the app
Two servers, not reachable at the same time Export to a file, move it, import it
You need to transform documents on the way An aggregation ending in $out or $merge
The destination is sharded $merge

When to use export and import instead

  • The two clusters cannot both be reached from your machine at the same time.
  • You want the intermediate file, for a backup or for a colleague.
  • You need to transform the documents on the way through.

For those, export to a file and import it on the other side. Use Extended JSON, not CSV, or the types will not survive.

Next