Mongoose unique index on subdocument

2019-01-24 07:55发布

问题:

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?

回答1:

Long story short: Mongo doesn't support unique indexes for subdocuments, although it allows creating them...



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



回答3:

First objectId length in mongodb must be 24. Then you can turn off _id, and rename _id as id or others,and try $addToSet. Good luck.

CoffeeScript example:

FromSchema = new Schema(
  source: { type: String, trim: true }
  version: String
  { _id: false }//to trun off _id
)

VisitorSchema = new Schema(
  id: { type: String, unique: true, trim: true }
  uids: [ { type: Number, unique: true} ]
  from: [ FromSchema ]
)

//to update
Visitor.findOneAndUpdate(
  { id: idfa }
  { $addToSet: { uids: uid, from: { source: source, version: version } } }
  { upsert: true }
  (err, visitor) ->
    //do stuff