Ignore case in enum values - node.js

I have created one mongoose schema as below:
const quesSchema = new mongoose.Schema({
acRate: {
type: Number,
},
difficulty: {
type: String,
enum: {
values: ['EASY', 'MEDIUM', 'HARD'],
message: 'Difficulty should be either EASY, MEDIUM or HARD',
},
},
title: {
type: String,
},
titleSlug: {
type: String,
},
topicTags: [
{
name: {
type: String,
},
},
],
});
In the data, the difficulty may occur in any case. I could not figure out how to ignore the case for the enum easily. I can make a custom validator that will make the input value lowercase/uppercase. But is there any other solution to it?

Yes, you need to add lowercase: true in your schema.
Reference

Related

Mongoose: How to validate length of array based on another documents variable

I am trying to do validation within the schema to validate the length of an array based on another documents length of its array. Suppose:
const Level = mongoose.Schema({
level: {
type: Array,
required: true,
items: {
exercise: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exercise",
required: true,
},
accuracyThreshold: { type: Number, required: true },
timeThreshold: { type: Number, required: true },
},
},
});
const UserLevelProgress = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
level: {
type: mongoose.Schema.Types.ObjectId,
ref: "Level",
required: true,
},
progress: {
type: Array,
required: true,
validate: {
validator: (progress) => {
return progress.length === level.level.length; // level.level.length is not valid
},
message: () => "level progress legnth is invalid!",
},
items: {
accuracyScore: { type: Number, required: true },
timeScore: { type: Number, required: true },
},
},
});
module.exports = mongoose.model("UserLevelProgress", UserLevelProgress);
module.exports = mongoose.model("Level", Level);
I have a validator function in UserLevelProgress, but obviously level is not valid object. How do I get access to this object?
The short answer is: You could implement this validation logic, but is it really a good idea to do this?
Downsights
validation is taking place in the pre('save') hook. This means that once you initially save of update a document, the validation will run.
since Level documents are stored in a different collection, the values (including length of the array) can change without UserLevelProgress documents noticing. This way your validator would not rerun (as said before it's run in pre('save') hook and your documents would technically be not valid anymore.
Better approach
Check if Level schema can be included as a nested document, instead of a referenced one. This way validation would be much easier and data will not be able to become invalid unnoticed.
Implementation if you still want to follow your intended approach
validate: {
validator: async function(progress) {
const level = await Level.findById(this.level);
return progress.length === level.level.length; // level.level.length is not valid
},
message: () => "level progress legnth is invalid!",
},

mongoose model multiple key types

I have a simple schema as
const xpReward = new mongoose.Schema({
...,
receivedFor: {
type: Object,
required: true
}
});
Received for is an object which can have 2 keys and the values can be something like { "articleId": 5} or { "testId": 7}
I want to expand this receivedFor object with an required field, but keeping the possibility to add articleId or testId.
I know I can let it as in example above, because is an object and can have any form, but I want to specify the type there, to can be known in further usage.
I'm thinking at something like:
const xpReward = new mongoose.Schema({
...,
receivedFor: {
learningPath: {
type: String,
required: true
},
articleId | testId: {
type: Number,
required: true
}
}
});
I don't want to use another nested object as
receivedFor: {
learningPath: {
type: String,
required: true
},
// in this example, the old receivedFor will become this valueFor
// this will be just another nested level, one more parent for it
valueFor: {
type: Object,
required: true
}
}
Can be this done somehow better? thx
Would likely need some hooks to go along with this, but this is the basic idea.
const xpReward = new mongoose.Schema({
...,
receivedFor: {
learningPath: {
type: String,
required: true
},
foreignId: {
type: Number,
required: true
},
foreignCollection: { // maybe even make it an enum - articleId | testId
type: String,
required: true
}
}
});

Error in getting referenced objects in admin-bro Nodejs (There are no resources with given id)

So the error I am getting is There are no resources with given id: "workshop.timelines"\nThis is the list of all registered resources you can use.
This is the resource I am trying to visualize in adminbro
const LearnerSchema = new mongoose.Schema(
{
workshops: [
{
workshop: {
workshop_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'workshop',
},
code: {
type: String,
},
timeline: {
type: mongoose.Schema.Types.ObjectId,
ref: 'workshop.timelines',
},
},
],
{ timestamps: true }
);
This is the workshop model:
const WorkshopSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
description: {
type: String,
},
timelines: [
{
group: {
type: Number,
},
city: {
type: mongoose.Schema.Types.ObjectId,
ref: 'city',
},
description: {
type: String,
},
venue: {
type: mongoose.Schema.Types.ObjectId,
ref: 'venue',
},
month: {
type: String,
},
start: {
type: Date,
},
end: {
type: Date,
},
registration_start: {
type: Date,
},
registration_end: {
type: Date,
},
registrations: {
type: Number,
default: 0,
},
registrations_cancelled: {
type: Number,
default: 0,
},
d_reg: {
type: Number,
default: 0,
},
classLink: {
type: String,
default: '',
},
status: {
type: String,
default: 'E',
},
resources: {
_id: false,
videoSessions: { type: Boolean, default: false },
},
},
],
status: {
type: String,
enum: ['NEW', 'F', 'DISABLED'], //f = FEATURED
default: 'NEW',
},
},
{ timestamps: true }
);
WorkshopSchema.index({ name: 'text', description: 'text' });
module.exports = Workshop = mongoose.model('workshop', WorkshopSchema);
Now, I have added both of these resources among others in my adminbro options, but when adminbro tries to fetch some records from the Collection, it fails with the error:
There are no resources with given id: "workshop.timelines"\nThis is the list of all registered resources you can use.
One more thing that might be affecting this issue is that in MongoDB the value of timelines in the workshop object is a mix of ObjectId and string, however I have tried converting all the object values to ObjectId but it still shows the same error.
Would really appreciate any help here.
had the same issue.
Basically, any ref model that appears in the models that are included in the resources array has to be included as well.
In your case, I will suggest to have a look at the ResourceOptions (https://adminbro.com/ResourceOptions.html) to see if you can included the nested property
comment out you're every (type: mongoose.Schema.Types.ObjectId,) to like this ↓↓ or you can delete it as well.
// type: mongoose.Schema.Types.ObjectId,
and it will work fine.
This is not working because by default, Mongoose adds an _id property to your schemas and you are explicitly defining it so need to explicitly insert it while creating but in AdminBro Dashboard you do not have that kind of option to add _id while creating any new Object. So, for that reason comment every _id generating field.

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.

Make a limit in a sub array of a MongoDB document

I have a MongoDB collection that allows to store videos rankings, here is the schema:
var ChallengeVideoRankingSchema = new Schema({
_challenge: {
type: ObjectId,
ref: 'Challenge',
},
since: {
type: String,
enum: ['all', 'day', 'week', 'month']
},
type: {
type: String,
enum: ['won_duels', 'diamonds']
},
total: {
type: Number
},
challengeVideos: [
{
rank: {
type: Number
},
_challengeVideo: {
type: ObjectId,
ref: 'ChallengeVideo'
}
}
],
creation_date: {
type: Date,
default: Date.now
},
});
I would like to make a request with mongoose which allows me to retrieve some of the ChallengeVideo objects in one of the rankings to make a pagination. I would like for example to have in the field "challengeVideos", 20 objects representing the ranks 1-20, 21-40, 41-60, ... according to a given parameter.
Thank you in advance,
Bastien
Get last elements with slice
db.posts.find( {}, { comments: { $slice: -3 } } )
link https://www.mongodb.com/docs/manual/reference/operator/projection/slice/#return-an-array-with-its-last-3-elements

Resources