How to validate schema at object level in mongoose? - node.js

I want to validate an object in mongoose schema, i.e here is my schema -
logs = new Schema({
user_id: String,
reminder_cat: String,
activityNote: String,
sent_email: Number,
added_date: Date
})
I want to validate if reminder_cat === "Renewal" then sent_email should not be more than 2 how do I achieve this at schema level. I understand there are validate function at attribute level which can be custom or provided from mongoose. But that doesn't contain the whole object so validating a dependent field is not possible. Any help or pointer will be appreciated.

Related

MongoDB check types of attributes before insert

I'm using mongoose to define the following schema:
var toySchema = new Schema({
id: {type: String, required: true, unique: true},
name: {type: String, required: true},
price: Number
});
I believed that when I inserted into the collection using db.toys.insert(), mongo would check to see that the type of each attribute matched the types in the schema, but it seems that it does not. For example, id should be a string, but I was able to insert a document with an integer id. How can I enforce the schema onto these inserts?
Actually it does work, but not as you might expect. If you specify a field in your model that is of type Date and you try to save a boolean value (e.g. true) then you will get an error like
Error [ValidationError]: MySchema validation failed: expireAt: Cast to Date failed for value "true" at path ...
The reason for your particular problem can be found in the documentation
If you pass an element that has a toString() function, Mongoose will call it, unless the element is an array or the toString() function is strictly equal to Object.prototype.toString().
You could add a custom validator

Non-existing field in Mongodb document appears in mongoose findById() result

I'm somewhat new in what is related to Mongoose and I came to this behaviour I consider as strange. The document returned by Mongoose has fields that are not present in the actual MongoDb document, and seem to be added by Mongoose based on the schema.
I use a schema similar to this (this one is simplified) :
const ProfessionalSchema = new mongoose.Schema({
product: {
details: [{
_id: false,
id: String, // UUID
name: String,
prestations: [{
_id: false,
id: String, // UUID
name: String,
price: Number,
}],
}],
},
[...]
My document as shown in Mongodb with mongo CLI utility doesn't have a product field.
What I don't understand is why the result of Professional.findById().exec() returns a document with a product:{details[]} field. I expect not to have that field in the Mongoose returned result, since it is not present in the original MongoDb document.
The Mongoose documentation found https://mongoosejs.com/docs/guide.html (Schema and Model paragraph) didn't help.
My business logic would require that field not to be present, instead of being forced by the schema. Is this achievable ?
Try taking a look at the default option. You could e.g. default your product to null and then, in your business logic, handle the "product is null" case rather than the "product field does not exist" case.
As for why this is happening, it's because you're dealing with a schema. If the field doesn't exist on the document, it's going to be auto-populated. The whole point of a schema is to ensure consistency of your document structure.

Mongoose Populate Returning null due to Schema design?

I'm stuck with mongoose populate returning null. I have a very similar situation to another question where it seems to we working just fine, perhaps with one important difference:
The model I'm referencing only exist as a subdocument to another model.
Example:
// The model i want to populate
Currency = new Schema({
code: String,
rate: Number
});
// The set of currencies are defined for each Tenant
// A currency belongs to one tenant, one tenant can have multiple currencies
Tenant = new Schema({
name: String,
currencies: [Currency]
});
Product = new Schema({
Name: String,
_currency: {type: ObjectId, ref: 'Currency'},
});
Customer = new Schema({
tenant: {type: ObjectId, ref: 'Tenant'},
products: [ Product ]
});
Then I export the models and use them in one of my routes where what I would like to do is something like
CustomerModel.find({}).populate('products._currency').exec(function(err, docs){
// docs[0].products[0]._currency is null (but has ObjectId if not usinn populate)
})
Which is returning null for any given product._currency but if I don't populate i get the correct ObjectId ref, which corresponds to an objectId of a currency embedded in a tenant.
I'm suspecting I need currencies to be stand-alone schema for this to work., Ie not just embedded in tenant, but that would mean I get a lot of schemas referencing each other.
Do you know if this is the case, or should my set-up work?
If this is the case, I guess I just have to bite the bullet and have multitude of collections referencing each other?
Any help or guidance appreciated!

how can i list mongoose schema fields and their types recursively?

i have a complex database (monogdb) structure i need to access via mongoose (node.js)
i've built schemes and models.
now - i need to be able to access the field names and field type of every schema from the client and from other places (i.e: in order to create forms, validate types, present tooltips etc)
the problem - i have many embedded types..
i there a way to do this easily?
(i've managed to iterate over the 'Schema.paths' but it some objects there have the 'instance' field, some don't, some have Schema inside etc..
exmaple for one of my sub-schemes:
var labelSpanType = {type: Number, min:0};//"description" : "a numeric type to use for Label spans",
var labelType = new Schema({
"#name": String,
"#start": labelSpanType,
"#end": labelSpanType,
"#fgColor": RGBColorType,
"#bgColor": RGBColorType,
"#itype": {type: "string",enum: ["APos","CNI","INI","DNI","INC"]},
"#feID": Number,
"#cBy": String
}, {_id:false});
var layerType = new Schema({
"label": [labelType],
"#name": String,
"#rank": orderType
},{_id:false});

is there a way to auto generate ObjectId when a mongoose Model is new'ed?

is there a way to declare a Model schema in mongoose so that when the model is new'ed the _id field would auto-generate?
for example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectIdSchema = Schema.ObjectId;
var ObjectId = mongoose.Types.ObjectId;
var PersonSchema = new Schema({
_id: ObjectIdSchema,
firstName: {type: String, default: 'N/A'},
lastName: {type: String, default: 'N/A'},
age: {type: Number, min: 1}
});
var Person = mongoose.model('Person', PersonSchema);
at first, i thought great!, i'll just do
_id: {type:ObjectIdSchema, default: new ObjectId()}
but of course that doesn't work, because new ObjectId() is only called on initialize of schema. so calling new Persion() twice creates two objects with the same _id value.
so is there a way to do it so that every time i do "new Person()" that a new ObjectId() is generated?
the reason why i'm trying to do this is because i need to know the value of the new person's _id value for further processing.
i also tried:
var person = new Person({firstName: "Joe", lastName: "Baz"});
person.save(function(err, doc, num){
console.log(doc._id);
});
even then, doc doesn't contain the ObjectId. but if i look in the database, it does contain it.
p.s. i'm using mongoose 2.7.1
p.p.s. i know i can manually create the ObjectId when creating the person as such:
var person = new Person({_id: new ObjectId(), firstName: "Joe", lastName: "Baz"});
but i rather not have to import ObjectId and have to new it every time i want to new a Person. guess i'm used to using the java driver for mongodb, where i can just create the value for the _id field in the Model constructor.
Add the auto flag:
_id: {
type: mongoose.Schema.Types.ObjectId,
index: true,
required: true,
auto: true,
}
source
the moment you call var person = new Person();
person._id should give you the id (even if it hasn't been saved yet). Just instantiating it is enough to give it an id. You can still save it after, and that will store the id as well as the rest of the person object
Instead of:
_id: {type:ObjectIdSchema, default: new ObjectId()}
You should do:
_id: {type:ObjectIdSchema, default: function () { return new ObjectId()} }
Taken from the official MongoDB Manual and Docs:
_id
A field required in every MongoDB document. The _id field must have a
unique value. You can think of the _id field as the document’s primary
key. If you create a new document without an _id field, MongoDB
automatically creates the field and assigns a unique BSON ObjectId.
Source
ObjectID
ObjectIds are small, likely unique, fast to generate, and ordered.
ObjectId values consist of 12 bytes, where the first four bytes are a
timestamp that reflect the ObjectId’s creation. Specifically:
a 4-byte value representing the seconds since the Unix epoch, a 5-byte
random value, and a 3-byte counter, starting with a random value. In
MongoDB, each document stored in a collection requires a unique _id
field that acts as a primary key. If an inserted document omits the
_id field, the MongoDB driver automatically generates an ObjectId
for the _id field.
This also applies to documents inserted through update operations with
upsert: true.
MongoDB clients should add an _id field with a unique ObjectId.
Using ObjectIds for the _id field provides the following additional
benefits: in the mongo shell, you can access the creation time of the
ObjectId, using the ObjectId.getTimestamp() method. sorting on an _id
field that stores ObjectId values is roughly equivalent to sorting by
creation time. IMPORTANT While ObjectId values should increase over
time, they are not necessarily monotonic. This is because they:
Only contain one second of temporal resolution, so ObjectId values
created within the same second do not have a guaranteed ordering, and
Are generated by clients, which may have differing system clocks.
Source
Explicitly declaring _id:
When explicitly declaring the _id field, specify the auto option:
new Schema({ _id: { type: Schema.ObjectId, auto: true }})
ObjectId only - Adds an auto-generated ObjectId default if turnOn is true.
Source
TL;DR
If you create a new document without an _id field, MongoDB automatically creates the field and assigns a unique BSON ObjectId.
This is good way to do this
in model:
const schema = new Schema({ userCurrencyId:{type: mongoose.Schema.Types.ObjectId,
index: true,
required: true,
auto: true});

Resources