1

How can I move one existing document from one collection to another?

Basically, what I have, is a customer Schema:

const customerSchema = new mongoose.Schema ({
    firstname: String,
    lastname: String,
    email: String,
});

and the Model for it:

const Customer = new mongoose.model('Customer', customerSchema);

Now I want to move one document to an archive collection. So I thought, I create a new model, like so:

const CustomerArchive = new mongoose.model('CustomerArchive', customerSchema);

Then, when someone clicks on a Button with the formaction: "/archive-data", the dataset needs to be moved from the Customer collection to the CustomerArchive collection.

  {
    _id: new ObjectId("6325ba96f228119e479963ca"),
    firstname: 'Testuser',
    lastname: 'Testuser',
    email: 'test@user',
    __v: 0
  }

Is there a way to move that one document to another collection without defining each attribute again?

app.post("/archive-data", (req, res) => {
    const docId = req.body.archiveButton;

    Customer.find({ _id: docId }, (err, document) => {
        CustomerArchive.create([document])
    })

})

This is what I have so far, it does work, but there has to be better way - even when it comes to unique IDs or duplicates:

app.post("/archive-data", (req, res) => {
    const docId = req.body.archiveButton;

    Customer.find({ _id: docId}, (err, document) => {
        CustomerArchive.create({
            _id: document[0]._id,
            firstname: document[0].firstname,
            lastname: document[0].lastname,
            email: document[0].email
        });
        Customer.findByIdAndRemove(docId, (err) => {
            if (!err) {
                res.redirect("/customers");
            } else {
                console.log(err);
            }
        });
    })
});

When doing this another problem might arise. Let's assume I move a document from Customer collection to an archive collection. But what, if someone decides to move the archived document back to the Customer collection - the chances that the id already exists might be very low, but within a huge customer db it might happen.

Fiehra
  • 659
  • 6
  • 23
VebDav
  • 152
  • 6
  • considering your 2nd question about ids across collections. I have found this. https://stackoverflow.com/questions/5303869/mongodb-are-mongo-ids-unique-across-collections so ids are unique only to their collection. – Fiehra Sep 18 '22 at 11:13
  • Why do you want to keep the _id the same as before? couldnt you just insert a new document with a normal Document.save() and let it assign a new unique id but with the old values for name, email etc – Fiehra Sep 18 '22 at 11:16
  • at the current state I am at your point, where it wouldnt matter to assing a new id - but what if you connect data to your customers id - then you wont lose this connection, just because you move them to the archive or something like that – VebDav Sep 19 '22 at 18:18

1 Answers1

1

You can simply write a short aggregation pipeline like this:

app.post("/archive-data", async (req, res) => {
    const docId = req.body.archiveButton;

    await Customer.aggregate([
     { $match: 
       { _id: docId}
     }, 
     {
       $merge: {
          into: "customerArchive",
          on: "_id",
          whenMatched: "replace",
          whenNotMatched: "insert"
       }
     }
     ]).exec((err, res) => console.log(`${err} ${res}`));
    Customer.findByIdAndRemove(docId, (err) => {
            if (!err) {
                res.redirect("/customers");
            } else {
                console.log(err);
            }
    });
});

In the pipeline, we first match the required document and merge it into the customerArchive collection using $merge.

If you are using ObjectId, as recommended by Mongodb, you can be sure that it will be unique across collections, as it is a combination of timestamp, a random number, and an auto-incrementing counter, hence the chances of collisions are highly unlikely.

Also, creating a new collection to store just archive customers, seems like overkill, you can reduce a lot of effort, by just storing a boolean value archive in the customer's collections itself, denoting whether the customer is archived or not.

Charchit Kapoor
  • 8,934
  • 2
  • 8
  • 24
  • Thanks for your quick response. But something is broken. The code snippet does only remove the document from the Customer collection and doesnt move the document to customerArchive – VebDav Sep 19 '22 at 18:29
  • To your last point, that seems like an easier way to handle data to be archived - so you ll only filter entries without a flag. But once data grows isnt it better to keep the db small with needed data? – VebDav Sep 19 '22 at 18:32
  • If you make the archive field required, within your customers' collection and index it. Then probably, the data size won't have much impact on performance. – Charchit Kapoor Sep 20 '22 at 04:05
  • The aggregation seems to be working here. https://mongoplayground.net/p/h9xcmMzwx2w Is it logging any error? Try simply with `$match` stage once, and see if it's returning the matching document, if it doesn't, you might have to wrap `docId` like this `new ObjectId(docId)`. Finally what is your mongodb version? – Charchit Kapoor Sep 20 '22 at 04:08