1

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?

Jdruwe
  • 3,450
  • 6
  • 36
  • 57

2 Answers2

3

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);
      });
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • I was thinking about doing this but does this mean that there is a bug in mongoose? – Jdruwe Jun 28 '15 at 15:39
  • 1
    @Jdruwe Yep, I'd consider the issue to be a bug in Mongoose. It shouldn't be trying to set `location.coordinates` to an empty array as a default in this case. – JohnnyHK Jun 28 '15 at 15:42
  • 1
    Ok I created an issue on the github repo, thanks for your time! – Jdruwe Jun 28 '15 at 15:43
  • 1
    @Jdruwe It's good to report the issue, but it would be best to simplify it to its essence for the Mongoose developers. I doubt this has anything to do with the `2dsphere` index. – JohnnyHK Jun 28 '15 at 16:15
0

One of the devs responded to my issue on github and added it as a milestone for the 4.0.8 release, this was his workaround:

  $setOnInsert: {
          facebookId: request.params.facebookId,
          email: request.payload.email,
          'location.type': 'Point',
          'location.coordinates': request.payload.location.coordinates
  }

His solution does NOT work for me, I get the following error:

{ [MongoError: exception: insertDocument :: caused by :: 16755 Can't extract geo keys from object, malformed geometry?: { _id: ObjectId('559024a0395dd599468e4f41'), facebookId: 1.020272578180189e+16, location: { coordinates: [], type: "Point" }, email: "druwe.jeroen@gmail.com", gender: "male", lastName: "Druwé", firstName: "Jeroen", __v: 0, activated: false, creationDate: new Date(1435509921264), familyDetails: { children: [] } }]
Jdruwe
  • 3,450
  • 6
  • 36
  • 57
  • When I tried that it got rid of the error, but `location.coordinates` ended up as `[]` instead of the coordinates from the request. – JohnnyHK Jun 28 '15 at 16:41
  • I also get an empty [] and the error I added in the answer. It's weird that you don't get this MongoError. – Jdruwe Jun 28 '15 at 16:47