MongoDB: Unique Key in Embedded Document

2019-01-11 14:10发布

问题:

Is it possible to set a unique key for a key in an embedded document?

I have a Users collection with the following sample documents:

 {
       Name: "Bob",
       Items: [
           {
               Name: "Milk"
           },
           {
               Name: "Bread"
           }
       ]
    },
    {
       Name: "Jim"
    },

Is there a way to create an index on the property Items.Name?

I got the following error when I tried to create an index:

> db.Users.ensureIndex({"Items.Name": 1}, {unique:true});
E11000 duplicate key error index: GroceryGuruApp.Users.$Items.Name_1  dup key: {
 : null }

Any suggestions? Thank you!

回答1:

Unique indexes exist only across collection. To enforce uniqueness and other constraints across document you must do it in client code. (Probably virtual collections would allow that, you could vote for it.)

What are you trying to do in your case is to create index on key Items.Name which doesn't exist in any of the documents (it doesn't refer to embedded documents inside array Items), thus it's null and violates unique constraint across collection.



回答2:

You can create a unique compound sparse index to accomplish something like what you are hoping for. It may not be the best option (client side still might be better), but it can do what you're asking depending on specific requirements.

To do it, you'll need to create another field on the same level as Name: Bob that is unique to each top-level record (could do FirstName + LastName + Address, we'll call this key Identifier).

Then create an index like this:

ensureIndex({'Identifier':1, 'Items.name':1},{'unique':1, 'sparse':1})

A sparse index will ignore items that don't have the field, so that should get around your NULL key issue. Combining your unique Identifier and Items.name as a compound unique index should ensure that you can't have the same item name twice per person.

Although I should add that I've only been working with Mongo for a couple of months and my science could be off. This is not based on empirical evidence but rather observed behavior.

More on MongoDB Indexes

  • Compound Keys Indexes
  • Sparse Indexes


回答3:

The index will be across all Users and since you asked it for 'unique', no user will be able to have two of the same named item AND no two users will be able to have the same named Item.

Is that what you want?

Furthermore, it appears that it's objecting to two Users having a 'null' value for Items.Name, clearly Jim does, is there another record like that?

It would be unusual to require uniqueness on an indexed collection like this.

MongoDB does allow unique indexes where it indexes only the first of each value, see http://www.mongodb.org/display/DOCS/Indexes#Indexes-DuplicateValues, but I suspect the real solution is to not require uniqueness in this case.

If you want to ensure uniqueness only within the Items for a single user you might want to try the $addToSet option. See http://www.mongodb.org/display/DOCS/Updating#Updating-%24addToSet



回答4:

An alternative would be to model the items as a hash with the item name as the key.

Items: { "Milk": 1, "Bread": 1 }

I'm not sure about whether you're trying to use the index for performance or purely for the constraint. The right way to approach depends on your use cases, and determining whether the atomic operations are enough to keep your data consistent.



回答5:

You can use use findAndModify to create a sequence/counter function.

function getNextSequence(name) {
   var ret = db.counters.findAndModify({
        query: { _id: name },
        update: { $inc: { seq: 1 } },
        new: true,
        upsert: true
    });
    return ret.seq;
}

Then use it whenever a new id is needed...

db.users.insert({
    _id: getNextSequence("userid"),
    name: "Sarah C."
})

This is from http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/. Check it out.



标签: mongodb