Cannot set properties of undefined (setting 'login') - node.js

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

Related

getting a CastError, ObjectId failed for value

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.

MongoDB Auto-increment for a nested Object in a document in Node.js

I've a schema in Mongo like below:
({
title: String,
genre: String,
publishDate: Date,
index:Number,
requirementInformation:{
requirementInformationStatus:{
type: String,
required: true
},
requirement: [{ requirementId: {
type: Number
},requirementState: {
type: String
}}]
}
})
I want to Auto-increment the field requirementId in Node.js. I tried using mongoose-auto-increment package but not able to create the Auto-increment for the nested Object. Need some help.
My code is as below:
const mongoose = require('mongoose')
const Schema= mongoose.Schema;
var autoIncrement = require('mongoose-auto-increment');
var connection = mongoose.createConnection("mongodb://localhost/Test");
autoIncrement.initialize(connection);
var bookSchema = new Schema({
title: String,
genre: String,
publishDate: Date,
index:Number,
requirementInformation:{
requirementInformationStatus:{
type: String,
required: true
},
requirement: [{ requirementId: {
type: Number
},requirementState: {
type: String
}}]
}
});
bookSchema.plugin(autoIncrement.plugin, {model:'Book',field: 'requirementInformation.requirement.requirementId'});
//authorSchema.plugin(autoIncrement.plugin, 'Author');
var Book = connection.model("Book", bookSchema);
const me = new Book({
title: 'MyBook',
requirementInformation:{requirementInformationStatus:'True',requirement:[{requirementState:"hj"}]}
})
me.save().then(() => {
console.log(me)
}).catch((error) => { console.log(error) })
How can I access the requirementId field?

MongoDB populate() to dynamically load/migrate data not working

I am building an app in which the user adds and deletes objects (Pic) in an array('pics') after registering, but not sure how to dynamically load or populate('pics') to userSchema to automatically render. The user registers on the app with that array originally empty ('pics' = zero), and will create or delete those objects thereafter when logged in.
Following the documentation, I used "await User.find().populate('pics');" to migrate data in index method, but did not work.
Besides, should I include 'pics' key at store method, or userSchema 'pics' should be enough?
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
pics: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Pic"
}
],
});
const picSchema = new mongoose.Schema({
thumbnail: String,
description: String,
dev: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
},
);
const User = mongoose.model('User', userSchema);
const Pic = mongoose.model('Pic', picSchema)
async index(req, res, next) {
const users = await User.find().populate('pics');
res.status(200).json(
devs
);
},
async store(req, res) {
try {
const { name } = req.body;
let user = await User.create({
name,
pics
})
// await user.populate('pics').execPopulate();
res.send({ user })
}
} catch (error) {
res.status(400).send(error);
}
},
I worked a time ago with MongoDB and NodeJS. I think that you have a problem with the definitions. Also, you can read the documentation https://mongoosejs.com/docs/populate.html
You need to define the _id for collections (Schema).
const userSchema = new mongoose.Schema({
_id: new mongoose.Types.ObjectId(),
name: {
type: String,
required: true,
trim: true
},
pics: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Pic"
}
],
});
const picSchema = new mongoose.Schema({
_id: new mongoose.Types.ObjectId(),
thumbnail: String,
description: String,
dev: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
},
);
So, when you create a new User, the _id is completed (you can generate it or it can be generated automatically) and ignore the field pics. When you create a Pic, you need to read the _id of the User and assigned as 'dev', something like:
let pic = new Pic({
thumbnail: '', description: '',
dev: yourUser._id
});
Using this way to create documents, you can use the populate function.

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

Object has no method '__super__'

I extends mongoose save function:
User Schema is:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var UserSchema = new mongoose.Schema({
Name: String,
Surname: String,
login: {type: String, required: true},
Password: {type: String, required: true},
email: { type: String, index: { unique: true } },
Age: Number
});
And extended method is:
UserSchema.methods.save = function(okFn, failedFn) {
if (this.isValid()) {
this.__super__(okFn);
} else {
failedFn();
}
};
And on 'save' try it gives me error:
TypeError: Object { object fields and values } has no method '__super__'

Resources