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
Related
So I have a primary mongoDB object that has multiple documents nested within. I want to access a specific document in an array and modify one of its values. This is my document setup
const sectionSchema = new mongoose.Schema({
name: String,
items: [itemSchema],
currentAmount: {
type: mongoose.Decimal128,
default: 0
},
limitAmount: {
type: mongoose.Decimal128,
required: true
},
isActive: {
type: Boolean,
default: 0
}
});
const Section = new mongoose.model("Section", sectionSchema);
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique:true
},
email: {
type: String,
lowercase: true,
trim:true,
required: true,
unique: true
},
password: {
type: String,
required: true
},
sections: [sectionSchema]
});
const User = new mongoose.model("User", userSchema);
I've added some dummy values to fill the database, including the other testSection and testItems.
const testSection2 = new Section({
name: "Vacation",
items: [testItem3,testItem4],
currentAmount: 0,
limitAmount: 800,
isActive: 1
});
const testUser = new User({
username: "wavey123",
email: "wvy#123.com",
password: "wvy123",
sections: [testSection1,testSection2]
});
I've tried different iterations of the .findOneAndUpdate methods with no success like:
app.post("/sSelect", function(req,res){
const selectedSection = req.body.sectionName;
User.findOneAndUpdate({sections: {$elemMatch: {isActive: 1}}},{sections: {isActive: 0}},{new: true}, function(err, aSection){
if (err){
console.log(err)
}
console.log(aSection);
})
User.findOneAndUpdate(({sections: {$elemMatch: {name: selectedSection}}}),{$set: {sections: {isActive: 1}}},{new: true}, function(err, aSection){
if (aSection){
res.redirect("/");
}
})
I end up with my base document looking like this:
[
{
_id: ObjectId("629a971bb8a72843a07df0fd"),
username: 'wavey123',
email: 'wvy#123.com',
password: 'wvy123',
sections: [
{
currentAmount: Decimal128("0"),
isActive: false,
_id: ObjectId("629a9756792a3b21872c329f"),
items: []
}
],
__v: 0
}
]
This happens after the first .findOneAndUpdate. Cant seem to get my head around it.
so i just scrapped the whole .findOneAndUpdate and just used JS to find the isActive key and manipulate it like so:
app.post("/sSelect", function(req,res){
const selectedSection = req.body.sectionName;
User.findOne({}, function(err, aSection){
aSection.sections.forEach(function(section){
if(section.isActive === true){
section.isActive = false;
console.log(section.isActive)
aSection.save();
}
})
});
User.findOne({}, function(err, aSection){
aSection.sections.forEach(function(section){
if(section.name === selectedSection){
section.isActive = true;
console.log(section.name,section.isActive)
aSection.save();
}
})
});
res.redirect("/");
:)
I have a model defined as so:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const feedbackSchema = new Schema({
Name: {
type: String,
required: true,
},
Email: {
type: String,
required: true,
},
Project: {
type: String,
required: true,
},
Wonder: {
type: String,
required: true,
},
Share: {
type: String,
required: true,
},
Delight: {
type: String,
required: true,
},
Suggestions: {
type: String,
required: true,
},
Rating: {
type: String,
required: true,
},
dateCreated: {
type: Date,
default: Date.now(),
},
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
const UserSchema = new Schema({
googleId: {
type: String
},
displayName: {
type: String
},
firstName: {
type: String
},
lastName: {
type: String
},
image: {
type: String
},
createdAt: {
type: Date,
default: Date.now(),
},
feedback: [feedbackSchema],
})
module.exports = mongoose.model("User", UserSchema);
An example document:
{
_id: ObjectId('60b9dc728a516a4669b40dbc'),
createdAt: ISODate('2021-06-04T07:42:01.992Z'),
googleId: '2342987239823908423492837',
displayName: 'User Name',
firstName: 'User',
lastName: 'Name',
image: 'https://lh3.googleusercontent.com/a-/89wf323wefiuhh3f9hwerfiu23f29h34f',
feedback: [
{
dateCreated: ISODate('2021-06-04T07:42:01.988Z'),
_id: ObjectId('60b9dc858a516a4669b40dbd'),
Name: 'Joe Bloggs',
Email: 'joe#bloggs.com',
Project: 'Some Project',
Suggestions: 'Here are some suggestions',
Rating: '10'
},
{
dateCreated: ISODate('2021-06-04T08:06:44.625Z'),
_id: ObjectId('60b9df29641ab05db7aa2264'),
Name: 'Mr Bungle',
Email: 'mr#bungle',
Project: 'The Bungle Project',
Suggestions: 'Wharghable',
Rating: '8'
},
{
dateCreated: ISODate('2021-06-04T08:08:30.958Z'),
_id: ObjectId('60b9df917e85eb6066049eed'),
Name: 'Mike Patton',
Email: 'mike#patton.com',
Project: 'No More Faith',
Suggestions: 'Find the faith',
Rating: '10'
},
],
__v: 0
}
I have two routes defined, the first one is called when the user clicked a button on a feedback item on the UI which takes the user to a "are you sure you want to delete this record"-type page displaying some of the information from the selected feedback record.
A second route which, when the user clicks 'confirm' the subrecord is deleted from the document.
The problem I'm having is I can't seem to pull the feedback from the user in order to select the document by id, here's what I have so far for the confirmation route:
router.get('/delete', ensureAuth, async (req, res) => {
try {
var url = require('url');
var url_parts = url.parse(req.url, true);
var feedbackId = url_parts.query.id;
const allFeedback = await User.feedback;
const feedbackToDelete = await allFeedback.find({ _id: feedbackId });
console.log(feedbackToDelete);
res.render('delete', {
imgSrc: user.image,
displayName: user.firstName,
feedbackToDelete
});
} catch (error) {
console.log(error);
}
})
Help much appreciated
Update
You should be able to do just this:
const feedbackToDelete = await User.feedback.find({ _id: feedbackId });
Or if feedbackId is just a string, which is appears to be, you may have to do something like:
// Create an actual _id object
// That is why in your sample doc you see ObjectId('foobarbaz')
const feedbackId = new mongoose.Types.ObjectId(url_parts.query.id);
const feedbackToDelete = await User.feedback.find({ _id: feedbackId });
Original
Shouldn't this:
const allFeedback = await User.feedback; (a field)
be this:
const allFeedback = await User.feedback(); (a method/function)
?
I have this document in mongo atlas
_id: 5f8939cbedf74e363c37dd86,
firstname: "Person",
lastname: "Person lastname",
sex: "Masculino",
age: "20",
birthDay: 2020-10-07T00:00:00.000+00:00,
vaccines: Array
0:Object
dose: Array
_id: 5f8939cbedf74e363c37dd87
vaccine:5f7023ad96f7ed21e85be521
createdAt:2020-10-16T06:12:27.726+00:00
updatedAt:2020-10-16T06:12:27.726+00:00
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
This is my mongoose schema
const mySchema = new Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
sex: {
type: String,
required: true,
},
age: {
type: String,
required: true,
},
birthDay: {
type: Date,
required: true,
},
vaccines: [
{
type: new Schema(
{
vaccine: {
type: Schema.ObjectId,
ref: "Vaccine",
},
dose: Array,
},
{ timestamps: true }
),
},
],
});
every time I add a new person the vaccines array gets one new object with the timestamp as you can see, in my js file I use this code:
const addPerson = (person) => {
const myPerson= new Model(person);
return myPerson.save();
};
Then when I add a new vaccine for the same person this does not get the timestamp, I'm using this code for that:
const addPersonVaccine = async ({ params, body }) => {
if (!params) return Promise.reject("Invalid ID");
const vaccines = [body];
const foundPerson = await Model.updateOne(
{
_id: params,
},
{
$push: {
vaccines: vaccines,
},
}
);
return foundPerson;
};
This is what my body inside vaccines array has:
[ { vaccine: '5f72c909594ee82d107bf870', dose: 'Primera' } ]
The problem is that I have no results about the next timestamps, as you can see in my mongo atlas document:
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
Is that the best way to implement timestamps in subdocuments or sub schemas?
I will appreciate your answers, thnks 👏
You can use mongoose schema timestamps options to the inner schemas
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
for more Mongoose schema set timestamp on nested document
I know, this is one of the popular questions out there. On populate I expect it to return the user with his posts, but it's returning an empty array of posts.
Here is my User model.
User.js
const mongoose = require("mongoose")
const Schema = mongoose.Schema
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}]
}, { timestamps: true })
const User = mongoose.model("User", userSchema)
module.exports = User
Post.js
const mongoose = require("mongoose")
const Schema = mongoose.Schema
const postSchema = new Schema({
postTitle: { type: String, required: true },
postDescription: { type: String, required: true },
user: { type: Schema.Types.ObjectId, ref: "User" },
}, { timestamps: true }
)
const Post = mongoose.model("Post", postSchema)
module.exports = Post
router.get("/posts/:id", usersController.getUserPosts)
usersController.js
getUserPosts: (req, res) => {
User.findById(req.params.id).populate("posts").exec((err, posts) => {
if (err) console.log(err)
console.log(posts)
})
}
I'm getting this:
{ posts: [],
_id: 5e4e3e7eecd9a53c185117d4,
username: 'rick',
email: 'rick#gmail.com',
createdAt: 2020-02-20T08:08:30.878Z,
updatedAt: 2020-02-20T08:08:30.878Z,
__v: 0 }
Where am I going wrong?
I am trying to develop a CRUD app for users to store, add, delete and update recipes. It's built on MEVN stack. As I need to show the user, which recipes they have created, I am trying to create a recipe based on this model:
const RecipeSchema = new Schema({
title: {
type: String,
required: [true, 'Title of the recipe is required'],
},
category: {
type: Array,
required: [true, 'Category is required'],
},
description: {
type: String,
required: [true, 'Description is required'],
},
imgUrl: {
type: String,
required: [true, 'Image is required'],
},
ingredients: {
type: Array,
required: [true, 'Ingredients are required'],
},
timeOfPreparation: {
type: String,
required: true,
},
preparation: {
type: String,
required: true,
},
sourceName: {
type: String,
required: true,
},
sourceUrl: {
type: String,
required: true,
},
author: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});
const Recipe = mongoose.model('Recipe', RecipeSchema);
module.exports = Recipe;
And at the same time update User model, based on this:
const UserSchema = Schema({
googleId: String,
name: String,
favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
authoredRecipes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
In the controller, I have this method (as per #Stock Overflaw comment):
exports.create_new_recipe = (req, res, next) => {
Recipe.create(req.body)
.then(recipe => {
User.update(
{ _id: req.body.author },
{
$push: { authoredRecipes: recipe.id },
}
);
res.send(res.status);
})
.catch(error => {
res.status(500).json({ error });
});
};
This method is called when I go to /create endpoint. However, even though I do get all the correct ids (req.body.author and recipe.id), I cannot get this to work. In my mLab recipe collection the recipe is displayed correctly (all data that I have inserted with authorId), however in the User collection, the array of authoredRecipes stays empty.
How can I get mongoose to both create an object in one collection as well as update another object based on their ids?
The documentation for findByIdAndUpdate requires the _id field as its value, not an object:
User.findByIdAndUpdate(req.body.author, {
$push: { authoredRecipes: recipe.id }
});
// equivalent to the more general method:
User.findOneAndUpdate({ _id: req.body.author }, {
$push: { authoredRecipes: recipe.id }
});
// and if you don't need the modified document in your callback, this should be faster:
// EDIT: this is advised against (we should use a user object, not the collection)
User.update({ _id: req.body.author }, { // or updateOne
$push: { authoredRecipes: recipe.id }
});
Edit: a working, minimal example
Mind {new: true} maybe? Depending on how you test whether it works...
const mongoose = require('mongoose');
const fs = require('fs');
const userIdFile = './tmp.txt'; // just for this test
mongoose.connect('mongodb://localhost/meuh', {
useNewUrlParser: true, // removes a deprecation warning
useFindAndModify: false // removes another deprecation warning
});
// make schemas/models
const RecipeSchema = mongoose.Schema({
title: { type: mongoose.Schema.Types.String }
});
const UserSchema = mongoose.Schema({
name: { type: mongoose.Schema.Types.String },
data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }]
});
const RecipeModel = mongoose.model('Recipe', RecipeSchema);
const UserModel = mongoose.model('User', UserSchema);
// user precreation
// UserModel.create({
// name: 'me, myself and I'
// }).then((user) => {
// fs.writeFile(userIdFile, user.id, console.log.bind(null, 'error writing file:'));
// mongoose.connection.close();
// });
// return;
// fetch user
const userId = fs.readFileSync(userIdFile);
let pushedRecipeId; // to test everything went smooth
RecipeModel.create({
title: 'pasta solo'
}).then((recipe) => {
console.log('created recipe:', recipe);
pushedRecipeId = recipe.id;
return UserModel.findOneAndUpdate(
{ _id: userId },
{ $push: { data: recipe.id } },
{ new: true } // forces callback to be passed a fresh object
);
}).then((user) => {
console.log('updated user:', user);
console.log('izok:', !!~user.data.indexOf(pushedRecipeId));
mongoose.connection.close();
}).catch((err) => {
console.log('error', err);
mongoose.connection.close();
})
Example output I got:
# creating user (uncommented this part)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
error writing file: null
# calling for $push (re-commented user creation)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72be7032bd2f1acad37c95, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# again $push
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c020c2ac7a1b8c65fa36, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95, 5c72c020c2ac7a1b8c65fa36 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# and again
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c023bf62331b97ef096b, title: 'pasta solo', __v: 0 }
updated user: { data:
[ 5c72be7032bd2f1acad37c95,
5c72c020c2ac7a1b8c65fa36,
5c72c023bf62331b97ef096b ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# end
ubuntu#ubuntu-VirtualBox:~/web/test$
I don't see what's wrong in your code, but at least you have something to compare with... hope this helps!