Validation for findOneAndUpdate doesn't have access to "this" values - javascript

I've got an Express endpoint that does the following:
router.put('/:appointmentId', async (req, res) => {
try {
let appointment = await Appointment.findOneAndUpdate(
{ _id: req.params.appointmentId },
{
member_id: req.body.appointment.memberId,
client_id: req.body.appointment.clientId,
address_id: req.body.appointment.addressId,
unit_id: req.body.appointment.unitId,
dateAndTime: req.body.appointment.dateAndTime
},
{
new: true,
runValidators: true,
context: 'query'
}
);
res.send(appointment);
} catch (err) {
res.send(err);
}
});
Appointment is a Mongoose model. This model has one of the following path validations:
AppointmentSchema.path("unit_id").validate(async function (unit_id) {
let unit = await Unit.findById(unit_id);
if ((unit === null) || !unit.address_id.equals(this.address_id)) {
return false;
}
return true;
}, "unit_id is not valid");
The Appointment schema looks like this:
var AppointmentSchema = new mongoose.Schema(
{
member_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Member',
required: true
},
client_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Client',
required: true
},
address_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Address',
required: true
},
unit_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Unit',
required: true
},
dateAndTime: {
type: Date,
required: true
}
}
);
When I hit the Express endpoint, this.address_id is undefined and my validations don't work properly. It's my understanding that if I set runValidators to true and context to 'query' that I'd have this access.
What am I doing wrong?
As a side-note, I also notice that Express is returning a 200 status when validation fails. This is curious.
Thanks.

Related

I can't figure out how to populate dynamic reference in mongoose

This is my user model code. I am referencing the favorites using dynamic referencing as there are three types of posts that can be added to favorites
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
contact:{
type:Number,
required: true,
unique:true
},
cnic: {
type: Number,
required: true,
unique: true
},
favorites: [
{
postType: {
type: String
},
postId: {
type: mongoose.Schema.Types.ObjectId,
refPath: 'postType'
},
}
],
itemsRented: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rent',
}
],
itemsRentedOut: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rent',
}
],
collectionRequestsSent: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'CommunityService',
}
],
itemsCollected: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'CommunityService',
}
],
servicesOrdered: [
{
type:mongoose.Schema.Types.ObjectId,
ref: 'Services'
}
],
paymentDetails: {
card: { type: Number },
cvc: { type: Number },
name: { type: String },
email: {type: String },
expiryDate: {
month: { type: Number },
year: { type: Number }
},
},
address: { type: String },
isDisputeResolutionStaff: {
type: String,
default: false
}
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
next()
}
const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
})
const User = mongoose.model('User', userSchema)
export default User
This is my controller. I want to get the posts' details added to favorites by using populate but when I run the code it returns the user object not the post details
const getFavorites = asyncHandler(async(req,res) => {
await User.
findById("61b51adfb7b8a64fd87420d3").
populate("favorites").
exec(function (err, story) {
if (err) throw new Error(err);
console.log(story);
});
})

Mongoose getting the error: MissingSchemaError: Schema hasn't been registered for model

I am getting this error and can't figure it out. I HAVE NAMED THE REF EXACLTY AS THE MODEL:
MissingSchemaError: Schema hasn't been registered for model "ParticipantStatus".
Here are my models:
ParticipantStatus model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const participantStatusSchema = new Schema({
_id: {
type: Number,
required: true,
},
name: {
type: String,
required: true,
},
});
module.exports = mongoose.model('ParticipantStatus', participantStatusSchema);
EventParticipant model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const eventParticipantSchema = new Schema(
{
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
eventId: {
type: Schema.Types.ObjectId,
ref: 'Event',
required: true,
},
teamId: {
type: Schema.Types.ObjectId,
ref: 'Team',
},
statusId: {
type: Number,
ref: 'ParticipantStatus',
required: true,
},
isActive: {
type: Boolean,
required: true,
default: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('EventParticipant', eventParticipantSchema);
This is the code where the error is thrown, when i try to get event participants and populate statusId(ParticipantStatus):
let participants = await EventParticipant.find(
{
eventId: req.params.eventId,
isActive: true,
},
{ _id: false }
)
.populate('userId', 'email')
.populate('statusId')
.select('userId');
I have been stuck for hours on this. Only the .populate('statusId') part throws the error, the rest works well. Thanks in advance
type of field that you want to populate based on should be ObjectId so change the type of statusId from Number to Schema.Types.ObjectId like this:
statusId: {
type: Schema.Types.ObjectId,
ref: 'ParticipantStatus',
required: true,
}
Well the problem was that u need to import:
const ParticipantStatus = require('../models/participantStatus.model');
even if you do not REFERENCE IT DIRECTLY in your code, which is really strange in my opinion.
Hope this helps anyone.

ExpressJS - can't get data on update User

I want to create an experience array in User model with new data, and the problem is that I don't get saved data in exec function so I can push new data in array on frontend. This is what I got so far.
router.post('/:username/experience', function(req, res) {
const username = req.params.username;
User.findOneAndUpdate(
username, {
$push: {
experience: req.body
}
}, {
safe: true,
upsert: true
})
.exec(function (err, data) {
console.log(data, "------>");
});
})
This is my schema for experience, which is called in User model like experience: [ExperienceSchema].
const ExperienceSchema = new Schema({
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
from: {
type: Date,
},
to: {
type: Date,
},
workingNow: {
type: Boolean,
default: false
},
description: {
type: String
}
}, {
usePushEach: true
})
Since findOneAndUpdate returns the original document (state before update) you need to add new: true to the options in order to get the updated document.
options:
{
safe: true,
upsert: true,
new: true
}

logic needed to post the data using if condition in node js (validation needed)

This is my existing schema,
const mongoose = require('mongoose');
const position = mongoose.Schema({
startIndex: { type: Number, required: true },
endIndex: { type: Number, required: false },
});
const Column = mongoose.Schema({
name: { type: String, required: true },
type: { type: String, enum: ["Description","ID","Time"], required: true },
positions: [position],
dataType: { type: String, enum: ["int", "float", "Double","String"], required: true },
oldName: {type:String, required: false }
});
const enrichedEventSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
name: { type: String, required: true,unique: true },
DataSourceID: { type: String, required: false},
delimiter: { type: String, required: true },
format: {type:String, enum:["JSON","DELIMITED","FixedWidth","LOG"], required: true},
columns:[Column]
});
module.exports = mongoose.model('EnrichedEvent', enrichedEventSchema);
Api for Post route:
router.post("/:projectId/events/enriched", (req, res, next) => {
const enrichedEvent = new EnrichedEvent({
_id: mongoose.Types.ObjectId(),
name: req.body.name,
projectId: req.params.projectId, //taking from url
delimiter: req.body.delimiter,
format: req.body.format,
columns:req.body.columns,
});
return enrichedEvent.save()
.then(result => {
res.status(201).json({
message: "Event stored",
createdEvent: {
_id: result._id,
projectId: result.projectId,
name: result.name,
type: result.type,
delimiter: result.delimiter,
columns:result.columns,
format:result.format
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
well my requirement is that
All EnrichedEvents Should have at least 2 Columns defined one with
Type = ID,
Type = TIME.
if FORMAT = DELIMITED then data must have Delimiter Field; ALL Columns should have a mandatory Starting Index.
if FORMAT = FIXEDWIDTH then data must have all Columns should have mandatory Starting and Ending Index.
all i could come up with is using if condition, something like this.
if ( req.body.format == 'DELIMITED' )
{
//then how to use my schema in my code??
}
I m stuck in this for 3 days, not getting logic. Any help would be appreciated.
Try making two different schemes.
const positionDel = mongoose.Schema({
startIndex: { type: Number, required: true },
endIndex: { type: Number, required: false },
});
const positionFixed = mongoose.Schema({
startIndex: { type: Number, required: true },
endIndex: { type: Number, required: true},
});
router.post("/:projectId/events/enriched", (req, res, next) => {
const enrichedEvent = new EnrichedEvent({
_id: mongoose.Types.ObjectId(),
name: req.body.name,
projectId: req.params.projectId, //taking from url
delimiter: req.body.delimiter,
format: req.body.format,
columns:req.body.columns,
});
if(delimiter=='DELIMITED'){
return enrichedEvent_SchemaForDELIMITED.save()
.then(result=>{
//use the result.
})
.catch(err=>{
// error thrown
})
}
else if(delimiter=="FIXEDWIDTH"){
return enrichedEvent_SchemaForFIXED.save()
.then(result=>{
//use the result.
})
.catch(err=>{
// error thrown
})
}
});
Look at the code. The logic will be like this.

Manipulating Mongoose/MongoDB Array using Node.js

I've noticed there's little documentation and info about how I should manipulate an array of objects using Mongoosejs.
I have the following model/Schema for an User:
'use strict';
/**
* Module Dependencies
*/
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var mongoose = require('mongoose');
/**
* Custom types
*/
var ObjectId = mongoose.Schema.Types.ObjectId;
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, index: true },
password: { type: String },
type: { type: String, default: 'user' },
facebook: { type: String, unique: true, sparse: true },
twitter: { type: String, unique: true, sparse: true },
google: { type: String, unique: true, sparse: true },
github: { type: String, unique: true, sparse: true },
tokens: Array,
profile: {
name: { type: String, default: '' },
gender: { type: String, default: '' },
location: { type: String, default: '' },
website: { type: String, default: '' },
picture: { type: String, default: '' },
phone: {
work: { type: String, default: '' },
home: { type: String, default: '' },
mobile: { type: String, default: '' }
}
},
activity: {
date_established: { type: Date, default: Date.now },
last_logon: { type: Date, default: Date.now },
last_updated: { type: Date }
},
resetPasswordToken: { type: String },
resetPasswordExpires: { type: Date },
verified: { type: Boolean, default: true },
verifyToken: { type: String },
enhancedSecurity: {
enabled: { type: Boolean, default: false },
type: { type: String }, // sms or totp
token: { type: String },
period: { type: Number },
sms: { type: String },
smsExpires: { type: Date }
},
friends: [{
friend: { type: ObjectId, ref: 'User' },
verified: { type: Boolean, default: false }
}]
});
/* (...) some functions that aren't necessary to be shown here */
module.exports = mongoose.model('User', userSchema);
So as you can check I defined Friends inside User like this:
friends: [{
friend: { type: ObjectId, ref: 'User' },
verified: { type: Boolean, default: false }
}]
Now the question is how can I add, edit and delete this array in a Node.js script?
BOTTOMLINE: How can I manipulate arrays that are inside MongoDB Schemas, using Node.js and Mongoose.js? Do I always have to create a Schema function or can I access it directly?
EDIT (13/07/2014): So far I've created a HTTP GET that gives me the array like this:
app.get('/workspace/friends/:userid', passportConf.isAuthenticated, function (req, res) {
User.find({_id: req.params.userid}, function (err, items) {
if (err) {
return (err, null);
}
console.log(items[0].friends);
res.json(items[0].friends);
});
});
But this only returns an array of friendIds, but what if I want to create some sort of '/workspace/friends/:userid/del/:friendid' POST, or add POST. I can't seem to figure out how I can get this done.
You can do something like following
app.get('/workspace/friends/:userid/delete/:friendId', passportConf.isAuthenticated, function (req, res) {
User.findOne({_id: req.params.userid}, function (err, user) {
if (err) {
return (err, null);
}
for (var i = 0; i < user.friends.length; i++) {
if (user.friends[i]._id === req.params.friendId) {
user.friends = user.friends.splice(i,1)
}
}
user.save(function(err, user, numAffected){
if (!err )res.json(user)
res.send('error, couldn\'t save: %s', err)
})
});
});
What it says in mongoose docs is that
"The callback will receive three parameters, err if an error occurred, [model] which is the saved [model], and numberAffected which will be 1 when the document was found and updated in the database, otherwise 0.
The fn callback is optional. If no fn is passed and validation fails, the validation error will be emitted on the connection used to create this model."
If you need to manipulate arrays, you should convert these in objects before.
User.findOne({_id: req.params.userid}, function (err, user) {
if (err) {
return (err, null);
}
var user = user.toObject();
//... your code, an example =>
delete user.friends;
res.json(user);
});
Regards, Nicholls

Categories

Resources