MongoDB: How to add UNIQUE constraint to a field (with examples)

Updated: February 2, 2024 By: Guest Contributor Post a comment

Introduction

MongoDB is a flexible NoSQL database that allows developers to store semi-structured data. By default, MongoDB does not impose constraints on the uniqueness of fields, except for the _id field, which is automatically unique. However, there are cases when you may want to ensure that a field or a combination of fields is unique across documents in a collection.

Understanding Uniquest Constraints in MongoDB

In MongoDB, a unique constraint can be applied to a field using an index. Creating a unique index on a field will prevent the insertion or updating of a document if it results in a duplicate value for that index. The unique index guarantees that the indexed fields do not store duplicate values.

Creating a Unique Index

db.collection.createIndex({"email": 1}, {unique: true});

The above command creates a unique index on the email field in the collection. This will prevent two documents from having the same email.

Adding a Unique Constraint to an Existing Collection

If you already have a collection and you want to add a unique constraint to one of its fields, you can still create a unique index. However, it’s essential to make sure that there are no duplicate values for the field you are indexing, as indexing will fail if duplicates are found.

For instance, to add a unique constraint to the username field:

db.users.createIndex({"username": 1}, {unique: true});

Before running this command, you would want to remove or modify any duplicate usernames.

Handling Duplicates Before Creating a Unique Index

You can find and remove duplicates using a script or aggregation pipeline, depending on the complexity of your duplicates. A basic example to find duplicates for the username field could be:

db.users.aggregate([
    { $group: {
        _id: { username: "$username" },
        uniqueIds: { $addToSet: "$_id" },
        count: { $sum: 1 }
    }},
    { $match: {
        count: { $gt: 1 }
    }}
]).forEach(function(doc) {
    doc.uniqueIds.pop(); // Keep one document
    db.users.remove({ _id: { $in: doc.uniqueIds }});
});

This aggregation pipeline groups the documents by the username field, then filters the groups to only those with more than one document. Then, for each group, it keeps one document and removes the others.

Working with Compound Unique Indexes

There are situations where a unique constraint should be based on a combination of fields. MongoDB allows for the creation of compound indexes that are unique.

db.orders.createIndex(
  { "customerId": 1, "orderId": 1 },
  { unique: true }
);

This command creates a unique index on a compound key that includes both the customerId and orderId fields, enforcing unique combinations of these fields across documents.

Error Handling When Violating Unique Constraints

When an operation violates a unique constraint, MongoDB will raise a duplicate key error. It’s important to handle these errors in your application code, typically by providing a user-friendly message, logging the error, and/or retrying the operation with different values.

Dealing with Unique Indexes in Sharded Collections

Creating unique indexes on sharded collections has special considerations. The unique index key must be either the shard key itself or contain it as a prefix. Without satisfying this requirement, you cannot create a unique index on a sharded collection.

db.shardedCollection.createIndex(
  { "shardKey": 1, "uniqueField": 1 },
  { unique: true }
);

This is an example of creating a unique index on a sharded collection that includes the shard key and another field.

Advanced Scenarios: Partial and Sparse Unique Indexes

There are cases when unique constraints should apply only to documents meeting certain criteria. MongoDB’s partial indexes can enforce uniqueness only for documents that match a specified filter expression.

db.products.createIndex(
    {"productCode": 1},
    {
        unique: true,
        partialFilterExpression: {
            quantity: { $gt: 0 }
        }
    }
);

This index enforces uniqueness for the productCode field, but only for documents where the quantity is greater than 0.

Sparse Indexes

Sparse indexes only index documents that have the indexed field. It’s a way to enforce uniqueness on fields that may not always be present.

db.customers.createIndex(
  { "discountCode": 1 },
  { unique: true, sparse: true }
);

This creates a unique sparse index on the discountCode field, which will enforce uniqueness on documents where discountCode exists.

Synergizing with Application Logic

Beyond database constraints, ensuring uniqueness often involves application-level checks in your code. Before inserting or updating data, querying for the existence of a value may be prudent to provide a better user experience.

if (db.collection.findOne({email: newUserEmail})) {
    // handle error due to email already existing
} else {
    // proceed with inserting a new document
}

This check helps to mitigate race conditions that can still occur when relying solely on database uniqueness constraints, especially in heavy load scenarios.

Conclusion

In summary, MongoDB provides various methods to enforce unique constraints through unique indexes, compound unique indexes, and partial or sparse indexes. These constraints are essential tools for maintaining data integrity and preventing duplicates, contributing to the overall stability and reliability of applications using MongoDB.