Update only changed fields in Mongoose - express

I'm trying to update a subdocument using ExpressJS and Mongoose. But only the fields that are changed. but somehow it updates all necesary field but also the fields that don't need to be changed.
This is the Update function in ExpressJS:
postController.updateMessage = function(req, res, item) {
var id = req.body.id;
var saveData = {
title: req.body.title,
text: req.body.text,
title_double: req.body.title_double,
text_double: req.body.text_double
};
item.findOneAndUpdate({'subitem.messages._id': id}, {$set:{'subitem.$.messages': saveData}}, {upsert: true, new: true},(error, result) => {
console.log(result);
console.log(error);
});
};
And this is the Mongoose Model:
var submessages = mongoose.Schema({
date: String,
type: String,
title: String,
text: String,
title_double: String,
text_double: String
});
var subitems = new mongoose.Schema({
title: String,
messages: [submessages]
});
var menuItems = new mongoose.Schema({
title : String,
subitem: [subitems]
}, {collection: 'menu_items'});
module.exports = mongoose.model("menu_items", menuItems);

you have two nested array while your are check/condtions on one array
{$set:{'subitem.$.messages.$': saveData}}
something like that on your condition

Related

Mongoose model schema referencing each other - how to simplify?

I am using mongoose and have two schemas: UserSchema and CommunitySchema:
const UserSchema = new Schema({
name: String,
communities: [{ type: Schema.Types.ObjectId, ref: CollectionModel }],
exCommunities: [{ type: Schema.Types.ObjectId, ref: CollectionModel }],
}, { timestamps: true });
const CommunitySchema = new Schema({
slug: { type: String, unique: true },
name: String,
description: String,
users: [
{ type: Schema.Types.ObjectId, ref: "User" }
]
}, { timestamps: true });
User can be a part of multiple communities and also leave any community and be in the exCommunities field.
When an user joins a community, I have to do a double work: Add the user to a user community and update the community user field with the reference ID.
Questions:
Is there a way to simplify it? For example, by managing the CommunitySchema users field automatically based on the UserSchema communities field?
Now I have to do this:
collection.users.push(userId);
user.communities.push(communityId);
Could the collection.users be automatically added when I push a community to user.communities? And how?)
Is it possible to add a date when the user is added to a community or leave a community? Something like: communities: [{ type: Schema.Types.ObjectId, ref: CollectionModel, createdAt: "<DATE>" }]
Thank you!
you no need to add communities and exCommunities in UserSchema
const UserSchema = new Schema({
name: String,
}, { timestamps: true });
const communityUserSchema = new Schema({
user_id:{type: Schema.Types.ObjectId, ref: "User"},
joined_at:{type:Date},
leaved_at:{type:Date},
is_active:{type:Boolean},
role:{type:String, enum:['user','admin'], default:'user'}
});
const CommunitySchema = new Schema({
slug: { type: String, unique: true },
name: String,
description: String,
users:{
type:[communityUserSchema],
default:[]
}
}, { timestamps: true });
you can find User's communities by :
let user_id = req.user._id;
all_communities = await Community.find({"users.user_id":user_id});
active_communities = await Community.find({"users.user_id":user_id, "is_active":true});
ex_communities = await Community.find({"users.user_id":user_id,"leaved_at":{"$ne":null}});
When User Create New Community (create as a Admin):
let current_user = {user_id:req.user.id,joined_at:new Date(),is_active:true,role:'admin'};
// if you select users from frontend while creating new community
let other_user = req.body.user_ids;
let other_users_mapped = other_user.map((item)=>{ return {user_id:item,joined_at:new Date(),role:'user',is_active:true}});
let all_users = [current_user];
all_users = all_users.concat(other_users_mapped);
let community = new Community();
community.name = req.body.name;
community.slug = req.body.slug;
community.description = req.body.description;
community.users = all_users ;
let created = await community.save();
When User Leave Community :
Community.updateOne({_id: community_id , 'users.user_id':user_id },{
$set:{
'users.$.is_active':false,
'users.$.leaved_at':new Date()
}
});
View Community with only active members :
let community_id = req.params.community_id;
let data = await Community.findOne({_id:community_id},{ users: { $elemMatch: { is_active: true } } });
AI solved it for me, here is the correct example:
import * as mongoose from 'mongoose';
const Schema = mongoose.Schema;
interface User {
name: string;
email: string;
communities: Community[];
}
interface Community {
name: string;
description: string;
users: User[];
}
const userSchema = new Schema({
name: String,
email: String,
}, {
timestamps: true,
});
const communitySchema = new Schema({
name: String,
description: String,
}, {
timestamps: true,
});
// Define the user_communities table using the communitySchema
const userCommunitySchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
community: { type: Schema.Types.ObjectId, ref: 'Community' },
joined_on: Date,
}, {
timestamps: true,
});
// Use the userCommunitySchema to create the UserCommunity model
const UserCommunity = mongoose.model('UserCommunity', userCommunitySchema);
// Use the userSchema to create the User model, and define a virtual property
// for accessing the user's communities
userSchema.virtual('communities', {
ref: 'Community',
localField: '_id',
foreignField: 'user',
justOne: false,
});
const User = mongoose.model<User>('User', userSchema);
// Use the communitySchema to create the Community model, and define a virtual property
// for accessing the community's users
communitySchema.virtual('users', {
ref: 'User',
localField: '_id',
foreignField: 'community',
justOne: false,
});
const Community = mongoose.model<Community>('Community', communitySchema);
The userSchema and communitySchema are then used to create the User and Community models, respectively. For the User model, a virtual property called communities is defined using the virtual method. This virtual property is used to specify how to populate the user.communities property when querying the database. The communitySchema also defines a users virtual property, which is used to populate the community.users property when querying.

Mongoose reference not placing ObjectId in parent document

My Mongoose model is not placing ObjectId's in the parent document for referencing to the subdocument in another collection. How can I do this?
These are my models:
menu_items.js
var menuItems = new mongoose.Schema({
title : String,
subitem: [{type: mongoose.Schema.Types.ObjectId,ref: 'sub_items'}]
}, {collection: 'menu_items'});
module.exports = mongoose.model("menu_items", menuItems);
sub_items.js
var subItems = new mongoose.Schema({
title: String,
},{collection: 'sub_items'});
module.exports = mongoose.model("sub_items", subItems);
My subitem post function in ExpressJS:
postController.postSubitems = function(req,res,item) {
var id = req.body.id;
var saveData = {
title: req.body.sub_item
};
var data = new item(saveData);
saveToDB(data,res);
};
You also need to update your menuItem as well. For example, say the _id of your new sub_item is 123456. You need to update your menuItem like so:
menuItem.subitem.push(123456);
menuItem.save();
This will add the _id to the subitem array, and thus give the menuItem a reference to the specified sub_item

mongoose populate nested subdocuments

I have following model
var fieldsSchema = new Schema({
name: String,
type: String,
value: String,
media: [{ type: Schema.Types.ObjectId, ref: 'Upload' }],
required: Boolean,
recepientvisible: Boolean
})
var orderSchema = new Schema({
number: String,
date: Number,
updated: Number,
type: { type: Schema.Types.ObjectId, ref: 'OrderTemplate' },
currentstatus: String,
comment: String,
assignedTo: { type: Schema.Types.ObjectId, ref: 'User' },
createdBy: { type: Schema.Types.ObjectId, ref: 'User' },
statuses: [{
name: String,
fields: [fieldsSchema]
}]
});
var Order = mongoose.model('Order', orderSchema);
module.exports = Order;
I do the following request
app.post('/order/', function (req, res) {
Order.find()
.populate({ path:'type', select: 'name -_id'})
.populate({ path:'assignedTo', select: 'name -_id'})
.populate({ path:'createdBy', select: 'name -_id'})
.populate({ path:'statuses', populate: { path: 'fields', populate: { path: 'media'} }})
.exec(function (err, orders) {
if (err) throw err;
res.send(orders)
});
})
What I need is to populate media fields. But in response I get only array of _id.
All other fields populates well.
How can I populate media field correctly ?

How to remove element from an array in mongoose

I have the following Schema:
// userSchema
{
_id: Schema.ObjectId,
email: { type: String, unique: true },
password: String,
boxes: [boxSchema]
}
// boxSchema
{
_id: Schema.ObjectId,
boxId: { type: String, unique: true },
boxName: String
}
I have data like this:
{
_id: random,
email: em#i.l,
password: hash,
boxes: [{ "boxId" : "box1", "boxName" : "Box 1"},
{ "boxId" : "box2","boxName" : "Box 2"},
{ "boxId" : "box3","boxName" : "Box 3"}]
}
I am trying to remove an element from boxes array with boxId: box1 and the code I tried was this:
User.findOne({
_id: req.body.id
})
.then(function (user) {
if (user) {
for (i in user.boxes)
if (user.boxes[i].boxId === 'box1')
user.boxes[i].remove();
res.json('removed');
}
})
.catch(function (err) {
....
});
But what happens is that it removes all the boxes which is residing, instead of the boxId: box1
What about using filter
User.findOne({
_id: req.body.id
})
.then(function (user) {
if (user) {
user.boxes = user.boxes.filter(function(box){
return box.boxId !== 'box1'
})
res.json('removed');
}
})
.catch(function (err) {
....
});
There are many ways to remove element from the array and are as follows:
1) Delete(): using this function will remove the element but will not change the array size and keep blank object after removal of element.
2)splice(): It works similar to delete() but remove blank places in array after removal of element.
3)filter(): It takes function as an argument and removes the element efficiently.

Mongoose relation not working both ways

I can't get a relationship running between my Rides and Comments controller in my app (built using the yeoman angular-fullstack generator).
Comment model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var CommentSchema = new Schema({
name: String,
comment: String,
active: Boolean,
ride: { type: mongoose.Schema.Types.ObjectId, ref: 'Ride' }
});
module.exports = mongoose.model('Comment', CommentSchema);
Ride model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RideSchema = new Schema({
name: String,
distance: String,
climb: String,
rating: String,
active: Boolean,
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
module.exports = mongoose.model('Ride', RideSchema);
Accessing /api/comments/ gives me a correct result, containing a related Ride:
{"_id":"54ce818f8c2889da58b01e19","name":"NAAM","comment":"COMMENT","ride":"54ce69647a78532057aa98e0","__v":0}]
Accessing /api/rides/ gives me the following result, without the corresponding Comments:
[{"_id":"54ce69647a78532057aa98e0","name":"Ride test ingevuld","distance":"4000","climb":"1200","rating":"1","__v":0,"comments":[]}]
Can anyone tell me what i am doing wrong?
Example from one of my projects:
exports.insertRoom = function(req, res) {
var id = req.body.id;
var r = req.body.room;
var room = new Room({name: r.name});
Floor.update(
{_id : id},
{
$push: { rooms: room}
},
{upsert:true},
function(floor, err)
{
res.sendStatus(200);
}
);
};
As far as I'am concerned it doesn't work like that. Your comments got it's ride, and your ride got it's comments. I think, you should remove
ride: { type: mongoose.Schema.Types.ObjectId, ref: 'Ride' }
and keep comments inside ride collection.
comments: ['Comment']
It is more objective solution as it supposed to be in MONGO DB which was designed for objective(hierarchial) data.