$addToSet not working for me

1,239 views
Skip to first unread message

Arthur Frankel

unread,
Jul 16, 2014, 7:31:54 PM7/16/14
to mongoo...@googlegroups.com
I'm sure I'm missing something obvious but I don't see it.

My update doesn't work:

  meetings.update({ _id: meeting.id },
      { $addToSet: { participants: { _id: req.user.id } } },
        function(err) {
          if (err) {
            return res.send(400, {
              message: getErrorMessage(err)
            });
          } else {
            res.jsonp(game);
          }
  });

My schema is as follows:
var MeetingSchema = new Schema({
description: {
type: String
},
participants: [ {
type: Schema.ObjectId,
ref: 'User'
} ]
});

I know that $addToSet ignores duplicates, but I'm sure the id for the participant doesn't exist in the array yet.  Any suggestions?


Ryan Wheale

unread,
Jul 16, 2014, 8:01:51 PM7/16/14
to mongoo...@googlegroups.com
Try this:

 { $addToSet: { participants: req.user.id } }

Also, you may need to cast it first:

 { $addToSet: { participants: mongoose.Types.ObjectId( req.user.id ) } }


--
Documentation - http://mongoosejs.com/
Plugins - http://plugins.mongoosejs.com/
Bug Reports - http://github.com/learnboost/mongoose
Production Examples - http://mongoosejs.tumblr.com/
StackOverflow - http://stackoverflow.com/questions/tagged/mongoose
Google Groups - https://groups.google.com/forum/?fromgroups#!forum/mongoose-orm
Twitter - https://twitter.com/mongoosejs
IRC - #mongoosejs
---
You received this message because you are subscribed to the Google Groups "Mongoose Node.JS ODM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongoose-orm...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Anthony Ettinger

unread,
Jul 16, 2014, 8:04:32 PM7/16/14
to mongoo...@googlegroups.com
First one should work, I haven't had to cast ids before. 

Ryan Wheale

unread,
Jul 16, 2014, 8:12:00 PM7/16/14
to mongoo...@googlegroups.com
Thanks Anthony.  I know you need to cast if you use aggregation, and since update() is more of a direct-to-db operation (similar to aggregation), I put it there as a just-in-case.  Mongoose handles the casting internally, but it's important to know that it happens IMO.

Also, you can operate on the native driver by doing something like Model.collection.update() - in which case you will definitely need to cast as mongoose will not do it for you.

~Ryan

Anthony Ettinger

unread,
Jul 16, 2014, 8:14:04 PM7/16/14
to mongoo...@googlegroups.com
I see. thanks.
Message has been deleted

Arthur Frankel

unread,
Jul 16, 2014, 8:29:41 PM7/16/14
to mongoo...@googlegroups.com
Firstly thank you for all of the responses, but unfortunately neither works.

I'm running node in debug mode so I can see the values:

meeting.id = "53c6fc6a4f0ea90000b1ae2d"
req.user.id = "53bf31eafb278d8823ff7182"
meeting.participants (before and after call) is an object containing several attributes including the only participant already in there from creation = model -> _doc -> _id (ObjectID) -> id (this value is "SÆ 0‡f Þ Å\") which I don't understand. Not sure if that has to do with anything.

Arthur Frankel

unread,
Jul 16, 2014, 8:38:59 PM7/16/14
to mongoo...@googlegroups.com
I tried adding with no existing participants and still the same issue.

Ryan Wheale

unread,
Jul 16, 2014, 9:03:30 PM7/16/14
to mongoo...@googlegroups.com
Can you post your game schema and update code.  You were referencing meetings originally.  Check back over everything, make sure everything is in line.


On Wed, Jul 16, 2014 at 6:38 PM, Arthur Frankel <afra...@solutionstreet.com> wrote:
I tried adding with no existing participants and still the same issue.

--

Arthur Frankel

unread,
Jul 16, 2014, 9:17:19 PM7/16/14
to mongoo...@googlegroups.com
Schema is just Meetings and Users ('game' was a copy/paste error)

Code to Add a Participant (current user):
  var meeting = req.meeting ;

  meeting = _.extend(meeting , req.body);

  meeting.update({ _id: meeting.id },
      { $addToSet: { participants: req.user.id } },
        function(err) {
          if (err) {
            return res.send(400, {
              message: getErrorMessage(err)
            });
          } else {
            res.jsonp(meeting);
          }
  });

var MeetingSchema = new Schema({
description: {
type: String
},
participants: [ {
type: Schema.ObjectId,
ref: 'User'
} ]
});

The User model is the one with meanjs:

var UserSchema = new Schema({
firstName: {
type: String,
trim: true,
default: '',
validate: [validateLocalStrategyProperty, 'Please fill in your first name']
},
lastName: {
type: String,
trim: true,
default: '',
validate: [validateLocalStrategyProperty, 'Please fill in your last name']
},
displayName: {
type: String,
trim: true
},
email: {
type: String,
trim: true,
default: '',
validate: [validateLocalStrategyProperty, 'Please fill in your email'],
match: [/.+\@.+\..+/, 'Please fill a valid email address']
},
username: {
type: String,
unique: true,
required: 'Please fill in a username',
trim: true
},
password: {
type: String,
default: '',
validate: [validateLocalStrategyPassword, 'Password should be longer']
},
salt: {
type: String
},
provider: {
type: String,
required: 'Provider is required'
},
providerData: {},
additionalProvidersData: {},
roles: {
type: [{
type: String,
enum: ['user', 'admin']
}],
default: ['user']
},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
}
});

Arthur Frankel

unread,
Jul 17, 2014, 7:50:34 AM7/17/14
to mongoo...@googlegroups.com
I would set up an example online but I don't think there is something like plunkr.co for mongodb/mongoose.

Arthur Frankel

unread,
Jul 17, 2014, 3:48:52 PM7/17/14
to mongoo...@googlegroups.com
I now have it working but I'm not exactly sure why.

If I use Meeting.update instead of meeting.update it works. I neglected to add the code prior which obtains the meeting from the request then copies the meeting object to the req.body.  If I use (big M) Meeting.update I seems to work - why is that?   Also, after I use the $addToSet successfully I need to re-get my meeting using something like Meeting.findById()....populate(.....) to fill in my added user information to display back on the UI.  Is this right?

  var meeting = req.meeting ;

  meeting = _.extend(meeting , req.body);
 
  meeting.update({ _id: meeting.id },
      { $addToSet: { participants: { _id: req.user.id } } },
        function(err) {
          if (err) {
            return res.send(400, {
              message: getErrorMessage(err)
            });
          } else {
            res.jsonp(meeting);
          }
  });

Arthur Frankel

unread,
Jul 17, 2014, 8:55:12 PM7/17/14
to mongoo...@googlegroups.com
OK, I'm still learning so bear with me.

First I figured out my issue.  With update it's apparently a static method since you pass in the query instead of operating on "self".  Also, I move the method from the controller to the model since it makes sense to have fat models and skinny controllers.

I ended up with the following code with works, but not I have to somehow populate the new reference object after I add it using $addToSet.

MeetingSchema.methods.join = function join(params, callback) {
  var Meeting = mongoose.model('Meeting');
  return Meeting.update({ _id: this.id },
      { $addToSet: { participants: params.id } })
      .populate('participants', 'displayName')
      .exec(callback);
};

I think I have 2 choices here:
a) populate the displayName of the participants after the update - this code above to populate doesn't see to work; or
b) see if I could add the displayName as a key value to the $addToSet call.  I don't think I can do this since the $addToSet apparently seems to just be looking for the id field only (as I have above in the code).

Suggestions?

Ryan Wheale

unread,
Jul 17, 2014, 9:02:07 PM7/17/14
to mongoo...@googlegroups.com
update doesn't select your document... it just updates it and returns the number of affected rows.  You will need to use findOneAndUpdate and set the "new" option to true.  Still not sure if populate() will work on this... it should:

  var Meeting = mongoose.model('Meeting');
  return Meeting.findOneAndUpdate({ _id: this.id },
      { $addToSet: { participants: params.id } },
      {new: true})
      .populate('participants', 'displayName')
      .exec(callback);

--

Arthur Frankel

unread,
Jul 17, 2014, 9:27:46 PM7/17/14
to mongoo...@googlegroups.com
Thank you.  That makes sense to not use the update method.  The populate still doesn't work. I would think this is a typical scenario of adding to a (reference) collection and wanting to populate on the way "out".

Arthur Frankel

unread,
Jul 17, 2014, 9:37:05 PM7/17/14
to mongoo...@googlegroups.com
Actually it does work. In my controller I needed to have the callback as follows:
function(err, updatedMeeting)

Thank you for your help.  Now I can add model tests.
Reply all
Reply to author
Forward
0 new messages