Cast to [ObjectId] failed for value - node.js

Please help me with this one. I have one post query and I want to save it into two tables seperately. But I encountered an error message from postman " Cast to [ObjectId] failed for value "
Please help me! Thank you!
Here is my code below:
FirstModel
const mongoose= require('mongoose');
const { Schema } = mongoose;
const class = require('./classModel');
const SubjectSchema = new mongoose.Schema({
name: {
type: String,
unique: true
},
address: {
type: String,
},
classes: [ { type: Schema.Types.ObjectId, ref: class.schema } ]
})
Second Model
const classSchema = new mongoose.Schema({
name: {
type: String,
unique: true
},
})
and here is my controller
exports.create = catchAsync (async (req, res, next) => {
const newSubject = await Subject.create(req.body)
let payload = {
body : {
...req.body.classes,
_id: new mongoose.Types.ObjectId(),
golf_course_id: newGolfCourse._id
}
}
const newClass = await Class.createClass(payload)
if(!newClass ) {
res.status(201).json({
status: 'success',
data : {
subject: newSubject
}
})
}
})

It's because you are trying to set your classes in your Subject before they have ObjectIds. In your Subject Schema you have the following line:
classes: [ { type: Schema.Types.ObjectId, ref: class.schema } ]
Which tells Mongoose that you are referencing another model with it's assigned ObjectId but in your case when you try to save it:
await Subject.create(req.body)
These classes don't have ID's yet, so you need to create them. I wouldn't manually set them, when mongoose sets them on it's own. So the following would be a suggestion.
try {
const classes = req.body.classes.map(async (className) => {
const newClass = new Class({ name: className });
newClass.golf_course_id = newGolfCourse._id;
await newClass.save();
return newClass;
});
const results = await Promise.all(classes);
const newSubject = await new Subject({
name: req.body.name,
address: req.body.address,
classes: results,
});
res.send(newSubject);
} catch (error) {
console.log(error);
}
This method creates the classes just like you had in your code but then it give the classes to the Subject after they have been created, that way the Subject can use them. Otherwise you will get an ObjectId error because you can't assign a field with a reference e.g. classes: [ { type: Schema.Types.ObjectId, ref: class.schema } ] without a reference to that item. By the way in your ref: class.schema just use ref: 'Class'

Related

insert to MongoDB array with axios, restAPI and nodeJS

I am trying to add an item to a MongoDB array with RESTAPI through Axios. I thought it would look similar to the push method but I have no idea how to do that.
my Model is of a person:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: [],
missions: []
})
const personModel = mongoose.model('Person', PersonSchema);
I want to add a mission to the mission array of a person.
and for example, in order to add a new Person, I use NodeJS and API:
(api.js)
router.post('/api/people', (req, res) => {
const personToAdd = req.body;
const newPersonPost = new personModel(personToAdd);
newPersonPost.save((e) => {
if (e) {
console.log("error");
}
});
res.json({
msg: 'Received'
})
});
and in the client side I use Axios:
axios({
url: 'http://localhost:8080/api/people',
method: 'POST',
data: dataToUpdate
})
.then(() => {
console.log('axios sent info to server');
}).catch((e) => {
console.log('error' + e);
})
Thank you so much!
express
router.post('updating mission endpoint url', async (req, res) =>
try {
const query = { /* content */}; /* write a query to retrieve the concerned user by using a unique identifier */
let person = await personModel.findOne(query);
person.missions.push(req.body.mission);
personModel.save();
} catch (err) {
console.log(err);
}
});
client
In the client side you just have to put the mission you want to add in data like you did above with the right endpoint url and you should add a unique identifier for the user you want to add mission to.
[] will not assign array type to your variable.
Change your schema file with the following:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: { type: String },
password: { type: String },
friends: { type: Array },
missions: { type: Array }
})
Update the db model entity file with following
First method:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: {type : Array},
missions: {type : Array}
})
const personModel = mongoose.model('Person', PersonSchema);
Second Method :
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: [{ type: String }],
missions: [{ type: String }]
})
const personModel = mongoose.model('Person', PersonSchema);
You can update the array object as per your requirements.
You just want to be using the $push update operator, very simple, like so:
db.collection.updateOne(
{
_id: user._id
},
{
"$push": {
"missions": {
mission: newMission
}
}
})
Mongo Playground

Mongoose .populate() not working correctly

I'm trying to create a Tumblr clone with GraphQL and MERN. Right now I'm just trying to create the template for posts with photos.
Just in case it's relevant, I am doing regular REST post requests with axios and express for the image files. I take the response from those, map over the _ids and send them in the createPost mutation.
In graphiql I can query for a single Image model and get everything back fine, like so:
{
image(_id: "someId"){
_id
url
created
}
}
But when I do a subquery with the ObjectIds I've pushed into the Post arrays I get null for everything besides _id and __typename:
{
post(_id: "someId"){
_id
mainImages {
_id //returns value
url //returns null
created //returns null
__typename //returns value
}
}
}
The posts have two arrays of objects with ObjectId and ref for images, the Post model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
mainImages: [
{
type: Schema.Types.ObjectId,
ref: 'Image'
}
],
bodyImages: [
{
type: Schema.Types.ObjectId,
ref: 'Image'
}
],
})
module.exports = mongoose.model('Post', PostSchema, 'posts')
The Image model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ImageSchema = new Schema({
url: {
type: String,
required: true
},
created: {
type: Date,
required: true
}
})
module.exports = Image = mongoose.model('Image', ImageSchema, 'images');
The PostType:
const PostType = new GraphQLObjectType({
name: 'PostType',
fields: () => ({
_id: { type: GraphQLID },
mainImages: {
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('images')
.then(post => post.mainImages)
}
},
bodyImages: {
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('images')
.then(post => post.bodyImages)
}
},
})
})
module.exports = PostType;
I'm wondering if .populate('images') isn't working correctly. I thought that if you have the ObjectIds then .populate() can take care of the rest. I've been looking around at a bunch of different questions but none of them have seemed to be relevant enough to my situation, and the GraphQL and Mongoose docs also haven't given me a breakthrough yet.
Alright so in the latest edition of I wasn't reading the docs correctly, I finally figured out what I was doing wrong.
.populate() takes a string that denotes a path to the field within the document that you're trying to populate the field of.
I figured out the solution while I was reading about populating multiple fields in a single document.
For some reason I thought the 'path' was supposed to point to the name of the collection in the database:
const PostType = new GraphQLObjectType({
name: 'PostType',
fields: () => ({
_id: { type: GraphQLID },
mainImages: {
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('images') //incorrectly pointing towards the collection in db
.then(post => post.mainImages)
}
},
bodyImages: {
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('images') //incorrectly pointing towards the collection in db
.then(post => post.bodyImages)
}
},
})
})
module.exports = PostType;
It's supposed to point back in to the document/model itself:
const PostType = new GraphQLObjectType({
name: 'PostType',
fields: () => ({
_id: { type: GraphQLID },
mainImages: { //this is the 'path'
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('mainImages') //that's referenced here
.then(post => post.mainImages)
}
},
bodyImages: {
type: new GraphQLList(ImageType),
resolve(parentValue) {
return Post.findById(parentValue._id)
.populate('bodyImages') //and once again
.then(post => post.bodyImages)
}
}
})
})
export default PostType;
And that solved my problem, all of my GraphQL subqueries are working correctly now. Another day, another episode of I didn't read the docs correctly.

Express api with mongoose PUT

I have endpoint:
router.put('/:customerId', async (req, res) => {
try {
const updatedCustomer = await Customer.updateOne(
{ _id: req.params.customerId },
req.body,
{ new: true }
);
res.send(updatedCustomer);
} catch {
res.json({ message: err });
}
});
const CustomerSchema = mongoose.Schema({
name: String,
surname: String
})
and after put request with only name value in my object still exist name and surname. I thought that my surname value will be delete. It works fine or not?
In your schema definition do the following
const CustomerSchema = mongoose.Schema({
name: String,
surname: String
},{
strict:false
})

mongoose populate in array of custom objects

In the user model, I have an array of custom objects followedPlaylists which contains two attributes ( playlist: the id of the playlist, public: to determine whether it is public or not) as shown below
const userSchema = new mongoose.Schema({
..... other attributes
followedPlaylists: [{
playlist: {
type: mongoose.Schema.ObjectId,
ref: 'Playlist',
unique: true
},
public: Boolean
}]
})
I want to populate on followedPlaylists.playlist so the response would be something like
[{
playlist: * the actual playlist object *,
public: true
}]
I hope my question is clear enough and thanks in advance.
Here I am assuming that your Playlist is working just fine. i.e., it has elements and has been tested independently.
So, given the schema:
Const Playlist = require (./Playlist)//here you have to provide the path to the Playlist model or use mongoose.model (“Playlist”) to bring it in
………….
const userSchema = new mongoose.Schema({
..... other attributes
followedPlaylists: [{
playlist: {
type: mongoose.Schema.ObjectId,
ref: 'Playlist',
unique: true
},
public: Boolean
}]
})
On whatever you want to print it, just make something like:
Const user = mongoose.model (“User”);//or use require, what fits best your applications
……
Console.log(user.find().populate(“Playlist”))//here is the trick, you ask to populate the Playlist
Example
Examples are the best way to grasp a concept. You can play around with this example:
//------------------------------------------------------------------
const mongoose = require("mongoose");
const { model, Schema } = require("mongoose");
var dbURI = "mongodb://localhost/mongoose-sample";
const app = require("express")();
mongoose
.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(console.log(`connected to ${dbURI}`));
//----------------------------------------------------------------------
const departmentSchema = new Schema({ name: String, location: String });
const Department = model("department", departmentSchema);
const EmployeeSchema = new Schema({
firstName: String,
lastName: String,
department: { type: mongoose.Types.ObjectId, ref: "department" }
});
const Employee = model("employee", EmployeeSchema);
app.use("/", async (req, res) => {
// await Department.remove({});
// await Department.create({
// name: "Fiocruz",
// location: "Presidência"
// }).then(console.log(`we are good`));
// await Department.create({
// name: "IASI",
// location: "Roma"
// }).then(console.log(`we are good`));
// await Employee.create({
// firstName: "Jorge",
// lastName: "Pires",
// department: await Department.findOne({ name: "Fiocruz" })
// });
// await Employee.create({
// firstName: "Marcelo",
// lastName: "Pires",
// department: await Department.findOne({ name: "IASI" })
// });
// Employee.findOne("")
// .populate("department", "name")
// .select("department")
// .then(result => {
// console.log(result);
// });
await Employee.findOne({ _id: "5e6e28ec480a9d32fc78c46b" }, (err, result) => {
console.log(result);
})
.populate("department", "name")
.select("department");
res.json({
departments: await Department.find(),
employees: await Employee.find(),
employeesWithDep: await Employee.find().populate("department", "name"),
justDepartment: await Employee.findOne({ _id: "5e6e28ec480a9d32fc78c46b" })
.populate("department", "name")
.select("department")
});
});
app.listen(3000, () => {
console.log("we are on port 3000");
});

How to get the Populated values of a model in GraphqQL list without _id reference?

I have a Users model with some fields and reference to "Skills" model.
The models are look like this below
1.Users.js (users model)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UsersSchema = new Scheam({
name: { type : String },
email: { type : String },
address: { type : String },
Skills: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Skills' }],
})
module.exports = mongoose.model('Users', UsersSchema);
2.Skills Model
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const SkillsSchema = new Schema({
name : { type: String },
});
module.exports = mongoose.model('Skills', SkillsSchema);
I am creating the new user with dropdown menu of skills selection storing the id's of the skills in users model.
And getting the skills by populating the Users model like this in node js app.
export function getUser(req, res) {
console.log('getUser');
console.log('req.body',req.body);
Users.findOne({_id: req.body.user_id}).populate('skills').exec(function(err, usr_doc){
if(err) {
res.json({
status: 401,
message: 'something went wrong!',
err: err,
});
} else if (!err && usr_doc != null) {
res.json({
status: 200,
message: 'User details found!',
data: {_id: usr_doc._id, skills: usr_doc.skills,name: usr_doc.name,token: usr_doc.token},
})
}
})//Users.findOne end
}
The above code is working fine for normal rest api.
Here I try to create the Same api using GraphQL server of express-graphql in Nodejs.
The code look like this.
3.Schema.js
const graphql = require('graphql');
const Users = require('../models/Users');
const Skills = require('../models/Skills');
const {
GraphQLObjectType,
GraphQLInt,
GraphQLFloat,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLList,
GraphQLNonNull,
GraphQLBoolean,
GraphQLObject
} = graphql;
const UsersType = new GraphQLObjectType ({
name: 'Users',
fields: () => ({
id: {type: GraphQLID},
name: { type : GraphQLString },
email: { type : GraphQLString },
**skills: {
type: new GraphQLList(SkillsType),
resolve(parent, args){
//Here I need to know how to get the skills name of the users
}
}**
})
const SkillsType = new GraphQLObjectType({
name: 'Skills',
fields: () => ({
name: { type : GraphQLString },
})
})
At present I didn't use any reference id in skills model, only name. I want to get the skill names of the Users.
If i use the below, I am getting all of the skill names instead of user particular or populated skill names.
skills : {
type: new GraphQLList(SkillsType),
resolve(parent, args){
return Skills.find({})
}
Please provide any suggestions regarding this.
I can give more information if you need.
To get the skills of a user, instead of Skills.find({}) which returns all the documents present in the skills collection, add a condition there with the parent object.
The parent object, which contains result from the resolver on the parent field here. So skills, a child resolver, can be written as:
skills: {
type: new GraphQLList(SkillsType),
resolve(parent, args) {
return await Skills.find({"_id": {$in: parent.skills} }) }
}

Resources