Add array to subdocument Mongoose - javascript

I am trying to add an array to an existing subdocument in Mongoose using Express, but I can't seem to figure it out
This is my Mongoose model:
var subitems = new mongoose.Schema({
_id: ObjectId,
title: String,
messages: Array
});
var menuItems = new mongoose.Schema({
title : String,
subitem: [subitems]
}, {collection: 'menu_items'});
module.exports = mongoose.model("menu_items", menuItems);
I am using the ID from the subdocument to update the specific item in "subitem"
This is the Express Update:
postController.postArticles = function(req, res,item) {
var id = req.body.id;
var saveData = {
title: req.body.title,
text: req.body.text
};
item.update({_id: id}, {$push:{messages: saveData}},(err, result) => {
});
};
It is not adding anything to the db, anyone have an idea?

With mongoose you have to update the whole object. So, first you get item with find or findOne, and the you have to add the element to the array with push, and finally you call .save() method of the mongoose object.
Something like this:
postController.postArticles = function(req, res,item) {
var id = req.body.id;
item.findOne({_id: id}, function(error, myItem) {
var saveData = {
title: req.body.title,
text: req.body.text
};
myItem.messages.push(saveData);
myItem.save();
});
};

Related

Can't update values in mongodb database

Username doesn't get updated after running this. I also tried UpdateOne and it didn't work as well. The code was supposed to update an array but I tried updating the username to make it easier to track down the problem.
const mongoose = require('mongoose');
const schema = mongoose.Schema;
const userSchema = new schema({
Username:{
type: String,
required: true
},
Password:{
type: String,
required: true
},
Cart: Array
});
const User = mongoose.model('User', userSchema);
module.exports = User;
.
app.post('/putbox', (req, res) => {
var query = { Username: user };
var newvalues = { $push: { Cart: "Boxing" } };
try{
User.find(query).then((d)=>{
var values = d[0].Cart;
values.push("boxing")
User.findByIdAndUpdate(d._id, {Username: "test1"})
});
} catch(err) {
console.log(err);
}
res.redirect('/boxing');
});
I believe the syntax is not correct. The first element of updateOne searches for matches to update. You are just passing d._id which is not the same as the _id key in your db structure. Try this one
User.updateOne({ _id: d._id }, {Username: "test1"})

MongoDB Cannot read property 'push' of undefined

I'm new to node.js, express and MongoDB, and still learning. The program had a runtime error when I tried some data association in MongoDB models. The reference has been included in the model already but push() method can't recognize it. The models look like this:
var mongoose = require('mongoose');
var commentSchema = new mongoose.Schema({
text: String,
author: String
});
var Comment = mongoose.model("comments", commentSchema);
module.exports = Comment;
and
var mongoose = require('mongoose');
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
var Campground = mongoose.model("campgrounds", campgroundSchema);
module.exports = Campground;
The part where these two models are needed looks like this:
Campground.remove({}, function(err){
if(err){
console.log(err);
}else{
console.log("removed campground!");
data.forEach(function (seed) {
Campground.create(seed, function (err, data) {
if(err){
console.log(err);
} else{
console.log("camp created");
Comment.create(
{
text: "nice campsite!",
author: "haiderboi"
},function (err, comment) {
if(err){
console.log(err);
}else{
Campground.comments.push(comment);
Campground.save();
console.log("comment added");
}
}
);
}
});
});
}
});
It seems like the array is already defined in the Campground model so I can't tell the error. Thanks in advance!
Hey i was have problem like this in my project.And it wasn't working because my blogpost schema was like this:
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]});
And changed it to this:
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.Mixed,
}
]});
Because up there you're referring ObjectId and you're setting an object inside your code.It's not match.So you should try like this
Found the problem!
Instead of doing
Campground.comments.push(comment);
I should do
data.comments.push(comment);
since I wanna push the comment to the current Campground object, I should use the object returned by the callback.

Express.js blogging application bug: filtering posts by category throws "Cast to ObjectId failed" failed error

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
I have Posts that are grouped into Categories, each in its own collection.
I run into a problem trying to filter posts by category. To obtain the post by category url, I turn the category name into a slug and use it this way:
Posted in <%= post.category.cat_name %>
In the public routes file I have:
const express = require('express');
const postsController = require('../../controllers/front-end/posts');
// Express router
const router = express.Router();
// Get Posts
router.get('/', postsController.getPosts);
// Get Single Post
router.get('/:id', postsController.getSinglePost);
// Get Posts by Category
router.get('/:catname', postsController.getPostsByCategory);
module.exports = router;
The Post model:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
short_description: {
type: String,
required: true
},
full_text: {
type: String,
required: true
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
},
post_image: {
type: String,
required: false
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Post', postSchema);
The Category model:
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
cat_name: {
type: String,
required: true
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Category', categorySchema);
In the Posts controller I turn the slug back into category name to filter posts by category name:
exports.getPostsByCategory = (req, res, next) => {
function titleize(slug) {
var words = slug.split("-");
return words.map(function(word) {
//return word;
return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
}).join(' ');
}
const postCategory = titleize(req.params.catname);
const posts = Post.find({ cat_name: postCategory }, (err, posts) => {
console.log('Category: ', postCategory);
if(err){
console.log('Error: ', err);
} else {
res.render('default/index', {
moment: moment,
layout: 'default/layout',
website_name: 'MEAN Blog',
page_heading: 'XPress News',
page_subheading: 'A MEAN Stack Blogging Application',
posts: posts.reverse(),
});
}
}).populate('category');
};
The line console.log('Category: ', postCategory) outputs Category: Favicon.ico instead of the category name.
What am I doing wrong?
if I read this correctly, res is expect json pairs.
I am guessing your post.reverse() does not output in json format.
first of all - look at the moment where you ask DB - you need to await the answer, so you should use Promise.than() or async/await in your routes ...
Another one from request you get STRING as parameter - but in mongo schema you have Object...
So you should receive smth like "CastError: Cast to ObjectId failed...",
it depends on your vision you can: first select category from category.db => so you receive category Object after that you can search the posts using this object ..., or you can first populate results of posts by category (obtain plain category fields) and make further search...
category in your post-schema is a $ref to the category-schema, which is why it holds an objectId. In order to reference and actually query your category-schema while using .find(), you need to populate it first:
Post.
find({}).
populate({
path: 'category',
match: { cat_name: postCategory}
}).
exec((err, posts) => {
// ...
});
The mongoose documentation for $ref/populate() is a bit hidden here, in case you want know more about it.
Thanks to Lilian Baxan, here is the right getPostsByCategory method in controllers\front-end\posts.js:
const Category = require('../../models/categories');
//more code here
exports.getPostsByCategory = async (req, res, next) => {
function titleize(slug) {
var words = slug.split("-");
return words.map(function(word) {
//return word;
return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
}).join(' ');
}
const postCategory = titleize(req.params.catname);
const singleCategory = await Category.findOne({cat_name:postCategory})
const posts = await Post.find({ category : singleCategory }, (err, posts) => {
if (err) {
console.log('Error: ', err);
} else {
res.render('default/index', {
moment: moment,
layout: 'default/layout',
website_name: 'MEAN Blog',
page_heading: 'XPress News',
page_subheading: 'A MEAN Stack Blogging Application',
posts: posts.reverse(),
});
}
}).populate('category');
};

Mongoose - Model.deleteOne() is deleting the entire collection instead of a single document

I have a User model that contains an array of customers. I want to delete a specific customer based on the customer _id. From what I've read in the Mongoose docs, I should use Model.deleteOne to delete a single document.
Here is my attempt
User Schema (it's been shortened for brevity):
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: {
type: String,
default: ''
},
password: {
type: String,
default: '',
},
registerDate: {
type: Date,
default: Date.now()
},
customer: [{
name: {
type: String,
default: '',
},
email: {
type: String,
default: 'No email name found'
},
fleet: [{
unitNumber: {
type: String,
default: 'N/A',
}
}]
}]
});
module.exports = mongoose.model('User', UserSchema);
Here is a look at the route and controller:
const express = require('express');
const router = express.Router();
const customer_controller = require('../../controllers/customers');
router.delete('/customers/:custid', customer_controller.customer_remove);
module.exports = router;
And finally the controller:
exports.customer_remove = (req, res) => {
const { params } = req;
const { custid } = params;
User.deleteOne({ 'customer._id': custid }, (err) => {
if (err)
throw err;
else
console.log(custid, 'is deleted');
});
};
From what I thought, User.deleteOne({ 'customer.id': custid }) would find the customer _id matching the custid that is passed in via the req.params. When I test this route in Postman, it deletes the entire User collection that the customer is found in, instead of just deleting the customer. Can I get a nudge in the right direction? I feel like I am close here (or not lol).
deleteOne operates at the document level, so your code will delete the first User document that contains a customer element with a matching _id.
Instead, you want update the user document(s) to remove a specific element from the customer array field using $pull. To remove the customer from all users:
User.updateMany({}, { $pull: { customer: { _id: custid } } }, (err) => { ...
Using Mongoose you can do this:
model.findOneAndUpdate({ 'customer._id': custid }, {$pull: { $pull: {
customer: { _id: custid } }}, {new: true}).lean();
Removing subdocs.
Each sub document has an _id by default. Mongoose document arrays have a special id method for searching a document array to find a document with a given _id.
Visit: https://mongoosejs.com/docs/subdocs.html
parent.children.id(_id).remove();
Use async-await, may be that will work.
exports.customer_remove = async (req, res) => {
const { params } = req;
const { custid } = params;
try {
await User.deleteOne({ 'customer._id': custid });
console.log(custid, 'is deleted');
} catch (err) {
throw err;
}
};

Mongoose + Mongodb User.update not working

What I am trying to do is create a new collection, and push that collection into a specific User.collections array. I have read many stackoverflow posts and they all say to use either User.update() or User.findOneAndUpdate(). I am have no luck with either. I can create a Collection and that is saved to mongo so I know I am indeed accessing the db. Here is my code, if any of you can help I would appreciate it.
User Schema
const mongoose = require('mongoose');
const { Schema } = mongoose;
const userSchema = new Schema({
googleID: String,
givenName: String,
familyName: String,
collections: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "collection"
}
]
});
mongoose.model('users', userSchema);
Collection Schema:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const collectionSchema = new Schema({
type: String,
name: String,
gamesCollected: [
{
id: Number
}
]
});
mongoose.model('collection', collectionSchema);
And my route:
router.get('/get_collection', (req, res) => {
const collection = new Collection({
type: 'SNES',
name: 'First SNES Collection',
gamesCollected: [{ id: 5353 }]
}).save();
User.update({googleID: req.user.googleID}, {$push: {collections: collection}});
});
Save is not a synchronous operation but asynchronous so you need to use the promise it returns and handle it and once it is complete then update the user model. Something among these lines:
router.get('/get_collection', async (req, res) => {
let collection = new Collection({
type: 'SNES',
name: 'First SNES Collection',
gamesCollected: [{ id: 5353 }]
})
await collection.save().exec();
await User.update(
{googleID: req.user.googleID},
{$push: {collections: collection._id}}
).exec();
});

Categories

Resources