If you cannot use a unique field as the shard key, or if you need to enforce uniqueness over multiple fields, create another collection to act as a proxy collection.
About this Task
A sharded cluster enforces a unique constraint across shards only when the unique index is on the shard key or includes the shard key as a prefix. A proxy collection works around that constraint: the proxy collection contains both a reference to the original document and the unique key, and your application writes to the proxy collection first.
When you use a proxy collection, consider the following:
Your application must catch errors when inserting documents into the proxy collection and must enforce consistency between the two collections.
If the proxy collection requires sharding, you must shard on the single field on which you want to enforce uniqueness.
Each field requires its own proxy collection. To enforce uniqueness on more than one field using sharded proxy collections, you must have one proxy collection for every field. If you create multiple unique indexes on a single proxy collection, you cannot shard the proxy collection.
This approach requires creating a unique ID for the
primary_idfield rather than letting MongoDB create it automatically on document insertion.
For the full set of constraints that apply to unique indexes on sharded collections, see Maintain Uniqueness in Sharded Clusters.
Before You Begin
Identify the field that requires a unique constraint but cannot be part of the shard key.
Confirm that no existing documents violate the uniqueness constraint for the field.
Steps
Consider a collection records that stores user information. The field email is not the shard key, but needs to be unique. The proxy collection contains the following fields:
{ "_id" : ObjectId("..."), "parent_id" : "<ID>", "email" : "<string>" }
Write to the proxy collection before the main collection.
Insert a document containing the target field and a generated unique ID into the proxy collection. If the operation is successful, insert the full document into the records collection:
records = db.getCollection('records'); proxy = db.getCollection('proxy'); var primary_id = ObjectId(); proxy.insertOne({ "_id" : primary_id, "email" : "example@example.net" }) // if: the above operation returns successfully, // then continue: records.insertOne({ "_id" : primary_id, "email": "example@example.net", // additional information... })