Check if a document has already been created in mongoose validator? - node.js

In one of my conditional schema validation I need to know if the current document has already
been created before returning true or false.
This is the kind of logic I'm looking for (in pseudo code):
password: {
type: String,
trim: true,
required: function () {
if (this.password == '') {
// Pseudo code:
return document.exists ? false : true;
}
}
},
How can I do that with real code and is it the correct way or should I look for a different approach ?
note: I'm aware of the exists() method but I don't know how to use it in a schema validation situation.

Related

Invalid Object Passes Schema Validation

In a meteor app I've been getting my feet wet with SimpleSchema lately. I've built the below mentioned pretty simple schema :-)
However, when I try to validate an invalidate entry (say one where entry.link.url ist not a valid URL or one where entry.link.project is undefined) against that schema via entrySchema.validate() the validation does not work properly, i.e. the invalid entry passes the validation whereas I would expect it to fail.
let entrySchema = new SimpleSchema({
userId: {
type: String,
optional: true
},
link: {
type: Object
},
'link.project': {
type: String //this validation does not work!
},
'link.url': {
type: SimpleSchema.RegEx.Url //this validation does not work!
}
});
Can anyone please tell me what I am doing wrong or what I am missing here?

Set default value to a model(Sails.js)

I am starting to learn Sails.js and I want to know if there is a simpler way to set the default value in a model from a session variable? I am using Waterlock to do authentication and setting the user_id in a session variable like req.session.user_id. I have a message model, I want to default the 'from' field to this session variable. Is there a way to do this in Sails.js?
If you are using Sail's default ORM, Waterline, then model attributes have a defaultsTo option. The supplied value may be a function. You can look at the waterline documentation.
Sample Model
module.exports = {
attributes: {
description: {
type: 'string',
defaultsTo: 'No description.'
},
email: {
type: 'email',
required: true,
unique: true
},
alias: {
type: 'string',
defaultsTo: function(){
return this.email;
}
},
}
};
If the supplied value is a function, then the call to this function is bound to the values in the create.
For Model.create(values)..., if alias is null/undefined, then alias = defaultsTo.call(values) is used to get the value for alias.
So to use req.session.user_id you may have to include it during create.
Model.create({session: req.session,...}). I am not really sure, since I do not use Waterlock. And I'm not don't think this is the best way to go about it.
From my experience these extra values are ignored when using adapters such as sails-mysql, but if you are using sails-disk for development, then you might see that these extra values are persisted. You may want to delete them before you persist.
beforeCreate: function(values, cb){
delete values['session'];
cb();
}

How to get only required fileds from Mongoose Schema?

I'm looking for a way to send a custom error message to the client when a required input is not set.
A sample Mongoose Schema:
this._schema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
}
});
I use a this._schema.pre('validate') function to test if a required input is missing and, if it is missing, pass a custom AppError to my next() function which will handle the rest.
The pre-validation:
this._schema.pre('validate', function(next) {
if(!this.email) return next(new AppError('Email not set'));
next();
});
I don't want to check for each field manually using a list of if statements. Rather I would like to loop over all required fields and throw the error if one of them is missing (loop should be recursive too for nested fields).
Question:
How can I loop over required fields and pass the first missing field to an Error?
Note: I'm not sure if this is the right way, so other interpretations are welcome! Found something here, but I'm not sure that's the correct answer.

Mongoose find with default value

I have a mongoose model: (With a field that has a default)
var MySchema= new mongoose.Schema({
name: {
type: String,
required: true
},
isClever: {
type: Boolean,
default: false
}
});
I can save a model of this type by just saving a name and in mongoDB, only name can be seen in the document (and not isClever field). That's fine because defaults happen at the mongoose level. (?)
The problem I am having then is, when trying to retrieve only people called john and isClever = false:
MySchema.find({
'name' : 'john',
'isClever': false
}).exec( function(err, person) {
// person is always null
});
It always returns null. Is this something related to how defaults work with mongoose? We can't match on a defaulted value?
According to Mongoose docs, default values are applied when the document skeleton is constructed.
When you execute a find query, it is passed to Mongo when no document is constructed yet. Mongo is not aware about defaults, so since there are no documents where isClever is explicitly true, that results in empty output.
To get your example working, it should be:
MySchema.find({
'name' : 'john',
'isClever': {
$ne: true
}
})

Mongoose Changing Schema Format

We're rapidly developing an application that's using Mongoose, and our schema's are changing often. I can't seem to figure out the proper way to update a schema for existing documents, without blowing them away and completely re-recreating them from scratch.
I came across http://mongoosejs.com/docs/api.html#schema_Schema-add, which looks to be right. There's little to no documentation on how to actually implement this, making it very hard for someone who is new to MongoDB.
I simply want to add a new field called enabled. My schema definition is:
var sweepstakesSchema = new Schema({
client_id: {
type: Schema.Types.ObjectId,
ref: 'Client',
index: true
},
name: {
type: String,
default: 'Sweepstakes',
},
design: {
images: {
type: [],
default: []
},
elements: {
type: [],
default: []
}
},
enabled: {
type: Boolean,
default: false
},
schedule: {
start: {
type: Date,
default: Date.now
},
end: {
type: Date,
default: Date.now
}
},
submissions: {
type: Number,
default: 0
}
});
Considering your Mongoose model name as sweepstakesModel,
this code would add enabled field with boolean value false to all the pre-existing documents in your collection:
db.sweepstakesModel.find( { enabled : { $exists : false } } ).forEach(
function (doc) {
doc.enabled = false;
db.sweepstakesModel.save(doc);
}
)
There's nothing built into Mongoose regarding migrating existing documents to comply with a schema change. You need to do that in your own code, as needed. In a case like the new enabled field, it's probably cleanest to write your code so that it treats a missing enabled field as if it was set to false so you don't have to touch the existing docs.
As far as the schema change itself, you just update your Schema definition as you've shown, but changes like new fields with default values will only affect new documents going forward.
I was also searching for something like migrations, but didn't find it. As an alternative you could use defaults. If a key has a default and the key doesn't exist, it will use the default.
Mongoose Defaults
Default values are applied when the document skeleton is constructed. This means that if you create a new document (new MyModel) or if you find an existing document (MyModel.findById), both will have defaults provided that a certain key is missing.
I had the exact same issue, and found that using findOneAndUpdate() rather than calling save allowed us to update the schema file, without having to delete all the old documents first.
I can post a code snippet if requested.
You might use mongo shell to update the existing documents in a specific collection
db.SweeptakesModel.update({}, {$set: {"enabled": false}}, {upsert:false, multi:true})
I had a similar requirement of having to add to an existing schema when building an app with Node, and only found this (long ago posted) query to help.
The schema I added to by introducing the line in the original description of the schema and then running something similar to the following line, just the once, to update existing records:
myModelObject.updateMany( { enabled : { $exists : false } }, { enabled : false } )
'updateMany' being the function I wanted to mention here.
just addition to what Vickar was suggesting, here Mongoose Example written on Javascript (Nodejs):
const mongoose = require('mongoose');
const SweeptakesModel = mongoose.model(Constants.SWEEPTAKES,sweepstakesSchema);
SweeptakesModel.find( { enabled : { $exists : false } }).then(
function(doc){
doc.enabled = false;
doc.save();
}
)

Resources