runnerpaul
runnerpaul

Reputation: 7236

MongoDB save() creating new document each time rather than updating existing document

I'm using this function to save to mongodb:

create(req, res, next) {
  let data = req.body;
  if(data) {
    let newIssue = this.lib.db.model('Issues')('data');
    console.log(newIssue);
    newIssue.save((err, emp) => {
      if(err) return next(this.RESTError('InternalServerError', err));
      this.writeHAL(res, emp);
    });
  } else {
    next(this.RESTError('InvalidArgumentError', 'No data received'));
  }
}

This is my schema:

module.exports = {
  "id": "Issues",
  "properties": {
    "issue_title": {
      "type": "string",
      "description": "The title of the issue"
    },
    "issue_number": {
      "type": "string",
      "description": "The issue number"
    },
    "issue_url": {
      "type": "string",
      "description": "URL of the issue"
    }
  }
}

My understanding of save() is that if the document already exists in mongodb it gets updated but a new one is created if it doesn't already exist. However, each time I POST the same document, a new document is inserted rather than the existing one getting updated. I have a suspicion this is because when saving, an _id key containing a unique ID is added to my schema making the document unique. Is this correct and if so how do I resolve this?

Additional info

I've now added a PUT. the idea is that I would do an update with {upsert:true}. I've it partially working in that I can PUT to issue/{id} where{id}is the mongodb_id` and update existing documents.

This is my PUT:

update(req, res, next) {
  let data = req.body;
  let id = req.params.id;
  if(id) {
    this.lib.db.model('Issues')
      .findOne({_id: id})
      .exec((err, updateIssue) => {
        if(err) return next(this.RESTError('InternalServerError', err));
        updateIssue = Object.assign(updateIssue, data);
        updateIssue.update((err, issue) => {
          if(err) return next(this.RESTError('InternalServerError', err));
          this.writeHAL(res, issue);
        });
      });
  }
}

This is the routing:

    controller.addAction({
      'path': '/issues/{id}',
      'method': 'PUT',
      'summary': "UPDATES the issues's information",
      'params': [swagger.pathParam('id', 'The id of the issue', 'string'),
    swagger.bodyParam('issue', 'the new information to update', 'string')],
      'responseClass': 'Issues',
      'nickname': 'updateIssue'
    }, controller.update);

I don't think I can use this for upsert as I have no _id to begin with so can;t create the path. My other option is to use the github_id. however any time I try to cahnge my routing path to /issues/{github_id} or update my function with something like .findOne({github_id: id}) I get an error. I need to be able to use /issues/{github_id} and for the check to see if the github_id already exists. If it does, update the document, if not, create a new one.

Further info.

I've changed my update function to:

update(req, res, next) {
  let data = req.body;
  let id = req.params.id;
  if(id) {
    this.lib.db.model('Issues')
      .findOne({_id: id})
      .exec((err, updateIssue) => {
        if(err) return next(this.RESTError('InternalServerError', err));
        updateIssue = Object.assign(updateIssue, data);
        updateIssue.update({_id: id}, data, (err, issue) => {
          if(err) return next(this.RESTError('InternalServerError', err));
          this.writeHAL(res, issue);
        });
      });
  }
}

If I add the following console.log's after line updateIssue.update({_id: id}, data, (err, issue) => {:

console.log('id: ', id);
console.log('data: ', data);
console.log('err: ', err);
console.log('issue: ', issue);

I get this output:

id:  5b80307403eea4f14b06fe8c
data:  { _id: '5b80307403eea4f14b06fe8c',
  github_id: '347593262',
  issue_title: 'test issue XY',
  issue_number: '2',
  issue_url: 'https://api.github.com/repos/PCTestOrg/test1/issues/2a',
  __v: 0 }
err:  null
issue:  { n: 1, nModified: 0, ok: 1 }

I expect issue_title to be updated from test issue X to test issue XY, however the update does not happen. What am I doing wrong here?

Upvotes: 0

Views: 877

Answers (2)

runnerpaul
runnerpaul

Reputation: 7236

I couldn't get upsert working so ended up with the below after reading this post:

update(req, res, next) {
  let data = req.body;
  let id = req.params.id;
  if(id) {
    this.lib.db.model('Issues')
      .findOne({_id: id})
      .exec((err, updateIssue) => {
        if(err) return next(this.RESTError('InternalServerError', err));
        updateIssue = Object.assign(updateIssue, data);
        updateIssue.save((err, issue) => {
          if(err) return next(this.RESTError('InternalServerError', err));
          this.writeHAL(res, issue);
        });
      });
  }
  else if(data) {
    let newIssue = this.lib.db.model('Issues')(data);
    console.log(newIssue);
    newIssue.save((err, issue) => {
      if(err) return next(this.RESTError('InternalServerError', err));
      this.writeHAL(res, issue);
    });
  } else {
    next(this.RESTError('InvalidArgumentError', 'No data received'));
  }
}

I'm sure there is a better way to do it if anybody can show me I'd appreciate it.

Upvotes: 0

Rahul Sharma
Rahul Sharma

Reputation: 10111

If you want to do create or update use Update with upsert

Model.update({_id: id}, obj, {upsert: true}, function (err) {...});

Upvotes: 1

Related Questions