getting a CastError, ObjectId failed for value - node.js

I have a route that is inserting an array of documents into a collection. I also want to capture the _is of the document into a user schema but I'm getting a CastError, how do I resolve this. here is my code:
the error:
(node:23088) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "
{
_id: 6032c1df6761405a308736f0,
name: ' test1',
surname: 'test',
email: 'example#example.com',
phone: '0915461',
__v: 0
},
{
_id: 6032c1df6761405a308736f1,
name: 'jane',
surname: 'doe',
email: 'jane#gmail.com',
phone: '12345678',
__v: 0
}
]" at path "references"
route:
app.post('/register_two/:id/references', async (req, res) => {
const staff = await Worker.findById(req.params.id);
const reference = await Reference.insertMany(req.body.references);
await staff.references.push(reference); <<--- Error when i try to push document IDs
});
my reference schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ReferenceSchema = Schema({
name: String,
surname: String,
email: String,
phone: String
});
module.exports = mongoose.model('Reference', ReferenceSchema);
my worker schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const WorkerSchema = new Schema({
name: String,
surname: String,
role: String,
nationality: String,
gender: String,
dateofbirth: Date,
contactnumber: String,
email: String,
address: String,
region: String,
righttowork: String,
righttoworkproof: String,
ninumber: String,
references: [
{
type: Schema.Types.ObjectId,
ref: 'Reference'
}
],
date: Date
});
module.exports = mongoose.model('Worker', WorkerSchema);
edit: fixed worker schema

I managed to solve it using the spread operator, all I did was change:
await staff.references.push(reference);
to
await staff.references.push(...reference);

You should run the insertMany command with the rawResult option set to true
const reference = await Reference.insertMany(req.body.references, {rawResult: true});
This way you will get the raw result from the MongoDB driver back into reference. That result will be an object that will contain this prop:
...
...
insertedIds: { '0': 6032ce0ed7fd8568aef0d0cd, '1': 6032ce0ed7fd8568aef0d0ce, ... }
...
insertedIds is what you need, but you need them in the form of an array. So here is what your code should look like:
const reference = await Reference.insertMany(req.body.references, {rawResult: true});
await staff.references.push(...Object.values(reference.insertedIds));
Credit to Emil C., who pointed out that by pushing an array we would simply be creating array of arrays inside the reference path. Spreading is the answer.

Related

Cannot set properties of undefined (setting 'login')

It looks like it doesn't like how I set the nested schema?
User Schema
const mongoose = require("mongoose");
const twitchSchema = mongoose.Schema(
{
id: Number,
login: String,
display_name: String,
type: String,
broadcaster_type: String,
description: String,
profile_image_url: String,
offline_image_url: String,
view_count: Number,
email: String,
created_at: String,
provider: String,
accessToken: String,
refreshToken: String,
},
{ _id: false }
);
const userSchema = new mongoose.Schema({
provider: {
youtube: {},
twitch: twitchSchema,
},
});
module.exports = mongoose.model("User", userSchema);
This is how I create a new document
const userModels = new UserModels();
userModels.provider[profile.provider].login = profile.login;
const result = await userModels.save()
You should always add the error you are getting in your question. I have tried your code and got undefined reference error:
// Schema setup
const mongoose = require('mongoose');
const twitchSchema = mongoose.Schema(
{
id: Number,
login: String,
display_name: String,
type: String,
broadcaster_type: String,
description: String,
profile_image_url: String,
offline_image_url: String,
view_count: Number,
email: String,
created_at: String,
provider: String,
accessToken: String,
refreshToken: String,
},
{ _id: false }
);
const userSchema = new mongoose.Schema({
provider: {
youtube: {},
twitch: twitchSchema,
},
});
module.export = mongoose.model('User', userSchema);
// Driver code
const u = new User();
console.log(u);
const profile = {
provider: 'twitch',
login: 'dummy_login',
};
u.provider[profile.provider].login = profile.login;
console.log(u);
const res = await User.create(u);
console.info(res);
// Error
// { _id: new ObjectId("624e8901dc46c217a1461c41") }
// TypeError: Cannot set property 'login' of undefined
So the problem is while setting the login field in the user model. You should tweak your userSchema like this:
const userSchema = new mongoose.Schema({
provider: {
youtube: {},
twitch: { type: twitchSchema, default: {} },
},
});
Please see mongoose documentation here.
Now if you run the below driver code it is able to create the record in mongo.
const u = new User();
console.log(u);
const profile = {
provider: 'twitch',
login: 'dummy_login',
};
u.provider[profile.provider].login = profile.login;
console.log(u);
const res = await User.create(u);
console.info(res);
See logs below:
{ _id: new ObjectId("624e8a9990d90dea47e7f62a") }
{
provider: { twitch: { login: 'dummy_login' } },
_id: new ObjectId("624e8a9990d90dea47e7f62a")
}
{
provider: { twitch: { login: 'dummy_login' } },
_id: new ObjectId("624e8a9990d90dea47e7f62a"),
__v: 0
}
Try defining user schema like this :-
const userSchema = new mongoose.Schema({
provider: {
youtube: {},
twitch: [twitchSchema],
},
});
You are using mongoose.Schema incorrect. This is called embeded documents in mongodb. You can define schema like following:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const twitchSchema = new Schema({
id: Number,
login: String,
display_name: String,
type: String,
broadcaster_type: String,
description: String,
profile_image_url: String,
offline_image_url: String,
view_count: Number,
email: String,
created_at: String,
provider: String,
accessToken: String,
refreshToken: String,
});
const userSchema = new Schema({
provider: {
youtube: {},
twitch: [twitchSchema],
},
});
module.exports = mongoose.model("User", userSchema);
There are a couple issues:
your schema definition has extra commas, missing 'new', and curly brackets, and an additional _id object just hanging out there.
youtube: {}, is not a valid schema type, should be youtube: Object for example, or define a schema for it! (see my next bullet)
You can't nest one schema within another like this, see comment from Bhaskar. I'd suggest you define TwitchSchema and a YoutubeSchema separately, and combine them in a third UserSchema.
If you omit fields at doc creation ( only provide 'login' and omit others) the resulting document WILL NOT have any other fields. If that is not what you want, provide at least default empty values for all other fields as well.
const mongoose = require('mongoose');
main().catch(err => console.log(err));
async function main() {
await mongoose.connect('mongodb+srv://ADDYOURS'); // <--- add url
const TwitchSchema = new mongoose.Schema({
id: Number,
login: String,
display_name: String,
type: String,
broadcaster_type: String,
description: String,
profile_image_url: String,
offline_image_url: String,
view_count: Number,
email: String,
created_at: String,
provider: String,
accessToken: String,
refreshToken: String
});
const YoutubeSchema = new mongoose.Schema({
id: Number,
login: String
});
const userSchema = new mongoose.Schema({
id: Number,
twitch : [TwitchSchema],
youtube : [YoutubeSchema]
});
UserModels = mongoose.model("User", userSchema);
const UserDocument = new UserModels();
let profile = {
provider : 'twitch',
login : 'foobars'
}
UserDocument[profile.provider].push({ login : profile.login});
console.log( UserDocument[profile.provider]);
const result = await UserDocument.save()
}
The above results in the following document:
If I were you I would do something like this
Define Schemas
const TwitchSchema = new mongoose.Schema({
twitchID: Number,
login: String,
display_name: String,
type: String,
broadcaster_type: String,
description: String,
profile_image_url: String,
offline_image_url: String,
view_count: Number,
email: String,
created_at: String,
provider: String,
accessToken: String,
refreshToken: String
});
const YoutubeSchema = new mongoose.Schema({
youtubeID: Number,
login: String
});
const UserSchema = new mongoose.Schema({
twitch: {
type: Schema.Types.ObjectId,
ref: 'Twitch'
},
youtube: {
type: Schema.Types.ObjectId,
ref: 'Youtube'
}
});
const User = mongoose.model('User', UserSchema);
const Twitch = mongoose.model('Twitch', TwitchSchema);
const Youtube = mongoose.model('Youtube', YoutubeSchema);
After this when I want to save the user and twitch value. I would do
route.post('/fake-route', async (req, res) => {
try {
//let's assume I have all the value from twitch in the `twitchData` variable
let twitchData = getTwitchData();
// Save Twitch Data to Mongo
const twitchSaveToDB = await Twitch.create(twitchData);
//Now Save User data using above saved Twitch Data
let userDataToSave = {
twitch: twitchSaveToDB._id
};
const userToDB = await User.create(userDataToSave);
res.status(201).json({
message: 'User Created'
});
} catch (err) {
console.error(err);
res.status(500).json({
message: 'Error Occured'
});
}
})
I didn't test the code exactly, so there might be few things to fix here and there but this would be the general concept I would follow

Mongoose array of objects are empty when saved

I am trying to save an array of objects to mongoose but this saves only an empty array in database. The below is model and schema
const HoldingSchema = new Schema({ symbol: String, quantity: Number });
const UserSchema = new Schema({
_id: {
type: String,
required: true,
},
holdings: {
type: [HoldingSchema],
default: undefined,
},
});
const UserModel = mongoose.model('Users', UserSchema, 'Users');
I try to store an array of objects with the below code. But this creates an empty array in the place of holding.
const testuser = new userModel({
_id: '001',
holding: [{ symbol: 'itc', quantity: 100 }],
});
await testuser.save(); // creates { _id: '001', holdings: [], __v: 0 }
Is it not possible to store array of custom objects. If not what would be an alternative?
There is actually a typo in your code that is why it doesn't save your holdings.
You have written holding, while the field is actually holdings
const testuser = new userModel({
_id: '001',
holdings: [{ symbol: 'itc', quantity: 100 }],
});

How to access nested data which is a reference data in an array in mongoose?

So I have this nested data object which is also a reference data. When I try to access it using populate it is showing only the id. I do not want only the id. I also want the details to come along. Can someone please help me on this?
This is the EmployeesDetailsSchema which have this employeesinfo property with a reference in it. Now the designation and speciality property as you can see in the image is coming only in ids and not the full details i want the full the details of these two as well as the other fields also.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const EmployeesDetailsSchema = new Schema({
employeesinfo: {
type: mongoose.Schema.Types.ObjectId,
ref: "employees"
},
workingdays: Number,
fathersname: String,
pan: String,
joiningdate: Date,
gender: String,
pfno: String,
esino: String,
branchname: String,
department: String,
paymode: String,
bankname: String,
acno: String,
ifscno: String,
//Earnings
basicsalary: Number,
extra: Number,
totalE: Number,
//Days
fixeddays: Number,
presentdays: Number,
absentdays: Number,
leavedays: Number,
holidays: Number,
//Deductions
pf: Number,
esi: Number,
professionaltax: Number,
advance: Number,
absentdeductions: Number,
leavedeductions: Number,
totalD: Number,
//Net pay Details
netpay: Number,
inwords: String,
name: String,
date: {
type: Date,
default: Date.now
}
});
module.exports = EmpDetails = mongoose.model(
"empdetails",
EmployeesDetailsSchema
);
This is the EmployeesSchema which is reference in the employeesinfo property of the EmployeesDetailsSchema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const EmployeesSchema = new Schema({
name: { type: String },
email: { type: String },
speciality: {
type: mongoose.Schema.Types.ObjectId,
ref: "speciality"
},
contactno: { type: Number },
designation: {
type: mongoose.Schema.Types.ObjectId,
ref: "designation"
},
alternatecontactno: { type: Number },
address: { type: String },
employeeImage: { type: String },
imageName: { type: String },
date: { type: Date, default: Date.now() }
});
module.exports = Employees = mongoose.model("employees", EmployeesSchema);
And these are the two models which is being reference in the EmployeesSchema
//Speciality Type Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const SpecialitySchema = new Schema({
speciality: {
type: String
},
description: {
type: String
}
});
module.exports = Speciality = mongoose.model("speciality", SpecialitySchema);
//Designation Type Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const DesignationSchema = new Schema({
designation: {
type: String
},
description: {
type: String
}
});
module.exports = Designation = mongoose.model("designation", DesignationSchema);
And this is the get route
router.get("/", (req, res) => {
EmpDet.find()
.populate({
path: "employeesinfo"
})
.then(empdet => res.json(empdet))
.catch(err =>
res.status(400).json({ msg: "Error in finding Employees Details" })
);
});
Populate usually returns the entire referred document if there is no specific options set. It can be used like this:
const story = await Story.findOne({ title: 'Some Titel' }).populate('authors');
See full documentation and further samples:
https://mongoosejs.com/docs/populate.html
Try this:
EmpDet.find()
.populate({
path: "employeesinfo",
populate: [
{ path: 'speciality' },
{ path: 'designation' }
]
})
.then(empdet => res.json(empdet))
.catch(err =>
res.status(400).json({ msg: "Error in finding Employees Details" })
);
Also, please have a look at the documentation too as mentioned by Simon.
https://mongoosejs.com/docs/populate.html

Mongoose Nested Populate Not Working

So I am currently extremely confused when it comes to mongoose populating.
My data structure is as follows: User has an array of Clients, a Client has an array of loans. I am using User.find and populating the client, no problem. But then how do I populate the loans that is inside of the client? I have tried this:
Basically, the goal is to pass in an array of Clients, each client will contain a loan. But for now, I want that loan object to be empty. The Client is being populated correctly, however, the Loan reference is being passed as undefined.
app.get("/loans", IsLoggedIn, function(req, res) {
User.findById(req.user._id).populate({path: "clients", populate: { path: "loans", model: "loan"}}).exec(function(err, user){
if(err){
console.log(err);
} else{
var amountRepaid = calcRepaid(user.clients.loans);
console.log(user.clients.loans);
res.render("index", {clients: user.clients, amountRepaid: amountRepaid});
}
});
However it doesn't seem to work, my models are listed below and any help is appreciated!
models:
Client:
var mongoose = require("mongoose");
var clientSchema = mongoose.Schema({
loans: [{
type: mongoose.Schema.Types.ObjectId,
ref: "loan"
}],
emailAdderess: String,
firstname: String,
lastname: String,
contactNumber: String ,
dateCreated: {type: Date, default: Date.now},
gender: String,
})
module.exports = mongoose.model("Client", clientSchema);
User:
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = mongoose.Schema({
username: String,
password: String,
firstname: String,
lastname: String,
clients: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Client"
}]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Loan:
var mongoose = require("mongoose");
var LoanSchema = mongoose.Schema({
firstName: String,
lastName: String,
email: String,
contactNumber: Number,
amountBorrowed: Number,
length: String,
address: String,
dateDue: Date,
gender: String,
date: { type: Date, default: Date.now },
amountRepaid: { type: Number, default: 0},
loanRepaid: {type: Boolean, default: false}
})
module.exports = mongoose.model("loan", LoanSchema);
Try this:
.populate({
path: 'clients',
populate: {
path: 'clients.loans'
}
})

MongoDB - NodeJs - Maximum call stack size exceeded

I get this error when saving the schema below in MongoDB/NodeJs (with Mongoose):
Uncaught RangeError: Maximum call stack size exceeded.
This occurs when I add details in my schema in the Array:
var mongoose = require('mongoose');
const Attribute = new mongoose.Schema({
context: String,
format: String,
measurand: String,
location: String,
unit: String
});
const Value = new mongoose.Schema({
value: Number,
attributes: Attribute
});
module.exports = mongoose.model('MeterValue',{
chargeBoxID: {type: String, ref: 'ChargingStation'},
connectorId: Number,
transactionId: Number,
timestamp: Date,
values: [Value]
});
Any idea?
Thanks in advance!
Serge.
I think you have to define an extra schema for the model in the array.
Something like:
const AddressSchema mongoose.Schema({
address1: { type: String },
address2: { type: String },
});
...
module.exports = mongoose.model('Person',{
_id: String,
name: String,
address: [ AddressSchema ],
});

Resources