113

I want to copy a collection within the same database and give it a different name - basically take a snapshot.

What's the best way to do this? Is there a command, or do I have to copy each record in turn?

I'm aware of the cloneCollection command, but it seems to be for copying to another server only.

I'm also aware of mongoimport and mongoexport, but as I'm doing this via PHP I'd prefer not to make calls out to the shell.

Tim
  • 8,036
  • 2
  • 36
  • 52
  • 1
    The correct answer is the one with 230 upvotes. – Daniel Viglione Aug 21 '20 at 21:59
  • 2
    `db.myoriginal.aggregate([ { $match: {} }, { $out: "mycopy" } ])` This is the faster than accepted answer. – Milan Mar 10 '21 at 10:59
  • Good to know, thanks. I am unable to unaccept the answer, but it may have been correct in 2012. $out requires v2.6 of MongoDB which was not released until 2014. – Tim Mar 10 '21 at 14:30

10 Answers10

313
> db.myoriginal.aggregate([{$out: "mycopy"}])

It is a lot faster than doing many inserts in a forEach loop.

Victor Schröder
  • 6,738
  • 2
  • 42
  • 45
jschildgen
  • 3,678
  • 4
  • 16
  • 13
69

You have a few options, but the fastest is:

mongodump -d db -c sourcecollection 
mongorestore -d db -c targetcollection --dir=dump/<db>/<sourcecollection.bson>

or

mongoexport -d db -c sourcecollection | mongoimport -d db -c targetcollection --drop

or in php:

`mongoexport -d db -c sourcecollection | mongoimport -d db -c targetcollection --drop`;

after that you have

mongo db < script.js

where, as shown in the mongo docs, script.js contains something like:

db.myoriginal.find().forEach( function(x){db.mycopy.insert(x)} );

The slowest (by an order of magnitude or more) way to copy a collection will be to use the native php driver - simply because of moving information around. But you could issue the above mongo query if you absolutely want to avoid cli calls using the db execute function.

carla
  • 1,970
  • 1
  • 31
  • 44
AD7six
  • 63,116
  • 12
  • 91
  • 123
  • Thanks. I am currently using your first option, but was hoping there was a command I could simply call so I don't need to call the shell from PHP. – Tim May 17 '12 at 13:04
  • I may try the script approach using http://php.net/manual/en/mongodb.execute.php – Tim May 17 '12 at 13:06
  • 1
    Using a dumb mapReduce (self-mapping, no reducing), measures between mongoexpor and script.js solutions for me... – Vajk Hermecz Jun 25 '13 at 23:39
  • 1
    Mongodump | mongorestore should be faster than export|import since it doesn't have to deserialize the binary format to json or csv. – Cody A. Ray Feb 05 '17 at 15:46
  • @CodyA.Ray it's a long time since I've used mongo, this answer was provided based on observed performance at the time (now 4+ years ago). Please update the answer if you are able to demonstrate a better method. – AD7six Feb 05 '17 at 16:56
  • 1
    How to preserve indexes during copying to targetcollection? – abdulmanov.ilmir Mar 01 '17 at 07:21
17

For versions less than 3.0, you can use copyTo() for experimental uses (never use on Production, as it blocks write operations):

db.source.copyTo("target"); 

& if "target" doesn't exist, it will be created

AbdelHady
  • 9,334
  • 8
  • 56
  • 83
4

First option: using mongodump

  1. Get a dump from source_collection

    $ mongodump -d db -c source_collection 
    
  2. Restore to target_collection collection from the dump

    $ mongorestore -d db -c target_collection dir=dump/db_name/source_collection.bson
    

Second option: $out aggregation step

  1. Run an aggregate command on source_collection

    > db.source_collection.aggregate([
        {$match: {emailAddress: "apitester@mailinator.com"}},
        {$out: "target_collection"}
      ])
    

Third option (slowest): iterate and copy all documents

  1. Run a loop through all documents of source_collection, inserting them into target_collection

    > db.source_collection.find().forEach((doc) => {
        db.target_collection.insert(doc);
      })
      && print("Copy completed!");
    
Victor Schröder
  • 6,738
  • 2
  • 42
  • 45
  • 1
    Which one of the first two options is faster? – pooya13 Jun 24 '21 at 17:17
  • 1
    The second option is the fastest, because everything happens inside MongoDB and the data never leaves the server. You don't need local disk space and won't use the network for data. The downside, it won't preserve indices. The first option is decent, but still requires the whole data to be transferred outside the server and then transmitted back. You have to account for disk space and bandwidth. The third option is very slow because it processes inserts individually and transmits each document over the network. It doesn't require local disk space, though. – Victor Schröder Apr 05 '22 at 15:54
  • Agreed with @vi – Isura Amarasinghe Apr 24 '22 at 11:23
1

Sorry for the answer, but I don't have enough rep to post a comment.

The accepted answer has a typo, it should be:

$ mongorestore -d db -c target_collection --dir=dump/db_name/source_collection.bson

note the -- before dir that isn't present in the accepted answer.

KayBeSee
  • 23
  • 1
  • 6
0

In addition to AD7six 1st solution, if you use mongoexport / import be sure about your collection data types and mongo configuration, as explained here: http://docs.mongodb.org/manual/reference/mongodb-extended-json/

kij
  • 1,421
  • 1
  • 16
  • 40
  • 1
    Please clarify/state what you wish to point out. Mongoimport and mongoexport use the same format, there's nothing to configure, and no risk of data mutating afaik. – AD7six Jun 26 '13 at 05:49
0

This is my implementation in python (pymongo):

def copy_collection(client, from_db, from_coll, to_db=None, to_coll=None):
    to_db = from_db if to_db is None else to_db
    to_coll = from_coll if to_coll is None else to_coll
    assert (to_db != from_db or to_coll != from_coll), "Copy Error: Source and destination can't be same!"
    documents = client[from_db][from_coll].find()
    client[to_db][to_coll].insert_many([d for d in documents])
Sagar Gupta
  • 1,352
  • 1
  • 12
  • 26
0

I managed to do that with this query :-

Suppose you have collection name data1 and you want to make new collection data2 with same indexes or want to delete or add some index you can do this by using this query.

data1 have indexes | FirstName | MiddleName | LastName | Age | Occupation

and you want to make new collection data2 | FirstName | MiddleName | LastName | Age | Occupation | Date

Query It took very less time in my case (speed depend on amount of data)

var cursor = db.data1.find();
var data = [];
while(cursor.hasNest()){
    var a = cursor.next();
    data.push({
        "FirstName" : a.FirstName,
        "MiddleName" : a.MiddleName,
        "LastName" : a.LastName,
        "Age" : a.Age,
        "Occupation" : a.Occupation,
        "Date" : new Date()
    });
}

db.data2.insertMany(data)

----- UPDATE -----

As this will build in memory array so for large set of data memory consumption will be high, so to solve this problem what one can do is define a flag and after some record let say 1000 you can insert the current data in collection and empty that array.

Subhash Rawat
  • 451
  • 6
  • 13
  • 1
    Yes, it saves time by accumulating documents and inserting in bulk, but this will create a brand new collection, without any indices. Also, for large collections, building an in-memory array with the documents may be too much memory footprint for the machine operating the MongoDB shell. – Victor Schröder Apr 05 '22 at 15:58
-1

The fastest way is db.collection.copyTo().

Note that it is deprecated since version 3.0.

chris
  • 2,761
  • 17
  • 24
-3

You can use the copyDatabase function in the mongo shell:

http://docs.mongodb.org/manual/tutorial/copy-databases-between-instances/

Jp Lorandi
  • 113
  • 1
  • 3
  • 4
    Note that [link-only answers are discouraged](http://meta.stackoverflow.com/tags/link-only-answers/info), SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference. – kleopatra Nov 10 '13 at 09:41