5

I have a MongoDB schema for users that looks something like this:

{
  userId: "some-string",
  anonymousId: "some-other-string",
  project: {"$oid": "56d06bb6d9f75035956fa7ba"}
}

Users must have either a userId or an anonymousId. As users belong to a project, the model also has a reference called project, which links to the project collection.

Any userId or anonymousId value has to be unique per project, so I created two compound indexes as follows:

db.users.createIndex({ "userId": 1, "project": 1 }, { unique: true })
db.users.createIndex({ "anonymousId": 1, "project": 1 }, { unique: true })

However as not both userId and anonymousId have to be provided but just either one of them, MongoDB throws a duplicate key error for null values (for example if there is a second user with a provided anonymousId but no userId).

I therefore tried to add a sparse: true flag to the compound indexes, but this obviously only works if both fields are empty. I also tried adding the sparse flag only to the fields and not the compound indexes, but this doesn't work either.

To give an example, let's say I have the following three users in the collection:

{ userId: "user1", anonymousId: null, project: {"$oid": "56d06bb6d9f75035956fa7ba"}}

{ userId: "user2", anonymousId: "anonym", project: {"$oid": "56d06bb6d9f75035956fa7ba"}}

{ userId: "user3", anonymousId: "random", project: {"$oid": "56d06bb6d9f75035956fa7ba"}}

The following should be possible:

  • I want to be able to insert another user {userId: "user4", anonymousId: null} for the same project (without getting a duplicate key error)
  • However if I try to insert another user with {userId: "user3"} or another user with {anonymousId: "random"} there should be a duplicate key error

How else can I achieve this?

3
  • What version of MongoDB are you using? Commented Mar 3, 2016 at 11:26
  • I'm using 3.0.9. Are you asking because of partial indexes? Commented Mar 3, 2016 at 11:30
  • Indeed. This is intention and will solve lot of your problems. I'll suggest to upgrade database engine if possible. Commented Mar 3, 2016 at 11:34

2 Answers 2

5

If you are using MongoDB 3.2, you can use unique partial index instead of sparse index.

Partial index is actually recommended over sparse index

Example

db.users.createIndex({ "userId": 1, "project": 1 }, 
{ unique: true, partialFilterExpression:{ 
  userId: { $exists: true, $gt : { $type : 10 } } } })

db.users.createIndex({ "anonymousId": 1, "project": 1 }, 
{ unique: true, partialFilterExpression:{ 
  anonymouseId: { $exists: true, $gt : { $type : 10 } } } })

In above example, Unique index will only be created when userId is present and doesn't contain null value. Same holds true to anonymousId too.

Please see https://docs.mongodb.org/manual/core/index-unique/#unique-partial-indexes

Sign up to request clarification or add additional context in comments.

11 Comments

Yes I know, but unfortunately I have to use version 3.0.9. However, how would you do this using a partial index?
Well in That case you have only one option which I see profesor79 posted above. Create compound index on all three fields.
Could you edit your answer to give an example how to create a unique partial index in that case?
sure, can you please update your question and post sample documents covering case you are trying to solve.
imho partial index will have the same weakness in this case - as there is a business logic needed to handle multiple annon ids and then create an real user id
|
1

index a,c - cannot be sparse as is unique.....

index b,c - cannot be sparse as is unique.....

what about index a,b,c ?

db.benjiman.insert( { userId: "some-string", anonymousId: "some-other-string", project: {"_oid": "56d06bb6d9f75035956fa7ba"} })

db.benjiman.insert( { userId: "some-string2", project: {"_oid": "56d06bb6d9f75035956fa7ba"} })

db.benjiman.insert( { anonymousId: "some-other-string2", project: {"_oid": "56d06bb6d9f75035956fa7ba"} })

db.benjiman.createIndex({ "userId": 1, "anonymousId": 1, "project": 1 }, { unique: true })

2 Comments

I thought about this too, but the problem with this approach is that it allows to insert the same userId for example if there is a different anonymousId provided.
ok, have a bigger image now, let's chat here chat.stackoverflow.com/rooms/104903/mongo-queries-issues

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.