Mongoose cast to ObjectID failed for value... but why - javascript

I know what the problem is, but can't figure out why it is happening. I have a simple recipe app using express and mongoose. User passes in recipe info via form and is saved to database via mongoose methods. This part seems to work perfectly and when I console.log using test data, I see that the following data is saved:
{
ingredients: [ 'peanut butter', 'jelly', 'bread' ],
_id: 5e47d564f775ce247052d01c,
name: 'pb jelly sammich',
author: 'rob',
oneLiner: 'classic pb jelly sammich',
image: 'picofpbsammich here',
method: 'add all the ingredients together and boom! pb jelly sammich.',
__v: 0
}
(This is also what shows when I check mongo db using db.recipes.find() and also what displays when I pass in the object to my ejs show template.
However, when I access my show route via get request, I get a long error message using the above test data. Here is they key part of the error message:
'Cast to ObjectId failed for value "picofpbsammich here" at path "_id" for model "Recipes"',
I understand what the problem is, but baffled as to why it is happening. Here is my show route:
app.get("/recipes/:id", function (req, res) {
console.log(req.params.id)
Recipe.findById(req.params.id, function (err, foundRecipe) {
if (err) {
console.log(err);
} else {
res.render("show", { recipe: foundRecipe });
}
})
})
console logging the req.params.id as shown above, prints the following:
5e47d564f775ce247052d01c
picofpbsammich here
The first line is the correct ID, the second is obviously not and the cause of the problem, but I have no idea where that could be coming from :S Why would req.params.id be pulling the VALUE of a property that is named something completely different?
I'm new to mongoose so it's probably something silly I'm doing and any explanations appreciated.
Here is the model:
var mongoose = require("mongoose");
let recipeSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
author: String,
oneLiner: String,
ingredients: [String],
image: String,
method: String
})
module.exports = mongoose.model("Recipes", recipeSchema)

You posted the following code:
app.get("/recipes/:id", function (req, res) {
console.log(req.params.id)
Recipe.findById(req.params.id, function (err, foundRecipe) {
if (err) {
console.log(err);
} else {
res.render("show", { recipe: foundRecipe });
}
})
})
And you mention that in the console.log you receive:
5e47d564f775ce247052d01c
picofpbsammich here
Followed by the exception being logged:
'Cast to ObjectId failed for value "picofpbsammich here" at path "_id"
for model "Recipes"',
Makes me logically assume that you are making two requests, one of which the id is not valid, being:
picofpbsammich here
Mongoose is not able to cast this value to an ObjectId, hence you get the exception, which makes sense imo.

Related

Deleting an element from an Array, that is inside an Object

In my To-Do app, when a logged-in User completes a task, I would like to clear it from the MongoDB database.
Here is the code for my Schema.
const user = new mongoose.Schema({
username : String,
password : String,
task : [{
text : String,
day : String,
reminder : Boolean,
]}
})
For example, if Daryl completed text : "Gym" & day : "Feb 4th 5.30pm", I would like to only remove task[0] from Daryl's task Array.
Here is my attempt at doing so using Mongoose,
app.delete("/tasks", (req,res) => {
User.findOne( {_id : req.user.id}).then((target) => {
target.task.remove({text : req.body.text, day : req.body.day})
})
})
User.findOne({_id : req.user.id}) to only target the person that logged in
Once targeted, access task array using .task
and use .remove along with filters, to remove that entry from the array
I have console.logged() all the variables and it tallies with the data fields, however the entry is not being removed. What am I doing wrong?
I managed to solve my problem, hopefully this helps someone else
app.delete("/tasks", (req, res) => {
User.findByIdAndUpdate(req.user.id, {$pull: {"task": {text: req.body.text}}}, {safe: true, upsert: true},
function (err, node) {
// console.log here for debugging, if you want
})
})
This successfully erases items based on
req.user.id
"task" : {//whatever your stricter conditions are}
I still don't understand why my earlier attempts failed, but at least this one works.

POSTING an array of objects to node.js/save to database using mongoose

I have a ReactJS form, in which you can dynamically add form "parts"(sections with form input). Here's an example of what I mean by "parts":
<div>
<input placeholder="Author" />
<input placeholder="Age" />
<input placeholder="Amount of books written" />
</div>
Something like this. You can add as many of these divs as you like.
I'm saving the values of these inputs in the state, which gives me a nested array like so:
this.state = {
formdata : [
{author: "somebody", age: 34, books: 0},
{author: "somebody else", age: 100, books: 1}
]
}
Right now I'm use axios post to post the data to node.js with express. This is my post function:
handleSubmit(e) {
axios.post('receivedata',
querystring.stringify({
formdata : this.state.formdata
}), {
headers : {"Content-Type": "application/x-www-form-urlencoded"}
}
)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
And this is the mongoose schema that I use:
var EntrySchema = new mongoose.Schema({
formdata: [{type:String}],
updated_at: {type: Date, default: Date.now}
});
and this is how I (try to) send the data to my database:
router.post("/", function(req, res) {
newEntry.formdata = req.body.formdata;
newEntry.save(function (err) {
if(err)
res.send(err);
res.send("Entry added successfully!");
});
});
That doesn't work though and when I check the database, I receive an array with an empty string like so: formdata:[{""}]
I think the problem is with how the schema is set up, since, when I do console.log(this.state.formdata) it correctly returns the data. My next guess would be that axios is not able to send nested array, but after some research I found that that's not the case so I'm assuming that there's a special way to define nested arrays in the mongoose schema? How would I go about that?
Edit: I was thinking that maybe I could do something along the lines of:
var EntrySchema = new mongoose.Schema({
formdata: [{
author: String,
age: Number,
books: Number
}],
updated_at: {type: Date, default: Date.now}
});
I tried this and it doesn't work either. Now, I don't know if I'm on the right track or how else to do this.
I also tried changing the Content-Type in the header to "application/ json", as suggested in another answer. Once again, it didn't work.
Any help is appreciated.
Okay so after some playing around I figured it out: I used querystring.stringify() before, after changing it to JSON.stringify() it worked perfectly for me.

How i can get query string from file to query database in expressjs?

I'm trying to use this code but it's not working
var content = fs.readFileSync('/home/diegonode/Desktop/ExpressCart-master/views/partials2/menu8xz.hbs', 'utf8' );
req.db.products.find( content ).skip(0).limit(3).exec(function (err, results) {
please anyone help me (this is a extract of code)
i put the rest of code
req.db.products.find( content ).skip(0).limit(3).exec(function (err, results) {
res.render('index', {
title: 'Shop',
results: results,
session: req.session,
message: clear_session_value(req.session, "message"),
message_type: clear_session_value(req.session, "message_type" , req.body.product_id),
config: req.config.get('application'),
helpers: req.handlebars.helpers,
page_url: req.config.get('application').base_url,
show_footer: "show_footer"
});
console.log(content );
});
console.log(content );
});
It looks like your mapper's find function expects a conditions object, and when you read content directly from the file it's a plain text string that just happens to look like an object instead.
You need to do two things: first, "quote" product_title to make the file valid JSON; and second, use JSON.parse to turn content into an object which db.products.find can work with.

Nested mongoose Schema giving trouble when trying to query in controller

I'm working on a small project and I have a solution to this problem, but it involves creating a new Schema with a reference to the new Schema in the old Schema. I would like to avoid this if at all possible because it will mean spending a couple hours rewriting some code and messing with tests.
The project is a forum site, and there are three main Schemas that comprise it (in addition to cursory Schemas for the forums, notifications, settings and the schemas for the user and the users activities). The Board Schema (contains a list of all forum sections if that wasn't apparent) Is a Schema that makes a reference to the Threads Schema so it can get the threads for each Board. My problem is in the Thread Schema.
var ThreadSchema = new mongoose.Schema({
... other unrelated Schema stuff...
comments: [{
created: {
type: Date,
default: Date.now
},
creator: {
type: mongoose.Schema.ObjectId,
required: true,
ref: 'User'
},
content: {
type: String,
required: true,
get: escapeProperty
},
likes: [{
type: mongoose.Schema.ObjectId,
required: false,
ref: 'User'
}],
liked: {
type: Boolean,
default: false
},
saved: [{
type: mongoose.Schema.ObjectId,
required: false,
ref: 'User'
}]
}]
});
blah blah blah.
I'm trying to pull for the users profile only the comments that that user has posted. The threads were easy, but comment data is not coming through. The request to the server goes through as successful, but I don't get any data back. This is what I am trying.
obj.profileComments = function (req, res) {
var userId = req.params.userId;
var criteria = {'comments.creator': userId};
Thread.find(criteria)
.populate('comments')
.populate('comments.creator')
.skip(parseInt(req.query.page) * System.config.settings.perPage)
.limit(System.config.settings.perPage + 1)
.exec(function (err, threads) {
if (err) {
return json.bad(err, res);
}
json.good({
records: threads
}, res);
});
};
This is a controller, and the json.bad and json.good are helpers that I have created and exported they basically are res.send.
var good = function (obj, res) {
res.send({
success: 1,
res: obj
});
};
and the bad is very similar, it just handles errors in an obj.res.errors and puts them into messages.
So now that that is all out of the way, I'm a little lost on what to do?
Is this something I should try to handle with a method in my Schema? It seems like I might have a little bit more luck that way. Thank you for any help.

Understanding Mongoose sub documents

I'm learning Mongoose. At the moment I did few nice things but I really don't understand exactly how Mongoose manage relationships between Schemas.
So, easy thing (I hope): I'm doing a classic exercise (by my self because I cannot find a good tutorial that create more than 2 Schemas) with 3 Schemas:
User, Post, Comment.
User can create many Post;
User can create many Comment;
Post belong to User.
Comment belong to User and Post.
I don't think it is something very hard uhu?
At the moment I can manage very well Relation between User and Post. My Unit test return exactly what I need, at the moment I'm using mongo-relation and I don't know if it is a good idea...
it('Use should create a Post', function(done) {
User.findOne({ email: 'test#email.com' }, function(err, user) {
var post = {
title: 'Post title',
message: 'Post message',
comments: []
};
user.posts.create(post, function(err, user, post) {
if (err) return done(err);
user.posts[0].should.equal(post._id);
post.author.should.equal(user._id);
// etc...
done();
});
});
});
The problem now is to create a comment.
I can not create a comment that refere to the Post and to the User together.
I did something like that and works but when I perform a remove it is removed only from the Post and not from the User.
So I think there is something I miss or I still need to study to enhance it.
it('User should add a Comment to a Post', function(done) {
User.findOne({ email: 'test#email.com' }, function(err, user) {
if (err) return done(err);
var comment = new Comment({
author: user._id,
message: 'Post comment'
});
Post.findOne({ title: 'Post title'}, function(err, post) {
if (err) return done(err);
post.comments.append(comment, function(err, comment) {
if (err) return done(err);
post.save(function(err) {
if (err) return done(err);
});
comment.author.should.equal(user._id);
post.comments.should.have.length(1);
// etc...
done();
});
});
});
});
As you can see the code is not very "nice to see" but it works fine in terms of creations.
The problem is when I remove a Comment. It seems like something is wrong.
Here is the Model relationship:
// User Schema
var userSchema = new mongoose.Schema({
// [...],
posts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Post' }],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
});
// Post Schema
var postSchema = new mongoose.Schema({
author: { type: mongoose.Schema.ObjectId, ref: 'User', refPath: 'posts' },
title: String,
message: String,
comments: [{ type: mongoose.Schema.ObjectId, ref: 'Comment' }]
});
// Comment Schema
var commentSchema = new mongoose.Schema({
author: { type: mongoose.Schema.ObjectId, ref: 'User', refPath: 'comments' },
post: { type: mongoose.Schema.ObjectId, ref: 'Post', refPath: 'comments' },
message: String
});
I really hope in your help to understand all this.
It will be nice also a simple good tutorial about it.
I think you are misunderstanding subdocuments. The way you have your schema setup you are creating references to documents in other collections.
For example if you create a post, in the database it will look like this:
{
"author": ObjectId(123),
"title": "post title",
"message": "post message",
"comments": [ObjectId(456), ObjectId(789)]
}
Notice the "author" field just contains the ID of the author who created it. It does not actually contain the document itself.
When you read the document from the DB you can use the mongoose 'populate' functionality to also fetch the referred to document.
Ex (with populate):
Post
.findOne({ title: 'Post title'})
.populate('author', function(err, post) {
// this will print out the whole user object
console.log(post.author)
});
Ex (no populate):
Post
.findOne({ title: 'Post title'}, function(err, post) {
// this will print out the object ID
console.log(post.author)
});
Subdocuments:
You can actually nest data in the DB using subdocuments, the schema would look slightly different:
var postSchema = new mongoose.Schema({
author: { userSchema },
title: String,
message: String,
comments: [commentSchema]
});
When saving a post the user document would be nested inside the post:
{
"author": {
"name": "user name",
"email": "test#email.com"
...
},
"title": "post title",
"message": "post message",
"comments": [{
"message": "test",
...
}, {
"message": "test",
...
}]
}
Subdocuments can be useful in mongo, but probably not for this case because you would be duplicating all of the user data in every post.
Removing documents
When you issue a Comment.remove(id) the comment will be removed but it will not affect the other documents referring to it. So you will then have a Post and a User with a comment ID that does not exist. You need to manually clean up the comment ID from the other documents. You could use the mongoose pre remove event to do this. http://mongoosejs.com/docs/middleware.html
commentSchema.pre('remove', function (next) {
// this refers to the document being removed
var userId = this.author;
var postId = this.post;
User.findById(userId, function(err, user) {
// remove comment id from users.comments here;
Post.findById(postId, function(err, post) {
// remove comment id from post.comments;
next();
});
});
});

Categories

Resources