Reputation: 99
I am trying to save data in MongoDB. I want to store unique data when data is not null. However, I want to allow multiple null values in the unique identifier.
My sample schema:
@Schema()
export class Contact extends Document {
@Prop({ unique: true, sparse: true, require: true })
email: string;
@Prop({ default: '+1' })
countryCode: string;
@Prop({ unique: true, sparse: true })
mobile: string;
}
In this case, a mobile number is not required. User can add their contact information with or without providing a mobile number. If the user sends their mobile number that should be unique. So, I need to allow multiple null
values in the mobile
field. However, that field should be unique when the user provides any mobile number.
Empty entries seem to get the value null
so every entry without mobile
crashes with the unique
identifier.
Is there any way to solve this problem either from the database layer or the application layer?
I am using NestJS for developing my API.
Upvotes: 2
Views: 758
Reputation: 331
A unique index still does not allow multiple docs with a field of null. You need to transform your data payload by dropping the null field before you save your docs in MongoDB. A transform pipe will help you to handle this issue. Here is a transform pipe that you can use for this purpose:
@Injectable()
export class NullValidationPipe implements PipeTransform {
private isObj(obj: any): boolean {
return typeof obj === 'object' && obj !== null;
}
private dropNull(values) {
Object.keys(values).forEach((key) => {
if (!(key === 'password' || key === '_id')) {
if (this.isObj(values[key])) {
values[key] = this.dropNull(values[key]);
} else if (Array.isArray(values[key]) && values[key].length > 0) {
values[key] = values[key].map((value) => {
if (this.isObj(value)) {
value = this.dropNull(value);
}
return value;
});
} else {
if (values[key] === null || values[key] === undefined) {
delete values[key];
}
}
}
});
return values;
}
transform(values: any, metadata: ArgumentMetadata) {
const { type } = metadata;
if (type === 'param' || type === 'custom') return values;
else if (this.isObj(values) && type === 'body') {
return this.dropNull(values);
}
throw new BadRequestException('Validation failed');
}
}
Use this pipe in the controller and this pipe will drop all incoming null fields which will come with the request payload.
You can also check nest pipe transform docs: https://docs.nestjs.com/techniques/validation
Upvotes: 2