MongoDB Add More users to a field - javascript

I have a MongoDB collection for company users. Inside of this, the company is able to add in their team members to a field called: "teamMemberDetails"
For example, see a demo of what it currently looks like when I put a user into the DB through my API.
{
"teamMemberDetails": [
{
"memberEmail": "example2#hotmail.com"
}
],
"teamMembers": "0",
"_id": "62fc49b53bcb32ca823466dc",
"companyTitle": "Working Acc!v2",
}
It's also worth mentioning the schema is:
const CompanyProfileSchema = new mongoose.Schema(
{
companyTitle:{type:String, required: true, unique: true, default: ""},
companyPassword:{type:String, required: true, default: ""},
centralEmail:{type:String, required: true, unique: true, default: ""},
description:{type:String, required: true, default: ""},
inviteToken:{type:String, required: true, default:""},
industry:{type:String, required: true},
employees:{type: Number},
companyImage: {type:String, required: false, unique: false, default: ""},
locationCity:{type:String, required: false, unique: false, default: ""},
industry:{type:String, required: false, unique: false, default: ""},
country:{type:String, required: false, unique: false, default: ""},
teamMembers: {
type: String, required: true, default: 0
},
teamMemberDetails: {
memberName: String, default: "",
memberEmail: String, default: "",
memberRole: String, default: ""
},
highlights: {type: Array},
isAdmin:{
type: Boolean,
default: false,
},
isVerified:{
type: Boolean,
default: false,
},
accountLevel: {type: Array},
emailAuthorised: { type: Boolean, default: false},
invitedUsers: {type: Array}
},
);
This user was placed in with an API request like below:
//UPDATE Company - PROFILE
router.put("/updateCompany/:id", async (req, res) => {
try {
const updatedUser = await CompanyProfile.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
{ new: true }
);
res.status(200).json(updatedUser);
} catch (err) {
res.status(500).json(err);
}
});
However, i'd like this team members detail to build, for example i'd like something like this:
"teamMemberDetails": [
{
"memberEmail": "example2#hotmail.com", "example3#hotmail.com", "g2g#hotmail.com", "192#hotmail.com"
}
],
Basically, I want to concatenate onto this field with several ongoing email addresses as they add new users.
Any ideas?

Change your schema for memberEmail so that it accepts an array. ie. [String]:
teamMemberDetails: {
memberName: String, default: "",
memberEmail: [String], default: [],
memberRole: String, default: ""
},
and to update the document:
const teamMemberDetailsObject =
{
"memberEmail": [
"example2#hotmail.com",
"example3#hotmail.com",
"g2g#hotmail.com",
"192#hotmail.com"
]
}
if you'd like to initialize the array of emails, you can do that like this:
const updatedUser = await CompanyProfile.findByIdAndUpdate(
req.params.id,
{
$set: {teamMemberDetails: teamMemberDetailsObject}
},
{ new: true }
);
If you'd like to add new emails to the memberEmail array in the same document in the collection, you can use the following:
const updatedUser = await CompanyProfile.findByIdAndUpdate(req.params.id, {
$addToSet: {
"teamMemberDetails.memberEmail":"newemailexample#hotmail.com"
}
},
{ new: true }
);

Using $set and $push together, you can update other values on the document
as well as push new items to the memberEmail array.
By using $push, you can add new items to the array.
// model
teamMemberDetails: {
memberName: String, default: "",
memberEmail: [String], default: [],
memberRole: String, default: ""
},
// dummy payload
const memberEmail = [
"example2#hotmail.com",
"example3#hotmail.com",
"g2g#hotmail.com",
"192#hotmail.com"
]
// Update call
const updatedUser = await CompanyProfile.updateOne(
{_id: req.params.id},
{
$set: req.body,
$push: {
teamMemberDetails.memberEmail: memberEmail
}
},
{ new: true }
);
Here's a working example.

Related

Node.js REST API Will only allow me to create one record before throwing error?

A bit of a back end issue here. I have a simple API i've built which is just a post endpoint. This should post data with the req.body. Now - weirdly, it works all fine, but when I try it again, I will recieve an error like this.
{
"index": 0,
"code": 11000,
"keyPattern": {
"centralEmail": 1
},
"keyValue": {
"centralEmail": null
}
}
Now this is weird because centralEmail is not apart of this model or router at all.
There is no reference to it any point. When I POST the first time, it works fine - the second, this.
This is my schema:
const mongoose = require("mongoose")
//this just regulates the roles and their data connected to them. Will build out with more aux data support
// //RolesPositionSchema
const RolesListingSchema = new mongoose.Schema(
{
role:{type:String, required: true, default: ""},
companyListing:{type:String, required: false, default: ""},
companyID:{type:String, required: false, default: ""},
dateListing:{type:String, required: false, default: ""},
status: {type: String, required: false},
finalFormat:{type:String, required: false, default: ""},
listingScore:{type: Number},
companyEmail:{type:String},
selectedStyle:{type:String, required: false, default: ""},
selectedLocation:{type:String, required: false, default:""},
selectedIndustry:{type:String, required: false},
optionalMinSalaryRange:{type:String, required: false, default: ""},
optionalMaxSalaryRange:{type:String, required: false, default: ""},
optionalQuals:{type:String, required: false, default: ""},
selectedInterview1:{type:String, required: false, default: ""},
selectedInterview2:{type:String, required: false, default: ""},
selectedInterview3:{type:String, required: false, default: ""},
optionalSkills:{type:[String], required: false, default: []},
optionalPerks:{type:[String], required: false, default: []},
optionalWFH:{type:String, required: false, default: ""},
optionalQuals:{type:[String], required: false, default: []},
//
lookingFor:{type:[String], required: false, default: []},
dailyResponsibilites:{type:[String], required: false, default: []},
FAQS:{type:[String], required: false, default: []},
//
requiredFields:{type:[String], required: false, default: []},
},
);
module.exports = mongoose.model("RoleListings", RolesListingSchema)
And also, this is my API. Very simple but please see:
//CREATE A ROLE DESC
//Implement new roles
router.post("/", async (req,res)=>{
// router.post("/", verifyTokenAndAdmin, async (req,res)=>{
const newRole = new Role(req.body)
try{
const savedRole = await newRole.save();
res.status(200).json(savedRole)
}catch(err){
res.status(500).json(err)
}
})
This error indicate that you have duplicated value for a unique field.
You are not have the centralEmail in the schema right now, but you maybe had it before. When you remove the field that had unique index from the schema, that will NOT automatically remove the index too. You would have to do it manually.
For example, you can open the collection in Compass, and navigate to the Indexes tab. There, see if there is an index for the centralEmail property, and then delete it.

How to update immutable values in mongodb?

I have collection like this.
const Device: Schema = new Schema(
{
location: {
type: String,
required: true,
},
macAddress: {
type: String,
required: true,
immutable: true,
},
ip: {
type: String,
required: true,
immutable: true,
},
},
{
timestamps: true,
versionKey: false,
collection: 'Device',
}
);
I want to update immutable fields with my endpoints and i use this function and doesnt work.
Device.findOneAndUpdate(
{ _id: req.params.mdeviceId },
{ $set: { macAddress: req.body.macAddress, ip: req.body.ip},
{ new: true, upsert: true }
);
How can i update this specific fields?
I would guess req.params.mdeviceId is a string and needs to be casted to ObjectId, this is not related to the immutable property as you are providing the new flag which allows to bypass the schema protection, try using this:
Device.findOneAndUpdate(
{ _id: new mongoose.Types.ObjectId(req.params.mdeviceId) },
{ $set: { macAddress: req.body.macAddress, ip: req.body.id},
{ new: true, upsert: true }
);

How to optimize performance with CREATE, PUT, and DELETE requests on MongoDB?

I have a database named "reviews" with a 9.7GB size. It has a collection name products. I was able to optimize the READ request using indexing technical by running the command db.products.ensureIndex({product_name: 1}); When I run the following command db.products.find({product_name:"nobis"}).explain("executionStats"); in MongoDB terminal, it shows that my execution time reduces from 28334ms to 3301ms.
I have the following 2 questions:
1) How do I use explain("executionStats"); on CREATE, PUT and DELETE requests? For example, I got this following error [thread1] TypeError: db.products.insert(...).explain is not a function when I tried to use the following insert function
db.products.insert({"product_id": 10000002,"product_name": "tissue","review": [{"review_id": 30000001,"user": {"user_id": 30000001,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000001,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}, {"review_photo_id": 60000002,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}, {"review_id": 30000002,"user": {"user_id": 30000002,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000003,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}]}).explain("executionStats");
2) Is there any performance Optimization method I can use for the CREATE, PUT and DELETE requests? For example, I am able to use POSTMAN to get the response time of a DELETE request, but the response time takes 38.73seconds.
const deleteReview = (request, response) => {
const id = parseInt(request.params.id);
Model.ProductModel.findOneAndDelete({ "review.review_id": id}, (error, results) => {
if (error) {
response.status(500).send(error);
} else {
response.status(200).send(results);
}
});
};
This is my MongoDB schema:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/reviews', { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
const Schema = mongoose.Schema;
const productSchema = new Schema({
product_id: { type: Number, required: true, unique: true },
product_name: { type: String, required: true, unique: true },
review: [{
review_id: { type: Number, required: true, unique: true },
user: {
user_id: { type: Number },
firstname: { type: String },
lastname: { type: String },
gender: { type: String, enum: ['Male', 'Female', 'Other'] },
nickname: { type: String },
email: { type: String, required: true },
password: { type: String, required: true },
},
opinion: { type: String, required: true },
text: { type: String },
rating_overall: { type: Number, min: 1, max: 5, required: true },
doesRecommended: { type: Boolean, required: true },
rating_size: { type: String, enum: ['a size too small', '1/2 a size too small', 'Perfect', '1/2 a size too big', 'a size too big'], required: true },
rating_width: { type: String, enum: ['Too narrow', 'Slightly narrow', 'Perfect', 'Slightly wide', 'Too wide'], required: true },
rating_comfort: { type: String, enum: ['Uncomfortable', 'Slightly uncomfortable', 'Ok', 'Comfortable', 'Perfect'], required: true },
rating_quality: { type: String, enum: ['Poor', 'Below average', 'What I expected', 'Pretty great', 'Perfect'], required: true },
isHelpful: { type: Number, required: true, default: 0 },
isNotHelpful: { type: Number, required: true, default: 0 },
created_at: { type: Date, required: true },
review_photo_path: [{
review_photo_id: { type: Number },
review_photo_url: { type: String }
}]
}]
});
const ProductModel = mongoose.model('product', productSchema);
module.exports = { ProductModel };
If you do not have one, ensure you have an index of review.review_id on your products collection. You're using that to look up what to delete so it should be indexed.
I read your deleteReview function as deleting the product document that contains the review, not just removing the individual review -- is that what you expect?
You should be able to just $pull the review from the reviews array to get rid of it.
You can use explain on an update like so:
db.products.explain().update({...}, {...});
See: https://docs.mongodb.com/manual/reference/method/db.collection.explain/
You can explain:
aggregate()
count()
find()
remove()
update()
distinct()
findAndModify()

Querying sub document of sub document in mongoose

I wanted to save the data in "messageSchema" which is sub document of chatSchema by checking the "receiver" of chatSchema and "username" of userSchema.
like pseudoCode:-
if(userSchema.username == "Rahul" && userSchema.chatSchema.receiver){
then save the data in chatSchema.message;
}
Here is my Schema:-
var messageSchema = mongoose.Schema({
messageId: {type: String, unique: true, required: true},
created: {type: Date, default: Date.now},
messageContent: String
});
var chatSchema = mongoose.Schema({
message: [messageSchema],
receiver: {type: String, required: true}
});
var userSchema = mongoose.Schema({
username: { type: String, unique: true, required: true },
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
token: { type: String, required: false },
conversations: [chatSchema]
});
please suggest what should be code to save the message data.
tried below one that didn't work.
User.findOneAndUpdate({username: "rahul", "conversations.receiver": data.receiver },{$push: {"conversations.message": message}});
I think you need to use $elemMatch instead of the dot notation for matching properties within an array. Try this:
User.findOneAndUpdate(
{
username: "rahul",
conversations: {
$elemMatch: { receiver: data.receiver }
}
},
// or whatever your update is
{$push: {"conversations.message": message}
})

Mongoose not saving changes to a document

I'm sorry if this might be a duplicate question but I'm quite having a hard time understanding Mongoose. I am working on a Node.js project that implements Mongoose and MongoDB. What I want to accomplish is to modify and save some users' data through a call from a specific endpoint.
Mongoose Schema looks like this
var UserSchema = new Schema({
isAdmin: {type: Boolean, default: false},
name: String,
surname: String,
nickname: { type: String },
email: { type: String, lowercase: true, required: true, trim: true, unique: true, dropDubs: true },
password: { type: String, required: true },
salt: { type: String },
verified: { type: Boolean, default: false },
bio: {
type: { type: String, enum: [0,1] }, // 0='Appassionato', 1='Giocatore'
birthday: String,
height: Number,
number: Number,
role: { type: String, enum: [0,1,2,3] }, // 0='Playmaker', 1='Ala', 2='Guardia', 3='Centro'
team: String,
city: String,
aboutMe: String,
},
newsletter: {type: Boolean, default: false},
lastCheckin: {type: mongoose.Schema.Types.ObjectId, ref: 'Checkin'},
follows: [{type: mongoose.Schema.Types.ObjectId, ref: 'Structure'}],
score: { type: Number, default: 0 },
profilePicture: String,
lastLogin: {type: Date},
facebook: {
id: String,
accessToken: String,
profileImage : String
}
}, {
collection: 'users',
retainKeyOrder: true,
timestamps: true,
}).plugin(mongoosePaginate);
Following is the code for when the endpoint gets interrogated
exports.updateUser = (req,res) => {
var userId = req.params.userId;
var updates = req.body;
User.findOneAndUpdate({_id: userId}, {$set: updates}, (err, saved) => {
if (!err) {
console.log("Ritorno questo: " + saved);
return res.status(202).json(saved);
} else {
return res.status(500).json(saved);
}
});
};
As far as I understood, the method findOneAndUpdate exposed by Mongoose should find the document I'm looking for and then modify it and save it. This doesn't happen though.
Through PostMan I'm sending this JSON
{"bio.aboutMe":"Hello this is just a brief description about me"}
But PostMan is responding with the non-modified object. What am I missing here?
What you need to do is to add {new:true}, it give you back the updated document.
In the documentation :
If we do need the document returned in our application there is
another, often better, option:
> Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true },
> function (err, tank) { if (err) return handleError(err);
> res.send(tank); });
This is something I don't really like as there is another option if we don't want to have the document → update
So what you need to do is :
User.findOneAndUpdate({_id: userId}, {$set: updates}, {new:true}.....

Categories