How to update an array in mongodb - node.js

I am trying to update a array which is in a item in the collection of mongoose. This maybe obscure so here is my code.
const itemSchema = new mongoose.Schema({
name: String,
});
const listSchema = new mongoose.Schema({
name: String,
items: [itemSchema],
});
These are the schema
List.findOne({name:listTitle},function (err, list) {
if (err) {
console.log(err);
} else {
list.items.push(item)
res.redirect(`/${listTitle}`)
}
})
This is the code for which I am trying to update that items array in the List collections.
It does not shows me any error in the console but It doesn't updates the array.
Any Suggestions??

You can directly go for :-
collection.findOneAndUpdate()
It will find the doc based upon your condition and then will make update to it.
While updating you need to use this $push operator to push elements to an array.
List.findOneAndUpdate({name:listTitle}, {
$push: {
items: item
}
}, function (err, list) {
if (err) {
console.log(err);
} else {
res.redirect(`/${listTitle}`)
}
})

Related

updating nested documents in mongoDB(node.js)

i am trying to update a value in the object of my embedded schema(comments schema) whose value i had previously stored 0 by default. i have tried all the ways to update but none of the stackoverflow answer worked.
my code is
var checkedBox = req.body.checkbox;
User.updateOne({_id: foundUser._id},{$set :{comments:{_id :checkedBox,cpermission:1,}}},function(err,updatec){
if(err){
console.log(err);
}
else{
console.log("successfull");
console.log(updatec);
}
});
i had comment schema nested in user schema,here foundUser._id is the particular users id,and checkedBox id is the embedded objects particular id. previously my cpermission was 0,set by default,but now i want to update it to 1. although this is updating my schema,but deleting the previous images and comments in the schema aswell.
where am i going wrong?
here is my schema
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
cpermission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
upermission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
First, you need to convert checkbox in the array, as it will be a string if you select a single element
Then wrap it with mongoose.Types.ObjectId as a precaution
Then you can use arrayFilters to update multiple matching array elements
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
checkedBox = checkedBox.map(id => mongoose.Types.ObjectId(id))
User.updateOne(
{ _id: foundUser._id }, // filter part
{ $set: { 'comments.$[comment].cpermission': 1 } }, // update part
{ arrayFilters: [{ 'comment._id': {$in: checkedBox }}] }, // options part
function (err, updatec) {
if (err) {
console.log(err);
}
else {
console.log("successfull");
console.log(updatec);
}
});
your comment is the array of documents. if you want to update an element of an array must be select it. for that must be added another condition to the first section of updateOne then in seconde section use $ for update selected element of the array.
User.updateOne(
{_id: foundUser._id, 'comments._id': checkedBox},
{
$set: {'comments.$.cpermission': 1}
}
, function (err, updatec) {
if (err) {
console.log(err)
}
else {
console.log('successfull')
console.log(updatec)
}
})
for more information, you can read this document form MongoDB official website.
Array Update Operators
var checkedBox = req.body.checkbox;
User.updateOne(
{ _id: foundUser._id, "comment._id": checkedBox },
{ $set: { "comment.$.cpermission": 1 } },
function (err, update) {
if (err) {
console.log(err);
} else {
console.log("successfull");
console.log(update);
}
}
);

Update field within nested array using mongoose

I'm trying to update the subdocument within the array without success. The new data doesn't get saved.
Express:
router.put('/:id/:bookid', (req, res) => {
library.findOneAndUpdate(
{ "_id": req.params.id, "books._id": req.params.bookid},
{
"$set": {
"title.$": 'new title'
}
}
});
LibraryScema:
const LibarySchema = new Library({
Name: {
type: String,
required: false
},
books: [BookSchema]
});
bookScema:
const BookSchema = new Schema({
title: {
type: String,
required: false
},
Chapters: [
{
chapterTitle: {
type: String,
required: false
}
}
]
});
I only aim to update the sub-document, not parent- and sub-document at same time.
I had a similar issue. I believe there is something wrong with the $set when it comes to nested arrays (There was an entire issue thread on GitHub). This is how I solved my issue.
var p = req.params;
var b = req.body;
Account.findById(req.user._id, function (err, acc) {
if (err) {
console.log(err);
} else {
acc.websites.set(req.params._id, req.body.url); //This solved it for me
acc.save((err, webs) => {
if (err) {
console.log(err);
} else {
console.log('all good');
res.redirect('/websites');
}
});
}
});
I have a user with a nested array.
Try this code
router.put('/:id/:bookid', (req, res) => {
library.findById(
req.params.id, (err, obj) => {
if (err) console.log(err); // Debugging
obj.books.set(req.params.bookid, {
"title": 'new title',
'Chapters': 'your chapters array'
});
obj.save((err,obj)=>{
if(err) console.log(err); // Debugging
else {
console.log(obj); // See if the saved object is what expected;
res.redirect('...') // Do smth here
}
})
})
});
Let me know if it works, and I'll add explanation.
Explanation: You start by finding the right object (library in this case), then you find the correct object in the array called books.
Using .set you set the whole object to the new state. You'll need to take the data that's not changing from a previous instance of the library object.
I believe this way will overwrite and remove any data that's not passed into the .set() method. And then you save() the changed.

Mongoose query not returning values

I have a CosmosDB collection called plotCasts, which has objects that look like this:
{
...
"owner" : "winery",
"grower" : "Bill Jones",
...
}
I have the following Mongoose schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const plotCastSchema = new Schema({
owner: String,
grower: String,
...
});
const ModelClass = mongoose.model('plotCast', plotCastSchema);
module.exports = ModelClass;
However, when I query the database using the query below, I get an empty array for a result. Any idea why?
PlotCast.find({ owner: 'winery' }).lean().exec(function(err, results) {
if (err) {
res.send(err);
} else if (!results) {
res.send(null);
} else {
res.send(results);
}
});
Okay, you named your model plotCast but your collection is plotCasts.
You can force your collection name this way:
const plotCastSchema = new Schema({
owner: String,
grower: String,
...
}, { collection: 'plotCasts' });
Or, simply define your Model in mongoose with the collection name as first argument, this way:
const ModelClass = mongoose.model('plotCasts', plotCastSchema);
Please let me know if that's it :)
the problem is naming the db always saves schema in plural form so it should be like below
PlotCasts.find({ owner: 'winery' }).lean().exec(function(err, results) {
if (err) {
res.send(err);
} else if (!results) {
res.send(null);
} else {
res.send(results);
}
});

Mongoose slice array, in populated field

I have the following mongoose schemas:
The main one is userSchema which contains an array of friends,
friendSchema. Each friendSchema is an object that contains an array of messageSchema. The messageSchema is the deepest object, containing the body of the message.
var messageSchema = new mongoose.Schema({
...
body: String
});
var conversationsSchema = new mongoose.Schema({
...
messages: [messageSchema]
});
var friendSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
conversation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Conversation',
},
}, { _id : false });
var userSchema = new mongoose.Schema({
...
friends: [friendSchema]
});
When retrieving specific user's friend, I populate its friends profiles, and if a conversation exist, I populate the conversation too.
How can I slice conversations.messages array, which resides in the population of the conversationobject ? I don't want to return the whole messages.
var userId = req.userid;
var populateQuery = [{ path:'friends.user',
select: queries.overviewConversationFields },
{ path:'friends.conversation' }];
User
.find({ _id: userId }, { friends: 1 })
.populate(populateQuery)
.exec(function(err, result){
if (err) { next(err); }
console.log(result);
}
EDIT(1) : I tried
.slice('friends.conversation.messages', -3)
EDIT(2) : I tried in populate query
{ path:'friends.conversation', options: { 'friends.conversation.messages': { $slice: -2 } }
EDIT(3) : For now, I can achieve what I want, slicing the array after the query is executed. This isn't optimized at all.
A little workaround that works.
I didn't found how to $slice an array that resides in a populated field.
However, the $slice operator works perfecly on any array, as long as its parent document has'nt been populated.
1) I decided to update the conversationSchema by adding an array containing both user's Id involved in the conversation :
var conversationsSchema = new mongoose.Schema({
users: [type: mongoose.Schema.Types.ObjectId],
messages: [messageSchema]
});
2) Then, I can easily find every conversation my user participates to.
As I said, I can properly slice the messages array, because nothing has to be populated.
Conversation.find({ users: userId },
{ 'messages': { $slice: -1 }}, function(err, conversation) {
});
3) Finally all I have to do, is to query all friends and conversations separately, and put back everything together, with a simple loop and a _find.
That would do more or less the same procedure of a Mongo population
Using async.parallel for more efficiency :
async.parallel({
friends: function(done){
User
.find({ _id: userId }, { friends: 1 })
.populate(populateQuery)
.exec(function(err, result){
if (err) { return done(err);}
done(null, result[0].friends);
});
},
conversations: function(done){
Conversation.find({ users: userId }, { 'messages': { $slice: -1 }}, function(err, conversation) {
if (err) { return done(err); }
done(null, conversation)
});
}}, function(err, results) {
if (err) { return next(err); }
var friends = results.friends;
var conversations = results.conversations;
for (var i = 0; i < friends.length; i++) {
if (friends[i].conversation) {
friends[i].conversation = _.find(conversations, function(conv){
return conv._id.equals(new ObjectId(friends[i].conversation));
});
}
}
});
// Friends contains now every conversation, with the last sent message.

How do I insert an element into an existing document?

I have an existing document that contains a nested array of elements (I'm not exactly sure of the terminology here). I have no problem creating the document. The problem arises when I need to insert a new element into the existing document. The code below may clarify what I'm trying to do:
Controller:
var Post = require('./models/post');
app.post('/post/:id/comment', function(req, res) {
var updateData = {
comments.comment: req.body.comment
comments.name: req.body.name,
};
Post.update({_id: req.params.id},updateData, function(err,affected) {
console.log('affected rows %d', affected);
});
});
Model:
var mongoose = require('mongoose');
var postSchema = mongoose.Schema({
post : String,
name : String,
created : {
type: Date,
default: Date.now
},
comments : [{
comment : String,
name : String,
created : {
type: Date,
default: Date.now
}
}]
});
module.exports = mongoose.model('Posts', postSchema);
So, each post can contain multiple comments. I'm just not sure how to insert a new comment into an existing post.
Since comments is declared as array, try to use
Post.update({_id:yourid}, { $push : { comments: { comment: '', name: '' } } }, ...
You can convert the object returned from mongodb in to an js object, and push new comment into the comments array. See the following:
var postSchema = require('./postSchema'); // your postSchema model file
postSchema.findOne({name: 'name-of-the-post'}, function (err, doc) { //find the post base on post name or whatever criteria
if (err)
console.log(err);
else {
if (!doc) { //if not found, create new post and insert into db
var obj = new postSchema({
post: '...'
name: '...'
...
});
obj.save(function (err) {
if (err)
console.log(err);
});
} else {
// if found, convert the post into an object, delete the _id field, and add new comment to this post
var obj = doc.toObject();
delete obj._id;
obj.comments.push(req.body.comment); // push new comment to comments array
postSchema.update(
{
'_id': doc._id
}, obj, {upsert: true}, function (err) { // upsert: true
if (err)
console.log(err);
});
}
console.log('Done');
}
});

Resources