Cast to ObjectId failed for value at path _id for model with getAll during populate - mongodb

I am trying to make a list of permissions for a role,
here's what I am trying to do in my permissions,
const PermissionsSchema = new mongoose.Schema({
name: {
type: String,
index: true,
required: true,
},
createdAt: {
type: Date,
default: Date.now
}
});
PermissionsSchema.statics = {
get(id) {
return this.findById(id)
.exec()
.then((permission) => {
if (permission) {
return permission;
}
const err = new APIError('No such permission exists!', httpStatus.NOT_FOUND);
return Promise.reject(err);
});
},
list({ skip = 0, limit = 50 } = {}) {
return this.find()
.sort({ createdAt: -1 })
.skip(+skip)
.limit(+limit)
.exec();
}
};
module.exports = mongoose.model('Permission', PermissionsSchema);
and this in my roles model.
const RoleSchema = new mongoose.Schema({
name: String,
type: String,
permissions: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Permission'
}],
createdAt: {
type: Date,
default: Date.now
}
});
RoleSchema.statics = {
get(id) {
// const _id = mongoose.Types.ObjectId.fromString(id);
return this.findById(id)
// .populate('permissions')
.exec()
.then((role) => {
if (role) {
return role;
}
const err = new APIError('No such role exists!', httpStatus.NOT_FOUND);
return Promise.reject(err);
});
},
list({ skip = 0, limit = 50 } = {}) {
return this.find()
.populate('permissions')
.sort({ createdAt: -1 })
.skip(+skip)
.limit(+limit)
.exec();
}
};
module.exports = mongoose.model('Role', RoleSchema);
and when I try to get all, I get this error
Cast to ObjectId failed for value "ADD_USER" at path "_id" for model "Permission"
I've gone through some other posts but they all say I need to pass _id as a string, but I am not querying myself, how would I cast _id?

So I already had some documents in my collection which didn't have any kind of ID in them.

Related

How to update a user profile which has a property which is a ref in MongooseJS?

I have a User schema which has reference to a profile schema.
const UserSchema = new Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
....email props...
},
password: {
...password props...
},
profile: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Profile",
}],
},
);
const Profile = new Schema({
_user: {
type: Schema.Types.ObjectId, ref: 'User'
},
'displayName': {
type: String,
default: ''
},
'interestedActivities': ['Ping-pong'], <---- This bad boy/girl is an array
'memberSince': { type: Date, default: Date.now }
}
)
I'd like to create a route which can update the User properties AND the Profile properties in one shot—with a caveat one of the properties on the Profile model is an array!!!
I tried this....
handler
.use(auth)
.put((req, res, next) => {
emailValidator(req, res, next, 'email');
},
async (req, res, next) => {
await connectDB()
const {
profileDisplayName,
profileEmail,
interestedActivities } = req.body;
const update = {
email: profileEmail,
'profile.$.displayName': profileDisplayName,
'profile.$.interestedActivities': interestedActivities
}
const filter = { _id: req.user.id };
const updatedUser = await User.findOneAndUpdate(filter, update, { new: true })
try {
console.log("updatedUser ", updatedUser);
if (updatedUser) {
return res.status(200).send({
updatedUser,
msg: `You have updated your profile, good job!`
});
}
} catch (error) {
errorHandler(error, res)
}
})
export default handler;
My response is:
Status Code: 500 Internal Server Error
Cast to ObjectId failed for value "[
{
id: 'ae925393-0935-45da-93cb-7db509aedf20',
name: 'interestedActivities',
value: []
}
]" (type Array) at path "profile.$"
Does anyone know how I could also afford for the property which is an array?
Thank you in advance!

Change field in object in array of object

I have a field achivment with an array of objects. I need to update the field currentPoints in one object that I will find by field name in the array.
Code model of mongoose:
const achive = new Schema(
{
achiveId: ObjectId,
name: { type: String, required: true },
finishedPoints: { type: Number, required: true },
currentPoints: {
type: Number,
default: 0,
set: function (v) {
if (v >= this.finishedPoints) this.isFinished = true;
return v;
}
},
isFinished: { type: Boolean, default: false }
},
{ _id: false }
);
const achivesSchema = new Schema({
userId: ObjectId,
achivement: [achive]
});
Code query:
export async function progressAchive(req, res) {
const value = 3;
try {
const test = await Achives.updateOne(
{
userId: req.user._id,
achivement: { $elemMatch: { name: req.params.nameAchive } }
},
{ $set: { achivement: { currentPoints: value } } },
{ new: true }
);
res.json(test);
} catch (e) {
console.log(e);
}
}
Instead of updating, it removes all objects from the array and leaves them one object with the currentPoint field. How can I update this like I want?
You should use the following for update
const test = await Achives.updateOne(
{
userId: req.user._id,
},
{
$set:{"achivement.$[el].currentPoints": value}
},
{
arrayFilters:[{
"el.name": req.params.nameAchive
}],
new: true
}
);

Query wont return data from relationship

I'm following this video series.
Here i have problem with getting data about user who created the event in following script
here's my app.js
const express = require('express');
const bodyParser = require('body-parser');
const graphqlHttp = require('express-graphql')
const { buildSchema } = require('graphql');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const Event = require('./models/event');
const User = require('./models/user');
const app = express();
const conString = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}#clusterpl-qiufl.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`
app.use(bodyParser.json());
app.use(
'/graphql',
graphqlHttp({
schema: buildSchema(`
type Event {
_id: ID!
title: String!
description: String!
price: Float!
date: String!
creator: User!
}
type User {
_id: ID!
email: String!
password: String!
createdEvents: [Event!]
}
input UserInput {
email: String!
password: String!
}
input EventInput {
title: String!
description: String!
price: Float!
date: String!
}
type RootQuery {
events: [Event!]!
}
type RootMutation {
createEvent(eventInput: EventInput): Event
createUser(userInput: UserInput): User
}
schema {
query: RootQuery,
mutation: RootMutation
}
`) ,
rootValue: {
events: () => {
return Event.find().populate('creator')
.then(events => {
console.log(events)
return events.map(event => {
console.log(event)
return {
...event._doc,
_id: event.id
};
});
})
.catch(err => {
throw err;
})
},
..
},
graphiql: true
})
);
mongoose.connect(conString, {useNewUrlParser: true}).then(
() => {console.log('Success !')},
err => { console.log(err) }
)
app.listen(3000);
user.js and event.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
createdEvents: [
{
type: Schema.Types.ObjectId,
ref: 'Event'
}
]
});
module.exports = mongoose.model('User', userSchema)
const mongoose = require('mongoose');
const Schema = mongoose.Schema
const eventSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
date: {
type: Date,
required: true
},
creator: [
{
type: Schema.Types.ObjectId,
ref: 'User'
}
]
});
module.exports = mongoose.model('Event', eventSchema);
once i submit this graphql query
query{
events {
creator {
email
}
}
}
returns "message": "Cannot return null for non-nullable field User.email.",
I'm completly new to graphql and any answer would be much appreciate.
maybe this might helps someone else, i don't know what happens there but i got expected results, by adding
const user = userId => {
return User.findById(userId).then(user => {
return { ...user._doc, _id: user.id };
})
.catch(err => {
throw err;
});
}
method and used it in event resolver function like this
events: () => {
return Event.find()
.populate('creator')
.then(events => {
return events.map(event => {
console.log('ev',event._doc)
return {
...event._doc,
_id: event.id,
// creator: {
// ...event._doc.creator._doc,
// _id: event._doc.creator.id
// }
creator: user.bind(this, event._doc.creator)
};
})
})
.catch(err => {
throw err;
})
},

Why do I get array of nulls in my database? [duplicate]

This question already has answers here:
Node.js Mongoose.js string to ObjectId function
(9 answers)
Closed 4 years ago.
I have an array of ids which is launchIds.
I'm trying to push it on a model field trips with
$addToSet: { trips: { $each: launchIds }. This gives me an error: Cast to [ObjectId] failed for value \"[\"1\",\"2\",\"3\"]\...
if I try to map through launchIds and convert to Mongoose.Shema.Types.ObjectId I get in the database trips: [null,null,null]
lauchIds = ['1','2','3']
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const mongoIds = launchIds.map(l => Mongoose.Schema.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$addToSet: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Here's my model Schema:
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
I'm passing ids via grapql playground. Here's my mutation:
bookTrips: async (_, { launchIds }, { dataSources }) => {
console.log(launchIds);
// logs ['1','2','3']
console.log(typeof launchIds);
//Object
const results = await dataSources.userAPI.bookTrips({ launchIds });
console.log(results);
return { message: "hello" };
}
To convert a string or a number into mongo object use Mongoose.Types.ObjectId,
const mongoIds = launchIds.map(l => Mongoose.Types.ObjectId(l));
I was getting back an array of strings where this should be numbers
The solution:
My model (same as above):
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
crud API:
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const idsToNums = launchIds.map(Number);
const mongoIds = idsToNums.map(l => Mongoose.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$push: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Notice the Mongoose.Schema.Types.ObjectId on model and Mongoose.Types.ObjectId on api. If I remove Schema from model or add Schema to api I'm getting an error. Not sure why, but the above example works. I hope someone will find this helpful or suggests a better solution.

MongoDB - Update Array with different types (discriminatorKey)

I have a document which can have an array of different sub documents.
Saving documents to the database work fine and the structure is exactly what I need.
My Problem is that I can not update values in the "sections" array (schema below)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const logoSchema = require('./site-sections/logo');
var sectionSchema = new Schema(
{
show: { type: Boolean, default: true },
order: Number
},
{ discriminatorKey: 'type' }
);
const siteSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
type: { type: String, required: true },
title: { type: String, default: '' },
name: { type: String, required: true },
password: { type: String, default: '' },
caching: { type: Number, default: 1 },
unique_id: { type: String, required: true },
sections: [sectionSchema]
});
const sectionArray = siteSchema.path('sections');
const headerSchema = new Schema({
image: { type: String, default: '' },
title: { type: String, default: '' },
sub_title: { type: String, default: '' },
show: { type: Boolean, default: true },
logo: logoSchema
});
sectionArray.discriminator('header', headerSchema);
const textSchema = new Schema({
text: String
});
sectionArray.discriminator('text', textSchema);
module.exports = mongoose.model('site', siteSchema);
My Update function:
req.body has the following value:
{ key: 'title',
value: 'Test',
unique_site_id: '_jxn7vw' }
const Site = require('../../models/site');
exports.update = async function(req, res, next) {
console.log(req.body);
if (req.body.unique_site_id) {
Site.update(
{
unique_id: req.body.unique_site_id,
_user: req.user.id,
'sections.type': 'header'
},
{
$set: {
['sections.$.' + req.body.key]: req.body.value
}
},
function(err, status) {
if (err) {
console.log(err);
return res.status(500).send();
}
console.log(status);
return res.status(200).send();
}
);
}
};
The console.log(status) always prints: { ok: 0, n: 0, nModified: 0 }.
How can I update the title value?
Discriminator keys cannot be updated. https://github.com/Automattic/mongoose/issues/3839
Ok. So the right order is:
convert mongoose document to object with toObject()
change discriminator, and change/delete other properties
convert back to mongoose document with hydrate()
save