autopopulate & virtual in child schema does not work - node.js

I have the following schemas:
"use strict";
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
autopopulate = require('mongoose-autopopulate');
const child = new Schema({
userUuid: {
type: String,
required: true
},
timeStamp: {
type: Date,
default: new Date()
}
}, {toJSON: {virtuals: true}});
child.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
const parentList= new Schema({
//some properties
children: [child]
});
parentList.plugin(autopopulate);
module.exports = parentList;
I need the Children's list to extract a full object - but it does not work.
When I put a single user not as a child then it works well:
const try= new Schema({
//some properties
userUuid: {
type: String,
required: true
}
}, {toJSON: {virtuals: true}});
try.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
try.plugin(autopopulate);
module.exports = try;
This leads me to the conclusion that the problem is when the virtual is within the child schema
What am I missing?

Here's the full code of my attempt to reproduce yours :
const
{ randomUUID } = require('node:crypto'),
{ MongoMemoryServer } = require('mongodb-memory-server'),
mongoose = require('mongoose'),
{ Schema } = mongoose,
autopopulate = require('mongoose-autopopulate');
(async () => {
const
dbServer = await MongoMemoryServer.create(),
dbClient = mongoose.createConnection(dbServer.getUri());
dbClient.on('disconnected', () => dbServer.stop());
await new Promise(resolve => dbClient.once('connected', () => resolve()));
try {
const trySchema = new Schema({
//some properties
userUuid: {
type: String,
required: true
}
}, { toJSON: { virtuals: true } });
trySchema.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
trySchema.plugin(autopopulate);
const childSchema = new Schema({
userUuid: {
type: String,
required: true
},
timeStamp: {
type: Date,
default: new Date()
}
}, { toJSON: { virtuals: true } });
childSchema.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
childSchema.plugin(autopopulate);
const parentListSchema = new Schema({
//some properties
children: [childSchema]
});
parentListSchema.plugin(autopopulate);
const userSchema = new Schema({
uuid: {
type: String,
required: true
}
});
const
Try = dbClient.model('try', trySchema),
Child = dbClient.model('child', childSchema),
ParentList = dbClient.model('parentList', parentListSchema),
User = dbClient.model('users', userSchema);
const userUuid = randomUUID();
await new User({ uuid: userUuid }).save();
await new Try({ userUuid }).save();
const child = await new Child({ userUuid }).save();
await new ParentList({ children: [child] }).save();
console.log('User:', (await User.findOne().exec()).toJSON());
console.log('Try:', (await Try.findOne().exec()).toJSON());
console.log('Child:', (await Child.findOne().exec()).toJSON());
console.log('ParentList:', (await ParentList.findOne().exec()).toJSON());
}
catch(error){
console.error(error);
}
dbClient.close();
})();
Which outputs :
User: {
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
Try: {
_id: new ObjectId("62c6e7bcef50638fe0097868"),
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0,
user: [
{
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
],
id: '62c6e7bcef50638fe0097868'
}
Child: {
_id: new ObjectId("62c6e7bcef50638fe009786b"),
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
timeStamp: 2022-07-07T14:03:40.902Z,
__v: 0,
user: [
{
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
],
id: '62c6e7bcef50638fe009786b'
}
ParentList: {
_id: new ObjectId("62c6e7bcef50638fe009786e"),
children: [
{
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
timeStamp: 2022-07-07T14:03:40.902Z,
_id: new ObjectId("62c6e7bcef50638fe009786b"),
__v: 0,
id: '62c6e7bcef50638fe009786b'
}
],
__v: 0
}
I'm not sure what you meant about [needing] the Children's list to extract a full object.
If you were talking about Child.user then you were only missing child.plugin(autopopulate);.
If you were talking about ParentList.children then it worked out of the box for me.

Related

Populate does not work in mongoose create

User schema looks something like this
addressId: [{ type: mongoose.Schema.Types.ObjectId, ref: 'address' }],
mongoose.model('user', userSchema);
Address model
mongoose.model('address', postalAddressSchema);
I am trying to do this:
const createdUser = await mongoose.models.user.create(user);
return { success: true, user: createdUser.populate('addressId') };
I am trying to populate address in user creation. It returns null
You may try to find, populate and exec after creating the user. Here is a working example
const theMongoose = await mongoose.connect(mongoServerUri);
const accountModel = theMongoose.model(
'Account',
new Schema({
_id: Schema.Types.ObjectId,
name: String
})
);
const activityModel = theMongoose.model(
'Activity',
new Schema({
account: { type: Schema.Types.ObjectId, ref: 'Account' },
date: Date,
desc: String
})
);
const account = await accountModel.create({ name: "Test User", _id: mongoose.Types.ObjectId.createFromTime(new Date().getTime()/1000) });
await activityModel.create({ date: new Date(), desc: "test", account: account.id });
// find, populate, & exec
const res = await activityModel.find({ desc: "test" }).populate("account").exec()
console.log(res);
And the output is
[
{
_id: new ObjectId("62da50ec77c026e2aad5577b"),
account: {
_id: new ObjectId("62da50ea0000000000000000"),
name: 'Test User',
__v: 0
},
date: 2022-07-22T07:25:32.119Z,
desc: 'test',
__v: 0
}
]

Cast to ObjectId failed for value "{ id: 61141a8345c9ba4338f2af20 }" (type Object) at path "_id" for model "LeaveTypes"

I was trying to create HRM project using Node and Mongodb (Mongoose) with leave management so for the leave I have created two documents 1. for leavetypes i.e anualLeave, maternityLeave and so on and the other one of taking care of the leave requests taken by the employees.
So here is my schemas and api requests.
// leave schema embedded in leaveTypeSchema
const mongoose = require("mongoose");
const Joi = require("joi-browser");
Joi.objectId = require("joi-objectid")(Joi);
const { branchSchema } = require("./branch");
const { employeeSchema } = require("./employee");
const { leaveTypesSchema } = require("./leaveType");
const leaveSchema = mongoose.Schema({
branch: {
type: branchSchema,
required: true,
},
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},
daysRequested: {
type: Number,
required: true,
},
fromDate: {
type: Date,
required: true,
},
endDate: {
type: Date,
required: true,
},
availableDays: {
type: Number,
},
});
const Leave = mongoose.model("leave", leaveSchema);
//validation
function validateLeave(leave) {
const schema = {
branchId: Joi.objectId().required(),
employeeId: Joi.objectId().required(),
leaveType: Joi.object()
.keys({
anualLeave: Joi.object()
.keys({
id: Joi.objectId().required(),
})
.required(),
})
.required(),
daysRequested: Joi.number().required(),
fromDate: Joi.date().required(),
endDate: Joi.date().required(),
};
return Joi.validate(leave, schema);
}
module.exports.Leave = Leave;
module.exports.Validate = validateLeave;
//route to post leave requests from employees
router.post("/", async (req, res) => {
// validate
const { error } = Validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
// check if branch is valid
let branch = await Branch.findById(req.body.branchId);
if (!branch) return res.status(400).send("Invalid Branch");
// check if employee is valid
let employee = await Employee.findById(req.body.employeeId);
if (!employee) return res.status(400).send("Invalid employee");
// check if leaveType is valid
let leaveType = await LeaveType.findById({
id: ObjectID(req.body.leaveType.anualLeave.id),
});
if (!leaveType) return res.status(400).send("invalid leave Type");
// post the leave request
const leave = new Leave({
branch: {
_id: branch._id,
name: branch.name,
},
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
},
jobTitle: employee.jobTitle,
salary: employee.salary,
},
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
daysRequested: req.body.daysRequested,
fromDate: req.body.fromDate,
endDate: req.body.endDate,
});
await leave.save();
res.send(leave);
Your document doesn't abide by the way you have created your schema.
When you are passing data to model, you have made leavetype nested inside employee
const leave = new Leave({
/**/
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
}, <- here
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
});
whereas in the schema your leaveType is a diff. object property.
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},

I have this error Unauthorised admin to execute command mongoose + Graphql

I used mongoose and Graphql to send my queries to the database but for some reason it doesn't let me create documents. I have tried creating a new user with full admin privileges it hasn't worked I tried changing the default user password but it didn't work.
I rechecked my mongoose model no errors so what might be the problem.
FYI the problem arose with the return (author.save()) and the database connects normally
Author Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const authorSchema = new Schema({
name: String,
age: Number
});
module.exports = mongoose.model('Author', authorSchema);
schema.js
const graphql = require('graphql');
const Book = require('../models/book');
const Author = require('../models/Author');
const _ = require('lodash');
const {
GraphQLObjectType,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLInt,
GraphQLList
} = graphql;
const BookType = new GraphQLObjectType({
name: 'Book',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
genre: { type: GraphQLString },
author: {
type: AuthorType,
resolve(parent, args){
//return _.find(authors, { id: parent.authorId });
}
}
})
});
const AuthorType = new GraphQLObjectType({
name: 'Author',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return _.filter(books, { authorId: parent.id });
}
}
})
});
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
book: {
type: BookType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(books, { id: args.id });
}
},
author: {
type: AuthorType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(authors, { id: args.id });
}
},
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return books;
}
},
authors: {
type: new GraphQLList(AuthorType),
resolve(parent, args){
//return authors;
}
}
}
});
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addAuthor: {
type: AuthorType,
args: {
name: { type: GraphQLString },
age: { type: GraphQLInt }
},
resolve(parent, args){
let author = new Author({
name: args.name,
age: args.age
});
return (author.save())
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation
})
;
error message
(node:31482) MongoError: (Unauthorized) not authorized on admin to execute command {
insert: "authors", documents: [[{name gyfdgyiszukjfheusdzyih} {age 88} {_id
ObjectID("60af9c682215ea7afad86f4c")} {__v 0}]], ordered: false, writeConcern: { w:
"majority" }
Found this issue, after trying practice by GraphQL tutorial on Youtube.
To solve it, you need to update your mongoose model to the last version.

Subdocument function "pull" and "id" not working in Mongoose

I am pretty new at mongoose and nodejs so I was doing my project referring to mongoose document. I want to remove a particular subdocument in the comment array by identifing the subdocument with its id. I trried doing it using "pull" as well as "id" method as shown in the image. I couldn't find any mistake in my syntax as well but still it is working.
This is sample document from my db:
{
comments: [
{
replyComment: [],
_id: 601a673735644c83e0aa1be3,
username: 'xyz123#gmail.com',
email: 'xyz123#gmail.com',
comment: 'test123'
},
{
replyComment: [],
_id: 601a6c94d1653c618c75ceae,
username: 'xyz123#gmail.com',
email: 'xyz123#gmail.com',
comment: 'reply test'
},
{
replyComment: [],
_id: 601eb7ba7233015d7090c6bf,
username: 'xyz123#gmail.com',
email: 'xyz123#gmail.com',
comment: 'test comment'
},
{
replyComment: [],
_id: 601ec090f5f22d75b41bec7b,
username: 'xyz123#gmail.com',
email: 'xyz123#gmail.com',
comment: 'test comment123'
}
],
_id: 601a3b8038b13e70405cf9ea,
title: 'latest test',
snippet: 'latest test snippet',
body: 'latest test body',
createdAt: 2021-02-03T05:58:24.123Z,
updatedAt: 2021-02-07T06:56:53.902Z,
__v: 15
}
By doing this test findById("601a3b8038b13e70405cf9ea") and console.log(result)
My topicSchema file:
const mongoose = require('mongoose');
const Schema =mongoose.Schema;
const topicSchema = new Schema({
title: {
type: String,
required: true
},
snippet: {
type: String,
required: true
},
body: {
type: String,
required: true
},
comments: {
type: Array,
required: false
}
}, {timestamps: true},{ versionKey: false });
const Topic = mongoose.model('Topic', topicSchema);
module.exports = Topic;
My commentSchema file:
const mongoose = require('mongoose');
const Schema =mongoose.Schema;
const comSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
comment: {
type: String,
required: true
},
replyComment: {
type: Array,
required: false
},
}, {timestamps: true},{versionKey: false});
const Comm = mongoose.model('comm', comSchema);
module.exports = Comm;
You have not defined topic and don't using topic = result, with , because it's not necessary
so doing like this :
result.comments.id(commId).remove();
result.save()
if you want to using topic just try
let topic = result;
topic.comments.id(commId).remove();
topic.save()
for this document you can using update and $pull like this:
Topic.updateOne(
{
_id: req.params.id,
},
{
$pull: {
"comments" : { _id: req.params.id1 }
},
},
).then((res)=>{
console.log(res)
}).catch(err=>{
console.log(err)
});
if you can use async/await just try
app.delete('/topic/:id/comments/:id1',async(req,res)=>{
let result = await Topic.findById(req.params.id);
result.comments.id(req.params.id1).remove();
let finalResult = await result.save()
console.log(finalResult)
})
and with .then() .catch approach:
Topic.findById(res.params.id).then(result=>{
let topic = result;
topic.comments.id(res.params.id1).remove();
topic.save().then(result=>{
console.log(result)
}).catch(err=>console.log(err))
}
).catch(err=>{
console.log(err)
});
NOTE: update the mongodb to 4.4 and uninstall mongoose module and install again

Cast to ObjectId failed for value "" at path "_id" for model

I have already checked the other entries on StackOverflow, but it did not help.
I am building a RESTapi with node.js, and I am using MongoDB with mongoose
I have a Schema that contains three different models. I am able to save POST request to the entry. I am sure that entry is saved because I checked on atlas.mongo. However, I have a problem when I am trying to use GET request.
It gives this error:
Cast to ObjectId failed for value "" at path "_id" for model
These are my Models: (These models are in different files)
const Model1 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
word1: { type: [String], require: true }
});
----------------------------------------------
const Model2 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
word2: { type: [String], require: true }
});
----------------------------------------------
const Model3 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
element1: { type: [String], default: ""},
element2: { type: [String], default: ""}
});
----------------------------------------------
const Word = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
md3: { type: mongoose.Schema.Types.Mixed, ref: 'Model3', require: true },
md2: { type: mongoose.Schema.Types.Mixed, ref: 'Model2', require: true },
md1: { type: mongoose.Schema.Types.Mixed, ref: 'Model1', require: true }
});
This is my POST request:
exports.entry_create = (req, res, next) => {
const newModel3 = new Model3({
_id: new mongoose.Types.ObjectId(),
element1: req.body.element1,
element2: req.body.element2
});
const newModel2 = new Model2({
_id: new mongoose.Types.ObjectId(),
word2: req.body.word2
});
const newModel1 = new Model1({
_id: new mongoose.Types.ObjectId(),
word1: req.body.word1
});
const newEntry = new Word({
_id: new mongoose.Types.ObjectId(),
md3: newModel3,
md2: newModel2,
md1: newModel1
});
newEntry
.save(); // i have also then() and catch() part
};
This is where I got the error on Postman
exports.entry_get_all = (req, res, next) => {
Word.find()
.select('_id md3 md2 md1')
.populate('md3')
.populate('md2')
.populate('md1')
.exec()
.then(docs => {
res.status(200).json({
numOfEntries: docs.length,
Entries: docs.map(doc => {
return {
_id: doc._id,
md3: doc.md3,
md2: doc.md2,
md1: doc.md1,
request: { type: 'GET' }
}
})
});
}); // i have also catch() part
};
What could be the problem? Is _id's of md3, md2 & md1 returns null?
I believe it has to do with your references md1, md2 and md3. The way you reference another model is by the _id, which in your case it's and ObjectId. That being said, when you define md1, md2, and md3 you say the type is mixed, not an ObjectId. Do this instead:
const Word = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
md3: { type: mongoose.Schema.Types.ObjectId, ref: 'Model3', require: true },
md2: { type: mongoose.Schema.Types.ObjectId, ref: 'Model2', require: true },
md1: { type: mongoose.Schema.Types.ObjectId, ref: 'Model1', require: true }
});
Also note: You don't need to explicitly create a new ObjectId when creating an instance of your model. If using mongoose, it creates the _id for you! So you can just create a new Word like this:
let md1 = null;
let md2 = null;
let md3 = null;
const newModel3 = new Model3({
element1: req.body.element1,
element2: req.body.element2
});
// Save newModel3
newModel3.save()
.then((_md3) => {
md3 = _md3;
const newModel2 = new Model2({
word2: req.body.word2
});
return newModel2.save();
})
.then((_md2) => {
md2 = _md2;
const newModel1 = new Model1({
word1: req.body.word1
});
return newModel1.save();
})
.then((_md1) => {
md1 = _md1
const newEntry = new Word({
md3: md3._id,
md2: md2._id,
md1: md1._id
});
return newEntry.save();
})

Resources