best way to ensure unique arrays in Ref attributes?

6,780 views
Skip to first unread message

Anthony Ettinger

unread,
Jun 7, 2014, 12:56:09 AM6/7/14
to mongoo...@googlegroups.com

I have this code, but it does not enforce the array be unique.

/**
 * User Schema
 */
var UserSchema = new Schema({
    username: { type: String, unique: true },
    followers: [{ type: Schema.ObjectId, ref: 'User', index: true, unique: true }],
    following: [{ type: Schema.ObjectId, ref: 'User', index: true, unique: true }]
});

I can add the same user to followers or following multiple times without any errors. It should throw an error if there is already that user in the list.

Is there a better way to provide uniqueness on the Schema.ObjectId, ref: 'User' arrays?


Someone mentioned I should look into $addToSet, but I cannot find any examples of how to do this with Mongoose.

Ryan Wheale

unread,
Jun 7, 2014, 7:34:06 PM6/7/14
to mongoo...@googlegroups.com
I just had my first bout with uniqueness within arrays.  In short, you can't really have unique values in array fields (as far as I have found).  I hope this is a feature mongo will implement some day or at least make it more clear as to how it should be implemented.Here are some good resources which help explain this:


Also, if you have existing documents in the collection with duplicate values, the unique index cannot be added retroactively.  You must go through, delete any duplicates, RESTART mongo, and then add the index.  You may be able to mess around with the "dropDups" option on the index, but be careful, as this will automatically delete data.  You are better off deleting data yourself IMO.  Some people even reported having to create a brand new collection, adding the index, porting data over from the old collection, deleting the old collection, and renaming the new one to match the old name.

Another technique I saw was creating a "compound" index using the parent document's _id and the array field - but I can't remember how successful the person was who was implementing this technique.

Lastly, the mongoose documentation are not very clear about how to pass other options for indexing, so here are some examples to help you (NOTE: I am providing examples for passing "options" for indexes.  I make no promise that the following will help you actually enforce uniqueness - and from my research, it won't):

...
  following: [{ type: Schema.ObjectId, ref: 'User', index: {unique: true, dropDups: true} }]
...
UserSchema.index({following: 1}, {unique: true, dropDups: true});


--
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.

Valeri Karpov

unread,
Jun 27, 2014, 5:26:15 PM6/27/14
to mongoo...@googlegroups.com
Any particular reason why you can't just use $addToSet? MongoDB has some decent docs about how to use it here: http://docs.mongodb.org/manual/reference/operator/update/addToSet/ The tricky bit is that $addToSet doesn't throw an error, it just silently doesn't add the value when its already there. You could also simply scan the array on the client side manually.

Also, FYI, you don't have to restart mongodb to create a new unique index.
To unsubscribe from this group and stop receiving emails from it, send an email to mongoose-orm+unsubscribe@googlegroups.com.

Ryan Wheale

unread,
Jun 27, 2014, 6:00:42 PM6/27/14
to mongoo...@googlegroups.com
Thanks Valeri.  I found several posts in my research where people said they restarted mongod and their index started working as expected.  There were several "me toos" on those posts.  I figured it's probably an edge case, but worth the time: ctrl + c, up arrow, enter

The problem with $addToSet and nested documents is that the entire document must be equivalent in order for the duplicate to be seen.  For example, lets say you had the following document and nested documents with a unique index on the "books" path:

{ _id: "joe", books: [{_id: "book1", title: "Name of Book 1"}, {_id: "book2", title: "Name of Book 2"}] }

You could add the following book: {_id: "book1", title: "Name of Book 3"}

Notice it has the same ID as an existing book. There is no way in mongo (as far as I know) to make the books array unique based on books._id.  This is something which I hope is fixed in the native mongo driver.  For now, you must check using client code.

~Ryan


To unsubscribe from this group and stop receiving emails from it, send an email to mongoose-orm...@googlegroups.com.

Anthony Ettinger

unread,
Jun 27, 2014, 8:15:59 PM6/27/14
to mongoo...@googlegroups.com
$addToSet() solved my problem. Thanks.


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.

William Riley-Land

unread,
Jun 27, 2014, 8:17:28 PM6/27/14
to mongoo...@googlegroups.com
Valeri is on a roll!

Sent from my iPhone
To unsubscribe from this group and stop receiving emails from it, send an email to mongoose-orm...@googlegroups.com.

Valeri Karpov

unread,
Jul 3, 2014, 4:52:20 PM7/3/14
to mongoo...@googlegroups.com
@Ryan Yeah you're right, MongoDB can't only look at a subset of elements in determining equality for $addToSet, it just does a deep equality check: http://docs.mongodb.org/manual/reference/operator/update/addToSet/#behavior . I don't think this'll be on the roadmap for the next version of mongoose, but you're more than welcome to open up a ticket at jira.mongodb.org with a feature request :)
Reply all
Reply to author
Forward
0 new messages