33

say I have this array property ('articles') on a Mongoose schema:

articles: [ 

 {
  kind: 'bear',
  hashtag: 'foo'    
 },

 {
  kind: 'llama',
  hashtag: 'baz',
  },

 {
  kind: 'sheep',
  hashtag: 'bar',
  }

]

how can I use

$addToSet https://docs.mongodb.org/manual/reference/operator/update/addToSet/

to add to this array by checking the value of hashtag to see if it's unique?

For example, if I want to add the following object to the above array, I want Mongo to 'reject' it as a duplicate:

{
  kind: 'tortoise',
  hashtag: 'foo'

}

because hashtag=foo has already been taken.

The problem is that I only know how to use $addToSet with simple arrays of integers...

for example, if articles looked like this:

articles: [ 1 , 5 , 4, 2]

I would use $addToSet like this:

var data = {

    "$addToSet": {
     "articles": 9
   }

}

model.update(data);

but how can I accomplish the same thing with an array of objects where the unique field is a string, in this case 'hashtag'? The docs don't make this clear and it seems like I have searched everywhere..

thanks

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

2 Answers2

52

You need to use the $ne operator.

var data = { 'kind': 'tortoise', 'hashtag': 'foo' };
Model.update(
    { 'articles.hashtag': { '$ne': 'foo' } }, 
    { '$addToSet': { 'articles': data } }
)

This will update the document only if there is no sub document in the "article" array with the value of hashtag equals to "foo".

As @BlakesSeven mentioned in the comment

The $addToSet becomes irrelevant once you are testing for the presence of one of the values, so this may as well be a $push for code clarity. But the principle is correct since $addToSet works on the whole object and not just part of it.

Model.update({ 
    { 'articles.hashtag': { '$ne': 'foo' } }, 
    { '$push': {'articles':  data } }
)
styvane
  • 59,869
  • 19
  • 150
  • 156
  • What if the object contains "_id" field? Will it still require '$ne' operator to check duplicity? – Abhinav Saini Sep 07 '17 at 07:08
  • @styvane What if instead of matching with only hashtag I want to match with "article.kind" and "article.hashtg" and then add to set if the perfect match not exist – Pavan Vora Jan 30 '21 at 10:20
  • 1
    @PavanVora, you could you `{'articles.hastag': {'$ne': 'cats'}, 'articles.kind': {'$ne': 'struct'}}` in the filter. – styvane Jan 31 '21 at 11:07
  • Thank you @styvane :) appreciate your reply. – Pavan Vora Jan 31 '21 at 13:11
  • @PavanVora If you're looking for perfect matches you could simply use $addToSet itself – Macindows Dec 07 '22 at 06:38
  • @Macindows are you sure? because I don't think it will work on Array types. – Pavan Vora Dec 09 '22 at 08:33
  • 1
    @PavanVora Yes, it'll work.. Only thing is, the order of keys within the object should be the same... So {a, b, c} and {a, c, b} are treated as different objects even though its basically the same object – Macindows Dec 29 '22 at 11:02
  • @PavanVora @Macindows `$addToSet` didn't work with Array. I added {a,b,c} two times and both obj got added. – Mitanshu Jun 16 '23 at 04:33
  • Good solution unless your are using `$each` to add multiple values, in which case the entire query is canceled by the `$ne` filter, and NONE of the values are added just because ONE of them is already present. – lance.dolan Aug 21 '23 at 21:02
0
// add the comment's id to the commentsList : 
                        // share.comments.commentsList.addToSet(callback._id);
                        share.update(
                            { '$push': {'comments.commentsList':  mongoose.Types.ObjectId(callback._id) } }
                        , function(){
                            console.log('added comment id to the commentsList array of obectIds')
                        })
Abdullah Alkurdi
  • 378
  • 1
  • 5
  • 11