Validate subdocuments in Mongoose - node.js

I am using a Schema as a Subdocument in Mongoose, but I am not able to validate it in its fields.
That's what I have
var SubdocumentSchema = new Schema({
foo: {
type: String,
trim: true,
required: true
},
bar: {
type: String,
trim: true,
required: true
}
});
var MainDocumentSchema = new Schema({
name: {
type: String,
trim: true,
required: true
},
children: {
type : [ SubdocumentSchema.schema ],
validate: arrayFieldsCannotBeBlankValidation
}
});
I want to be sure that every field of the subdocument is not empty.
I found out that this is not possible to validate an Array with standard methods, so I wrote my custom validation function.
By now I have to manually check that all the fields are correct and not empty, but it looks to me like a not really scalable solution, so I was wondering if there was some native method to trigger the Subdocument validation from the MainDocument one.

In the definition of children, it should be [SubdocumentSchema], not [SubdocumentSchema.schema]:
var MainDocumentSchema = new Schema({
name: {
type: String,
trim: true,
required: true
},
children: {
type : [ SubdocumentSchema ],
validate: arrayFieldsCannotBeBlankValidation
}
});
SubdocumentSchema.schema evaluates to undefined so in your current code Mongoose doesn't have the necessary type information to validate the elements of children.

Related

How to create a new objectID in mongoose schema?

I want to generate two different objectIds in mongoose schema. One for departureLocation and one for arrivalLocation and want to reference it later.
I imported the object id from mongoose like this
let id = new mongoose.Types.ObjectId()
now I want to generate two different object ids like I said above
const routeSchema = new mongoose.Schema(
{
location: {
departureLocation: {
name: {
ref: "Location",
type: String,
required: true,
},
//want to create new object id here,
subLocation: [String],
_id: {
type: String,
},
},
arrivalLocation: {
name: {
ref: "Location",
type: String,
required: true,
},
//want to create new object id here,
subLocation: [String],
_id: {
type: String,
},
},
},
duration: {
type: Number,
required: true,
},
busId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Bus",
required: true,
},
date: {
type: String,
required: true,
},
},
{
timestamps: true,
}
);
MongoDB will automatically create _id for an object in a schema. You do not need to create one. In this schema, there will be 4 automatically generated _ids one for location, one for departureLocation, one for arrivalLocation, and one for the overall schema.

How to use virtual on a nested field in mongoose

I am using mongoose in node.js. I have the following Schema.
const CustomerSchema = new mongoose.Schema({
...
email: {
type: String,
required: true,
lowercase: true,
trim: true
},
addresses: [
{
addressType: {
type: String,
enum: [ 'personal', 'shipping', 'billing' ]
},
street: {
type: String,
required: true,
trim: true
},
streetNumber: {
type: String,
trim: true
},
floor: {
type: String,
trim: true
},
apartament: {
type: String,
trim: true
},
cp: {
type: String,
required: true,
trim: true
},
district: {
type: String,
trim: true
},
city: {
type: mongoose.Schema.ObjectId,
ref: 'City',
required: true
}
}
]
});
I want to use "virtuals" to "add" a new field in every object in the array addresses?
How I can do this using virtuals? Is it possible?
I can achieve the same result using, but I would like to use virtuals.
const customerDB = await Customer.findById(idCustomer).lean()
customerDB.addresses = customerDB.addresses.map((address) => ({
...address,
addressDesc: mapTypeAddressDescription(address.addressType)
}));
Many Thanks!
As the name suggests virtuals are not added to the MongoDB documents. They are used for computed properties on documents.
Suppose you have a User model. Every user has an email, but you also want the email's domain. For example, the domain portion of 'test#gmail.com' is 'gmail.com'.
Below is one way to implement the domain property using a virtual. You define virtuals on a schema using the Schema#virtual() function.
const userSchema = mongoose.Schema({
email: String
});
// Create a virtual property `domain` that's computed from `email`.
userSchema.virtual('domain').get(function() {
return this.email.slice(this.email.indexOf('#') + 1);
});
const User = mongoose.model('User', userSchema);
let doc = await User.create({ email: 'test#gmail.com' });
// `domain` is now a property on User documents.
doc.domain; // 'gmail.com'
You should check the documentation for more details.
You can do something like this:
CustomerSchema.path('addresses').schema.virtual('fullAddr').get(function() {
return 'foo'
})
Also, it is useful to check this answer on stackoverflow if the above one doesn't work.

How to structure nested object model properly in mongoose

I'm trying to define the following mongoose model for a case. Strange I can not see a proper documentation about what I'm trying to do. In the end I want to have only 1 collection "Case"
which documents are nested object like this:
const CaseSchema = new Schema({
clientDataType: {
type: String,
required: true,
enum: [
'dataSchema1',
'dataSchema2',
],
},
clientData: {
type: "Either dataSchema1 or dataSchema2",
required: true,
},
caseDataType: {
type: String,
required: true,
enum: ['type1', 'type2', ...]
},
caseData: {
type: "One of 6 types of cases again they are schemas",
required: true,
}
}, { timestamps: true });
And here is mockup for dataSchema1 same goes for dataSchema2
const dataSchema1 = new Schema({
field1: {
type: String,
required: true,
},
fiedl2: {
type: String,
required: true,
},
field3: {
type: "One of subschemas of my choice",
required: true,
},
...
}, { timestamps: true });
Is mongoose capable of doing that or I must skip mongoose for this kind of case model. Basically at the end I wan't to submit this object and let mongoose validate it for me.

Find all the documents that has same subdocuments mongoose

I have postSchema which references the tagsSchema.
var tagsSchem = new Schema({
name: {
type: String,
required: true
}
}, {
timestamps: true
});
// create a schema
var postsSchema = new Schema({
title: {
type: String,
required: true,
unique: true
},
mainImage: {
type: String
},
category: {
type: String,
required: true
},
body: {
type: String,
required: true
},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
tags: [tagsSchem]
}, {
timestamps: true
});
One post can contain any no. of tags. So if a post has 3 tags then I want to get all the posts with those 3 tags without querying it multiple times. Is it possible?
When you perform find, you can use the $in option to find values that are in your array. For example:
posts.find({tags:{$in:{["tag1","tag2","tag3"]}}, function(err,data) {
... //Your code here
}
This will take all the posts that contains one of the three tags. It's important you have to pass an array in the $in option. This should work.

Mongoose - how to create portable documents with fixed type

Given the goal of having a JSON object that can be passed to a consumer where the json object contains some variation of:
{
_id: 1
name: "my_name",
type: "my_type",
my_particulars: {
value: 1,
author: "some author"
}
}
such that the "type" value is locked into the schema/model is there an established pattern for satisfying this requirement?
It seems to me that the best options is some form of:
var WidgetSchema = new Schema({
//Name
name: {type: String, required: true, unique: true},
type: {type: String, required: true, default: "widget"},
title: {type: String, required: true },
description: { type: String, required: true },
//Status 1: Not Live
//Status 2: Live
status: {type: Number, required: true, default: 1}
});
WidgetSchema.virtual('type').set(
function () {
return false;
});
Instead of actually storing the type you can add it as a virtual property which is returned with the JSON. Something like:
WidgetSchema.virtual('type').get(function () {
return 'widget';
});
With this defined, you can instruct mongoose to include virtuals in the toObject/toJSON output by passing the virtuals option.
// either directly to the method
instanceOfWidget.toJSON({virtuals: true});
// or as a default by setting the option on the schema
WidgetSchema.set('toObject', {virtuals: true});

Resources