35

My approach would be to get the document instance, and create a new one from the instance fields. I am sure there is a better way to do it.

fusio
  • 3,595
  • 6
  • 33
  • 47

9 Answers9

92

You need to reset d1.isNew = true; as in:

Model.findById(yourid).exec(
    function(err, doc) {
        doc._id = new mongoose.Types.ObjectId();
        doc.isNew = true; //<--------------------IMPORTANT
        doc.save(callback);
    }
);

                
                                                                                                                                                                                                                        
F.H.
  • 1,456
  • 1
  • 20
  • 34
jlchereau
  • 1,152
  • 1
  • 7
  • 5
16

Can you clarify what you mean by "copy/clone"? Are you going trying to create a duplicate document in the database? Or are you just trying to have two vars in your program that have duplicate data?

If you just do:

Model.findById(yourid).exec(
    function(err, doc) { 
        var x = doc; 
        Model.findById(yourid).exec(
            function(err, doc2) {
                var y = doc2;
                // right now, x.name and y.name are the same
                x.name = "name_x";
                y.name = "name_y";
                console.log(x.name); // prints "name_x"
                console.log(y.name); // prints "name_y"
            }); 
    });

In this case, x and y will be two "copies" of the same document within your program.

Alternatively, if you wanted to insert a new copy of the doc into the database (though with a different _id I assume), that would look like this:

Model.findById(yourid).exec(
    function(err, doc) {
        var d1 = doc;
        d1._id = /* set a new _id here */;
        d1.isNew = true;
        d1.save(callback);
    }
);

Or if you're doing this from the outset, aka you created some document d1, you can just call save twice without setting the _id:

var d1 = new Model({ name: "John Doe", age: 54 });
d1.save(callback);
d1.save(callback);

There will now be two documents with differing _id's and all other fields identical in your database.

Does this clarify things a bit?

Liam
  • 27,717
  • 28
  • 128
  • 190
Amalia
  • 896
  • 7
  • 18
  • 4
    Yes, indeed, I ended up getting the `doc`, removing `_id` and saving :) – fusio Sep 19 '13 at 17:53
  • How did you remove the _id? – JoeTidee Jan 01 '17 at 20:12
  • 2
    @JoeTidee You can use `delete doc._id` will remove the `_id` property from the `doc` object! – Will Brickner Feb 10 '17 at 06:19
  • 4
    This does not answer the question unfortunately. To truly clone a document, you would do: `new Model(doc.toObject())`. – losnir Aug 09 '17 at 14:07
  • What are the approaches for copying the entire collection and create it with new name Through Mongoose? As I know how to clone/Copy the collection through cmd, But this things not work on mongoose. Any help is appreciated – Iron_Man Jan 05 '18 at 12:33
  • sorry but this answer is bad, you just can do: var doc1= doc.toJSON(); var doc2= doc.toJSON(); and you have 2 objects with same data, no query. – Ninja Coding Sep 05 '18 at 00:51
  • Great answer but, this does not work as expected. You cannot delete an _id on a Mongoose object. Second, the id is created on the local object, so saving twice does not create two identical objects. It just saves the same document twice. Even if you create a new id. This might be a version thing as this answer is from 2013. Maybe it used to work. – David J Mar 01 '21 at 14:33
10

My two cents:

const doc = await DocModel.findById(id);
let obj = doc.toObject();
delete obj._id;
const docClone = new DocModel(obj);
await docClone.save();
Mo D Genesis
  • 5,187
  • 1
  • 21
  • 32
Caina Santos
  • 919
  • 1
  • 9
  • 16
3

So, a lot of these answers will work well for simple docs, but there could be an error case when you're trying to make a deep clone of complex documents.

If you have arrays of subdocs, for example, you can end up with duplicate _ids in your copied document, which can cause subtle bugs later.

To do a deep clone of a mongoose doc, I suggest trying something like:

//recursively remove _id fields
function cleanId(obj) {
    if (Array.isArray(obj))
        obj.forEach(cleanId);
    else {
        delete obj['_id'];
        for (let key in obj)
            if (typeof obj[key] == 'object')
                cleanId(obj[key]);
    }
}

let some_doc = await SomeModel.findOne({_id: some_id});
let new_doc_object = cleanId(some_doc.toObject());
let new_doc = new SomeModel(new_doc_object);
await new_doc.save();

This is going to be a pretty safe approach, and will ensure that every part of your object is cloned properly with newly generated _id fields on save.

Clayton Gulick
  • 9,755
  • 2
  • 36
  • 26
2

The following code to clone documents:

Model.findById(yourid).exec(
        function(err, doc) {
            var newdoc = new Model(doc);
            newdoc._id = mongoose.Types.ObjectId();
            newdoc.save(callback);
        }
    );
Trevor
  • 11,269
  • 2
  • 33
  • 40
Asad Fida
  • 216
  • 2
  • 6
1

For simply clone use this :

Context.findOne({
    _id: context._id
})
    .then(function(c) {
        c._id = undefined;
        c.name = context.name;
        c.address = context.address;
        c.created = Date.now();
        return Context.create(c.toObject());
    }).then(function(c) {
        return res.json({
            success: true,
            context: context
        });
    }).catch(function(err) {
        next(err, req, res);
    });
Espinasse
  • 21
  • 3
  • This might be more simply stated as you just have to copy object properties. I've not found any of the approaches to work. – David J Mar 01 '21 at 14:36
1
const cloneDoc = (doc, model)=>{
  const copyDoc = new Model({
    ...doc.toObject(),
    _id: undefined,
  });
  copyDoc.isNew = true;
  return copyDoc;
}
Ihor
  • 461
  • 4
  • 8
0

To copy a document into the same collection or different collection first get (query) the data and make a copy of the data. Afterwards remove the _id from the new list as you can't from the current data. This will allow you to insert a new record with new _id assigned from mongodb

change searchBy to what you are trying to find the document by. change collectionA and collectionB to the name of the collection to create you copy to. Currently we are searching in collectionA and copying the data in collectionB

collectionA.find(searchBy).exec(function (err, doc) {
      // create a new copy
      let newDoc = { ...doc[0] }._doc;

      // delete property from new copy (remove _id).
      delete newDoc._id;

      // insert copy into db
      var newdoc = new collectionB(newDoc);
      newdoc.save();
    });
Jerry Seigle
  • 233
  • 4
  • 15
-1

You can basically use .clone() to get a copy.

const cars = Cars.find();
const carsCopy = cars.clone();

await cars;
await carsCopy;

https://mongoosejs.com/docs/api.html#schema_Schema-clone