Mongoose unique index on subdocument
Solution 1
Long story short: Mongo doesn't support unique indexes for subdocuments, although it allows creating them...
Solution 2
This comes up in google so I thought I'd add an alternative to using an index to achieve unique key constraint like functionality in subdocuments, hope that's OK.
I'm not terribly familiar with Mongoose so it's just a mongo console update:
var foo = { _id: 'some value' }; //Your new subdoc here
db.yourCollection.update(
{ '_id': 'your query here', 'myArray._id': { '$ne': foo._id } },
{ '$push': { myArray: { foo } })
With documents looking like:
{
_id: '...',
myArray: [{_id:'your schema here'}, {...}, ...]
}
The key being that you ensure update will not return a document to update (i.e. the find part) if your subdocument key already exists.
Related videos on Youtube
Sebastian Nowak
Updated on September 20, 2022Comments
-
Sebastian Nowak over 1 year
Let's say I have a simple schema:
var testSchema = new mongoose.Schema({ map: { type: [ mongoose.Schema.Types.Mixed ], default: [] }, ...possibly something else });
Now let's ensure that pairs (
_id
,map._id
) are unique.testSchema.index({ _id: 1, 'map._id': 1 }, { unique: true });
Quick check using
db.test.getIndexes()
shows that it was created.{ "v" : 1, "unique" : true, "key" : { "_id" : 1, "map._id" : 1 }, "name" : "_id_1_map._id_1", "ns" : "test.test", "background" : true, "safe" : null }
The problem is, this index is ignored and I can easily create multiple subdocuments with the same
map._id
. I can easily execute following query multiple times:db.maps.update({ _id: ObjectId("some valid id") }, { $push: { map: { '_id': 'asd' } } });
and end up with following:
{ "_id": ObjectId("some valid id"), "map": [ { "_id": "asd" }, { "_id": "asd" }, { "_id": "asd" } ] }
What's going on here? Why can I push conflicting subdocuments?
-
Adam LockhartIt is bad practice to use anything other than ObjectId for
_id
. Why would you want to do that?
-
-
Sebastian Nowak over 9 yearsSubdocument's
_id
isn't anObjectId
when it's declared as array ofMixed
. Also it doesn't answer the question. -
Sebastian Nowak over 9 years...which means that unique indexing for subdocuments isn't possible in the way explained in the question, and that's what I wrote.
-
John John about 4 yearsyou can use
$addToSet
operator that adds a value to an array unless the value is already present, in which case$addToSet
does nothing to that array. -
Tobi over 3 yearsbut doesnt this run over the entire set of documents of the model? because exactly once the id is matched and then, if this myArray.id already exists, the query tries to find a document where the id as well as myArray.id match. Seems pretty wasteful
-
phuhgh over 3 yearsRE collection scans, use indexes. $addToSet at the time of writing did not understand object equality so would always add regardless. Depending on your use case this approach may well be overkill now.