TL; dr
Yes, it is possible to have several documents with a field equal to null or not defined, while using unique "actual" values.
requirements :
- MongoDB v3. 2+.
- Knowing your specific value types in advance (for example, always
string or object when not null ).
If you are not interested in the details, feel free to skip to the implementation section.
long version
In addition to @Nolan's answer, starting with MongoDB v3.2, you can use a partial unique index with a filter expression.
A partial filter expression has limitations. This may include only the following:
- equality expressions (i.e. field: value or use of the
$eq operator), $exists: true expression,- expressions
$gt , $gte , $lt , $lte , $type expressions,$and top-level operator
This means that the trivial expression {"yourField"{$ne: null}} cannot be used.
However, assuming your field always uses the same type , you can use the expression $type .
{ field: { $type: <BSON type number> | <String alias> } }
MongoDB v3.6 adds support for specifying several possible types that can be passed as an array:
{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }
This means that it allows a value to have any of a variety of types when it is not null .
Therefore, if we want to allow the email field in the example below to accept either string or, say, binary data , binary data values, the corresponding $type expression will look like:
{email: {$type: ["string", "binData"]}}
implementation
mongoose
You can specify this in the mongoose pattern:
const UsersSchema = new Schema({ name: {type: String, trim: true, index: true, required: true}, email: { type: String, trim: true, index: { unique: true, partialFilterExpression: {email: {$type: "string"}} } } });
or directly add it to the collection (which uses the node.js native driver):
User.collection.createIndex("email", { unique: true, partialFilterExpression: { "email": { $type: "string" } } });
native driver mongodb
using collection.createIndex
db.collection('users').createIndex({ "email": 1 }, { unique: true, partialFilterExpression: { "email": { $type: "string" } } }, function (err, results) {
Mongodb shell
using db.collection.createIndex :
db.users.createIndex({ "email": 1 }, { unique: true, partialFilterExpression: { "email": {$type: "string"} } })
This will allow you to insert multiple entries with a null email address or no email field at all, but not with the same email string.