0

I setup a mongoose schema like so:

const expertSchema = new mongoose.Schema({
  firstName: {
    type: String,
    required: true,
  },
  lastName: {
    type: String,
    required: true,
  },
  contactMethods: {
    type: [ContactMethod.schema],
  }
})

expertSchema.plugin(autoIncrement.plugin, {
  model: 'EXP-',
  startAt: 1,
  prefix: 'EXP-'
});

const Expert = mongoose.model('Expert', expertSchema);

const contactMethodsSchema = new mongoose.Schema({
  type: {
    type: String,
    required: true,
  },
  value: {
    type: String,
    required: true,
  },
  preferred: {
    type: Boolean
  }
});

contactMethodsSchema.plugin(autoIncrement.plugin, {
  model: 'CON-',
  startAt: 1,
  prefix: 'CON-'
});
const ContactMethod = mongoose.model('ContactMethod', contactMethodsSchema)

I'm using the mongoose-auto-increment plugin to auto-increment my monogodb document's id's (actually using this fork, but they are basically the same)

I have some code that seeds my dev. db on restart which looks like this:

const contMethod1 = new models.ContactMethod({
  type: 'email',
  value: "janedoe@acme.org",
  preferred: false,
});

const contMethod2 = new models.ContactMethod({
  type: 'phone',
  value: "+12125550711",
  preferred: true,
});

const expert1 = new models.Expert({
  firstName: 'Jane',
  lastName: 'Doe',
  contactMethods: [contMethod1, contMethod2]
});

expert1.save();

This code works great and I end up with a document that looks like:

{
  "_id": "EXP-1",
  "firstName": "Jane",
  "lastName": "Doe",
  "contactMethods": [{
      "type": "email",
      "value": "janedoe@acme.org",
      "preferred": false,
      "_id": "CON-1"
    },
    {
      "type": "phone",
      "value": "+12125550711",
      "preferred": true,
      "_id": "CON-2"
    }
  ]
}

However when my frontend posts this edited data back to me I get json that looks like:

{
  _id: "EXP-1",
  "contactMethods": [{
      "type": "email",
      "value": "janedoe@acme.org",
      "preferred": false,
      "_id": "CON-1"
    },
    {
      "type": "phone",
      "value": "+12125550711",
      "preferred": true,
      "_id": "CON-2"
    },
    {
      "type": "whatsapp",
      "value": "+123456789",
      "preferred": false
    }
  ]
}

Now what I want is to update existing embedded documents and/or add new ones if needed. I setup this code for this purpose (I appreciate there is probably a much more intelligent way to implement this, but even so, it sort of "works"):

req.body.contactMethods.map((_, index) => {
  if (_._id) {
    expert = req.context.models.Expert.findOneAndUpdate({
      "contactMethods._id": _._id
    }, {
      $set: {
        "contactMethods.$.type": _.type,
        "contactMethods.$.value": _.value,
        "contactMethods.$.preferred": _.preferred
      }
    }, {
      useFindAndModify: false,
      returnOriginal: false,
      runValidators: true
    })
  } else {
    expert = req.context.models.Expert.findOneAndUpdate({
      "_id": req.user._id,
    }, {
      $push: {
        "contactMethods": _
      }
    }, {
      useFindAndModify: false,
      returnOriginal: false,
      runValidators: true
    })
  }
})

Unfortunately, is this scenario my plugin doesn't "trigger" or invoke and I end up with a document updated in the collection which lacks a "_id" property for the newly pushed contactMethod.

Why is my plugin not being called on the findOneAndUpdate call?

I found this question but my plugin doesn't use ES6 syntax. Also I found this comment but the current mongoose documentation doesn't state that anymore so I was kinda hoping maybe they changed/fixed it.

Thanks! (first question, phew..)

1 Answers1

0

I eventually solved (or rather worked around this) by changing my code and splitting up the addition of a new contact method to two operations: findOne and save, like so:

req.body.contactMethods.map((_, index) => {
  if (_._id) {
    expert = req.context.models.Expert.findOneAndUpdate({
      "contactMethods._id": _._id
    }, {
      $set: {
        "contactMethods.$.type": _.type,
        "contactMethods.$.value": _.value,
        "contactMethods.$.preferred": _.preferred
      }
    }, {
      useFindAndModify: false,
      returnOriginal: false,
      runValidators: true
    })
  } else {
    expert = req.context.models.Expert.findOne({
      "_id": req.user._id,
    })
    expert.contactMethods.push(_);
    expert.save({ validateModifiedOnly: true });
  }
})