Validating Mongoose Mixed schema type - mongodb

I have a schema:
// Schema
var Product = new Schema({
data: {
type: mongoose.Schema.Types.Mixed
},
created: {
type: Date,
'default' : Date.now
}
});
The 'data' field is used to store a json string which will vary. I do however want to perform some basic validation such as length etc.. However doing this:
// Validation
Product.path('data').validate(function (value) {
console.log(value);
return value.length > 0;
}, 'Data cannot be blank');
Throws an error about data not existing:
TypeError: Cannot read property 'length' of undefined
What is the best way to do this?

You are treating "value" as an object without checking if it really is. Try with this:
if(typeof value !== "undefined" && value !== null)
{
return value.length > 0
}

Related

How to create a mongoose schema that makes sure that user provide only one subcategory?

I am trying to create a product for an e-commerce website. I want to make sure that the user provides only one sub category for the product. But the way I have implemented my schema user can create product even without providing any sub category and I want the user to select only one sub category. My current schema is like this:
category: {
type: String,
required: true,
enum: {
values: [
"men",
"women",
"kids",
]
}
},
subCategory: {
men: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
},
women: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
}
kids: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
}
}
But right now even if I don't provide any subCategory the product is still created. Please help me out with this one.
I used pre validate middleware in mongoose to solve this.
productSchema.pre("validate", function (next) {
if (!this.subCategory.men && !this.subCategory.women && !this.subCategory.kids) {
return next(new Error("Please provide atleast one value for sub category"));
}
if (
(this.subCategory.men && this.subCategory.women && this.subCategory.kids) ||
(this.subCategory.women && this.subCategory.kids) ||
(this.subCategory.men && this.subCategory.kids) ||
(this.subCategory.women && this.subCategory.men)
) {
return next(new Error("Please provide only one value for sub category"));
}
next();
});

MongoDB field only accepts 3 special values

slider_value: {
type: Number,
required: false,
},
This is the Mongoose schema for one of the fields in my MongoDB model.
It may only accept the integer values of 1, 4, and 10.
How can this validator be specified in the schema?
If you only need to store either one of these three values, storing them as a string, and validating using the enum key would be reasonable. For example that could look like this:
{
slider_value: {
type: String,
enum: ["1", "4", "10"],
},
}
Alternatively, if it is a requirement to store them in form of an int, you could use a custom validator to check a value before it's saved. That would look like this:
{
slider_value: {
type: Number,
validate: {
validator: value => value === 1 || value === 4 || value === 10,
message: props => `${props.value} is invalid for slider_value`,
},
},
}
For more details on custom validators and validation in mongoose in generell, here are the mongoose validation docs.

Mongoose Schema planning: using ObjectID reference or using array of type: [otherSchema]?

I'm currently planning out the database structure for an app I'm building, and something in this linked answer raised some questions for me.
In the structure that Shivam proposed, he sometimes references another collection directly, in other words within one Schema he defines the type of a field to be an array of another schema type. Example:
import { Schema } from "mongoose";
import { QuestionSchema } from "./question-schema";
const mongoose = require('mongoose');
export const QuestionSetSchema: Schema = new Schema({
questionSet: {
type: [QuestionSchema],
validate: {
validator: function(value: any) {
return value.length === 12;
},
message: 'Question set must be 12.'
}
},
}, {
timestamps: true
});
In other cases, he only uses an ObjectID reference to another schema/collection:
export const CandidateSchema: Schema = new Schema({
name: String,
email: String, // you can store other candidate related information here.
totalAttempt: {
type: Number,
default: 0,
validate: {
validator: function(value: number) {
return value === 3;
},
message: 'You have already done three attempts.'
}
},
candidateQuestionAnswers: {
type: [Schema.Types.ObjectId],
ref: 'CandidateQuesAnswer'
}
}, {
timestamps: true
});
What are the use cases for each of the above? Does the "type:[otherSchema]" method actually embed instances of that collection or does it only provide their properties to the Schema they are called from?

Sails Waterline Model attributes Validation type 'integer', 'float' fails

My Model Attributes
per: {
type: 'float',
required: true
},
typeid: {
type: 'integer',
required: true
},
My input
{
per: '5GH',
typeid: '6SD',
}
I expect this should fail and will get error message something like
typeid:
[ { rule: 'integer',
message: 'undefined should be a integer
But on validation the o/p after validation
{
per: 5,
typeid: 6,
}
Do we need to manually validate integer and float in this case?
Official Sails Doc for Validation
As in documentation you can see that integer validation check for integer as well as string for validation.
According to what i have experienced with validation
For strictly validate use int,decimal in place of integer,float
problem with your scene is as follow.
a=5 =>integer in a
a="5" =>string but the integer value is 5
a="5a4" =>string but integer value is 5 not 54
a="a5" =>string and no integer value.Only this case will fail on validation rule
If you want strictly validate the attributes according to you custom rule then you can add custom validation rule in your models.See the code below:
module.exports = {
attributes: {
name: {
type:'string'
},
mail: {
type:'string',
defaultsTo:'a'
},
age:{
type:'string',
isInt:true
}
},
types:{
isInt:function(value){
console.log(value);
if(typeof value==='number' && value=== Math.floor(value)){
console.log('value is a number');
return true;
}
if(!isNaN(1*value)){
console.log(value);
return true;
}
return false;
}
}
};
So for each model you need to write custom validator.
And
i guess there is now way currently to write global custom validation
rule so that you could apply your validation on attributes of different models by writing validation
globally.

How can I validate a model attribute against another model attribute in Sails?

Let's say I have an Invoice model in SailsJS. It has 2 date attributes: issuedAt and dueAt. How can I create a custom validation rule that check that the due date is equal or greater than the issued date?
I tried creating a custom rule, but it seems I cannot access other properties inside a rule.
module.exports = {
schema: true,
types: {
duedate: function(dueAt) {
return dueAt >= this.issuedAt // Doesn't work, "this" refers to the function, not the model instance
}
},
attributes: {
issuedAt: {
type: 'date'
},
dueAt: {
type: 'date',
duedate: true
}
}
};
I hope you found a solution now, but for those interested to a good way to handle this i will explain my way to do it.
Unfortunatly as you said you can't access others record attributes in attribute customs validation function.
#Paweł Wszoła give you the right direction and here is a complete solution working for Sails#1.0.2 :
// Get buildUsageError to construct waterline usage error
const buildUsageError = require('waterline/lib/waterline/utils/query/private/build-usage-error');
module.exports = {
schema: true,
attributes: {
issuedAt: {
type: 'ref',
columnType: 'timestamp'
},
dueAt: {
type: 'ref',
columnType: 'timestamp'
}
},
beforeCreate: (record, next) => {
// This function is called before record creation so if callback method "next" is called with an attribute the creation will be canceled and the error will be returned
if(record.dueAt >= record.issuedAt){
return next(buildUsageError('E_INVALID_NEW_RECORD', 'issuedAt date must be equal or greater than dueAt date', 'invoice'))
}
next();
}
};
beforeCreate method in model as first param takes values. The best place for this kind of validation I see here.
beforeCreate: (values, next){
if (values.dueAt >= values.issuedAt) {
return next({error: ['...']})
}
next()
}