mongoose index needs to be unique, not required to take a null or '' value

97 Views Asked by At

a user is supposed to be able to create a booking and choose the way the company will contact them - email or phone or whatsapp, I do not want anyone spamming with bookings so these are unique values, we do not know which contact a user is going to provide so we need to be able to take a null as a value and still not give a "duplicate key" error

I tried an example from here - it allows null as a unique value but also a duplicate value so it is not a solution - my schema:

const BookingSchema = new mongoose.Schema({
  fullName: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  email: {
    type: String,
    trim: true,
    index: {
      unique: true,
      partialFilterExpression: {email: {$type: 'string'}},
    },
    set: v => (v === '' ? null : v),
  },
  phone: {
    type: String,
    trim: true,
    index: {
      unique: true,
      partialFilterExpression: {phone: {$type: 'string'}},
    },
    set: v => (v === '' ? null : v),
  },
  whatsapp: {
    type: String,
    trim: true,
    index: {
      unique: true,
      partialFilterExpression: {whatsapp: {$type: 'string'}},
    },
    set: v => (v === '' ? null : v),
  },})
1

There are 1 best solutions below

0
Elena On

i found the solution, this particular schema will let keep just documents with unique fields and in same time will let keep documents where this field has value: null, value: "", so the solution is you ned use index and describe it for every field which you want be this way, also use middleware helps to keep null and empty values

const BookingSchema = new mongoose.Schema({
    fullName: {
        type: String,
        required: true
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
    message: {
        type: String
    },
    status: {
        type: Boolean,
        required: true,
        default: false
    },
    email: {
        type: String,
        unique: true,
        sparse: true,
    },
    phone: {
        type: String,
        nique: true,
        sparse: true,
    },
    whatsapp: {
        type: String,
        unique: true,
        sparse: true,
    },
    messenger: {
        type: String,
        trim: true
    }
})

BookingSchema.index(
    { email: 1 },
    { unique: true, partialFilterExpression: { email: { $exists: true } } }
)

BookingSchema.index(
    { phone: 1 },
    { unique: true, partialFilterExpression: { phone: { $exists: true } } }
)

BookingSchema.index(
    { whatsapp: 1 },
    { unique: true, partialFilterExpression: { whatsapp: { $exists: true } } }
)

BookingSchema.pre('save', function(next) {
    if (this.email === null || this.email === "") {
        this.email = undefined;
    }
    if (this.phone === null || this.phone === "") {
        this.phone = undefined;
    }
    if (this.whatsapp === null || this.whatsapp === "") {
        this.whatsapp = undefined;
    }
    next()
})