Search code examples
node.jsmongodbmongoose2dsphere

Mongoose setDefaultsOnInsert and 2dsphere index not working


I am having trouble trying to insert a new user in my database when using findOneAndUpdate with upsert and setDefaultsOnInsert set to true. What I am trying to do is to set the default values of the following schema:

var userSchema = new mongoose.Schema({
   activated: {type: Boolean, required: true, default: false},
   facebookId: {type: Number, required: true},
   creationDate: {type: Date, required: true, default: Date.now},
   location: {
       type: {type: String},
       coordinates: []
   },
   email: {type: String, required: true}
});

userSchema.index({location: '2dsphere'});

findOneAndUpdate code:

model.user.user.findOneAndUpdate(
      {facebookId: request.params.facebookId},
      {
          $setOnInsert: {
              facebookId: request.params.facebookId,
              email: request.payload.email,
              location: {
                  type: 'Point',
                  coordinates: request.payload.location.coordinates
              }
          }
      },
      {upsert: true, new: true, setDefaultsOnInsert: true}, function (err, user) {
          if (err) {
              console.log(err);
              return reply(boom.badRequest(authError));
          }
          return reply(user);
      });

As you can see I also store the latitude and longitude of the user and that's where the problem begins. When findOneAndUpdate gets called I get this error:

{ [MongoError: exception: Cannot update 'location' and 'location.coordinates' at the same time]
name: 'MongoError',
message: 'exception: Cannot update \'location\' and \'location.coordinates\' at the same time',
errmsg: 'exception: Cannot update \'location\' and \'location.coordinates\' at the same time',
code: 16836,
ok: 0 }

When I remove the 2dsphere index and all location related code It does set my creationDate. I am doing something wrong?


Solution

  • The setDefaultsOnInsert option uses the $setOnInsert operator to perform its function, and it looks like that's conflicting with your own use of $setOnInsert to set the location.

    A workaround would be to remove the setDefaultsOnInsert option and put it all in your own $setOnInsert operator:

    model.user.user.findOneAndUpdate(
          {facebookId: request.params.facebookId},
          {
              $setOnInsert: {
                  activated: false,
                  creationDate: Date.now(),
                  email: request.payload.email,
                  location: {
                      type: 'Point',
                      coordinates: request.payload.location.coordinates
                  }
              }
          },
          {upsert: true, new: true},
          function (err, user) {
              if (err) {
                  console.log(err);
                  return reply(boom.badRequest(authError));
              }
              return reply(user);
          });