NodeJS & Mongoose, update values in array of objects not working - node.js

I am struggling to update some specific arrays in my UserSchema with the mongoose function findByIdAndUpdate().
This is my UserSchema:
const UserSchema = new mongoose.Schema({
mail: {type: String, required: true, unique: true},
password: {type: String, required: true},
friends: [{id: String}],
prot: [{
id: String,
admin: Boolean,
}]
});
I only want to update the prot element, this is how I want todo this:
User.findByIdAndUpdate(req.body.userId, {
$set: { prot: [{ id: req.body.lockId, admin: req.body.isAdmin }] }, function(err, user) {
if (err) {
return res.status(500).send({
message: err.message || "Some error occured while updating user"
});
}
if (!user) {
return res.status(404).send({
message: "User not found"
});
}
return res.status(200).send(user);
}
})
But when I try to send a request via Postman, I didn't get an response or error..

FindByIdAndUpdate doesn't return updated document per default, you should add option {new:true}. You have mixed brackets too. Do it like below:
User.findByIdAndUpdate(
req.body.userId,
{
$set: {
prot: [{
id: req.body.lockId,
admin: req.body.isAdmin
}]
}
},
{ new: true },
function(err, user) {
if (err) {
return res.status(500).send({
message: err.message || "Some error occured while updating user"
});
}
if (!user) {
return res.status(404).send({
message: "User not found"
});
}
return res.status(200).send(user);
}
);

If you want to update a specific record inside an array of objects. You can do it like this.
User.update(
{ _id: req.body.userId,
prot:
{ $elemMatch: { id: req.body.lockId }}
},
{ $set:
{ prot: { admin: req.body.isAdmin }
}
},(error,result)=>{
if(error){
//handle error
}
console.log(result);
}
)

Related

Adding json response to a Map field Mongoose Node

I'm just starting out whit node and mongoose and I'm trying to create a user, then create a stripe customer with a stripe generated id and save the response in a user Map field stripeDetails.
Here is the schema:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
photoUrl: { type: String, required: true },
phoneNumber: { type: String, required: false },
address: { type: String, required: false },
zipCode: { type: String, required: false },
city: { type: String, required: true },
region: { type: String, required: true },
country: { type: String, required: true },
isVerified: { type: Boolean, required: false, default: false },
lastLogin: { type: Number, required: false, default: Date.now },
stripeDetails: {type: Map, required: false}
},
{ timestamps: true });
module.exports = mongoose.model('User', userSchema, 'Users');
I tried setting the stripeDetails field like
.then(stripeCustomer => {
console.log('Stripe.customer.create', stripeCustomer);
result.set('stripeDetails', stripeCustomer);
result.save();
...
but is not working.. I settle to update the record's field but is a bit messy..
exports.createUser = async (req, res) => {
const user = req.body;
console.log('User is :', user);
/// Creat use in DB
User.create(
user,
function (err, result) {
if (err) {
console.log('Mongoose createUser error: ', err);
res.statut(503).send({ error: "Internal error" });
return;
}
console.log('Mongoose createUser: ', result);
res.status(200).send({
message: "User created successfully!",
data: result
});
/// Create stripe customer
stripe.customers.create({
"address": {
"city": user.city,
"state": user.region,
"country": user.country,
"postal_code": user.zipCode
},
"balance": 0,
"created": Date.now,
"email": user.email,
"name": user.name,
"phone": user.phoneNumber,
"preferred_locales": [],
"shipping": null,
"tax_exempt": "none"
})
.then(stripeCustomer => {
console.log('Stripe.customer.create', stripeCustomer);
// save stripe details to db
//not working..
// result.set('stripeDetails', stripeCustomer);
// result.save();
// working
User.findByIdAndUpdate(
result.id,
{stripeDetails: stripeCustomer},
{ new: true },
function(err, result) {
if (err) {
console.log('Stripe customer not updated to db: ', err);
}
if (result != null){
console.log('Stripe customer updated to DB', result);
} else {
console.log('Stripe customer to update not found in db ');
}
}
);
})
.catch(error => {
console.log('Stripe.customer.create error: ', error);
});
}
);
};
also I can't access the stripeDetails.id value for when I need to delete the user..
exports.deleteUserById = async (req, res) => {
const id = req.params.id;
User.findByIdAndDelete(
id,
function (err, result) {
if (err) {
console.log('Mongoose deleteUserById error: ', err);
res.statur(505).send({ errro: "Internal error" });
}
if (result != null) {
console.log('Mongoose deleteUserById: ', result);
res.status(200).send({
message: "User found!",
data: result
});
console.log('stripe id is: ', result.stripeDetails['id']);
stripe.customers.del(`${result.stripeDetails['id']}`)
.then(stripeCustomer => {
console.log('Stripe.customer.delete', stripeCustomer);
})
.catch(error => {
console.log('Stripe.customer.delete error: ', error);
});
} else {
console.log("Mongoose deleteUserById: user not found");
res.status(404).send({ message: "User not found" });
}
});
}
I could use the mongoose _id as the stripe id but I rather use their own separate id generators and keep the ids separate, and get used to work with maps in mongoose. Can you see what I'm doing wrong with in writing and reading stripeDetails?
Try to change stripeDetails in Schema to be of type Object:
stripeDetails: {type: Object, required: false}
Now you can do this:
.then(stripeCustomer => {
result.stripeDetails.details = stripeCustomer;
User.findByIdAndUpdate(result.id, result).then((result)=>{
console.log('User updated.');
})
})
Note that when you are using Map as a type, you should access the value of a key with .get(). So try to access stripe_id like this:
let stripe_id = result.stripeDetails.get("id");
stripe.customers.del(stripe_id)
.then(stripeCustomer => {
console.log('Stripe.customer.delete', stripeCustomer);
})
.catch(error => {
console.log('Stripe.customer.delete error: ', error);
});
Check it here.

Unique array in Mongoose is not throwing error when same key is stored again

I try to store friends in my friends collection. This collection contains a field userId which is an array of user ids. When I store the same id again I want mongoose to throw an error.
My friend schema looks like this:
const friendSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: [{
type: mongoose.Schema.Types.ObjectID,
unique: true,
required: true,
ref: 'User',
}],
});
I am calling it like this:
Friends.findByIdAndUpdate({_id: req.userData.userId}, {$addToSet: { userId: req.body.id } }, {safe:false, upsert: true}, function (error, friend) {
if(error){
return res.status(500).json({
message: 'You already added this user as friend! ' +error,
});
}else if (!friend) {
return res.status(401).json({
message: 'Authentication failed',
});
} else {
Friends.
find({_id: req.userData.userId})
.populate('userId')
.exec(function(error, posts) {
if(!error) {
let returnValue = [];
posts.map((x)=>{
returnValue = x.userId;
})
return res.status(200).json(returnValue);
}else {
return res.status(400).json({message: error.message});
}
})
}

Nested array value comparison with another array value in mongoose using nodejs

How can I get addedproducts array elements where productids' of addedproducts array match with every id in productids' array of invitationfrom array(for a particular user email) in the below structure?
var UserSchema = new Schema(
{ email:
{ type: String,
unique: true,
required: true
},
addedproducts:[ {
name: String,
productid:String,
producttype:String
} ],
invitationfrom : [ {
fromemail: String,
productid:[String]
}]
}, {collection: 'user-data-db'});
Try this one
User.find({'addedproducts.productid': "123456"}, {'invitationfrom.productid': "123456"})
.exec(function (err, user) {
if (err) {
return res.status(500).json({message: err});
}
if (!user) {
return res.status(404).json({message: 'No Match Found'});
} else {
return res.status(200).json(user);
}
});

How to check if an Id exist in mongodb array if not push the Id into the array

am using mongoose for my schema design i want to check a user collection if a device id exist in am array if false push the device into the user devices array
here is ma user schema
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var deviceSchema = mongoose.Schema(
{
macAddress: {type: String, required: true, unique: true},
createdAt: {type: Date, default: Date.now},
}
)
var Device = mongoose.model('Device', deviceSchema);
module.exports = Device;
here is my user schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = Schema(
{
firstname: {type: String, required: true},
lastname: {type: String, required: true},
email: {type: String, required: true, unique: true},
username: {type: String, required: true, unique: true},
password: {type: String, required: true},
createdAt: {type: Date, default: Date.now},
devices: [{ type: Schema.Types.ObjectId, ref: 'Device1' }]
}
)
var User = mongoose.model('User', userSchema);
module.exports = User;
and here is my nodejs router
router.post('/user/device/:username', function(req, res, next) {
if(!req.body.macAddress) {
sendJSONresponse(res, 400, {
"message": "Device Mac-Address required"
});
return;
}
User.findOne({ username: req.params.username }, function(err, user) {
if (err) { return next(err); }
if (!user) { return sendJSONresponse(res, 400, {
"message": "user not found"
});
}
Device.findOne({macAddress : req.body.macAddress}, function(err, device){
if(err){return next(err);}
if(!device){
sendJSONresponse(res, 400, {
"message": "No device with that macaddress"
});
return;
}
User.find({ devices: { $elemMatch: { $eq: req.body.macAddress} } }, function(err, users){
if(err){return next(err);}
if(users){
sendJSONresponse(res, 400, {
"message": "Device already assigned to a user"
});
return;
}else{
user.devices.push(device._id);
user.save(function(err) {
if(err){return next(err);}
sendJSONresponse(res, 200, user);
});
}
});
});
});
});
after doing all this when i try using the api with postman is tells me The server couldn't send a response: please help me fix my code
You are pushing device Id to devices array
user.devices.push(device._id)
but you are trying to elemMatch macAddress
User.find({ devices: { $elemMatch: { $eq: req.body.macAddress} } }..
When it should be
User.find({ devices: { $elemMatch: { $eq: device._id} } }..
Your have to change elemMatch param to device._id to make it correct.
router.post('/user/device/:username', function(req, res, next) {
if(!req.body.macAddress) {
sendJSONresponse(res, 400, {
"message": "Device Mac-Address required"
});
return;
}
User.findOne({ username: req.params.username }, function(err, user) {
if (err) { return next(err); }
if (!user) { return sendJSONresponse(res, 400, {
"message": "user not found"
});
}
Device.findOne({macAddress : req.body.macAddress}, function(err, device){
if(err){return next(err);}
if(!device){
sendJSONresponse(res, 400, {
"message": "No device with that macaddress"
});
return;
}
User.find({ devices: { $elemMatch: { $eq: device._id} } }, function(err, users){
if(err){return next(err);}
if(users){
sendJSONresponse(res, 400, {
"message": "Device already assigned to a user"
});
return;
}else{
user.devices.push(device._id);
user.save(function(err) {
if(err){return next(err);}
sendJSONresponse(res, 200, user);
});
}
});
});
});
});
Use debugger to go through code step by step, and see what's going on. If you don't know how to debug, take a look at https://code.visualstudio.com/docs/editor/debugging. It is a free tool.

Error:Can't canonicalize query: BadValue Unsupported projection option

User Schema
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false},
favouriteid:[{eventid:String}]
});
Event Schema
var EventSchema=new Schema({
name:String,
location:{ type:String },
description:{type:String },
price: String,
rating: {
value: String,
count: {type: String, default: 10},
userrating: [{
uservalue: String,
userid: String
}]
},
imageurl:[String],
userimageurl:[String],
reviews:[{ userid:String,
username: String,
comment:String}]
});
POST METHOD to push the value of userid and uservalue in Event Schema.
api.post('/rateevent', function (req, res) {
var userid = req.body.userid;
var uservalue = req.body.uservalue;
var eventid = req.body.eventid;
Event.findById({_id: eventid},
{$push: {rating: {userrating: {uservalue: uservalue, userid: userid}}}},
{upsert: true},
function (err, events) {
if (err) {
res.send(err);
return;
}
else {
calculaterating(events);
}
});
function calculaterating(event) {
event.rating.count++;
event.rating.value = (event.rating.value * (event.rating.count - 1) + uservalue) / event.rating.count;
res.json("rating updated");
}
});
It is showing the following error:
{
"name": "MongoError",
"message": "Can't canonicalize query: BadValue Unsupported projection option: $push: { rating: { userrating: { uservalue: \"5\", userid: \"56593f3657e27af8245735d7\" } } }",
"$err": "Can't canonicalize query: BadValue Unsupported projection option: $push: { rating: { userrating: { uservalue: \"5\", userid: \"56593f3657e27af8245735d7\" } } }",
"code": 17287
}
Is the post method not correct? I have seen other mongodb documents but not able to find this type of thing. I am new to node js. Help me.
It should be Event.update instead of Event.findById, Also your push operation looks wrong. It should be like this:
Event.findOneAndUpdate(
{_id: eventid},
{$push: {'rating.userrating': {uservalue: uservalue, userid: userid}}},
{new: true},
function (err, events) {
if (err) {
res.send(err);
return;
}
else {
if(events.length > 0){
calculaterating(events);
}
else {
res.json({msg: "Nothing to update"});
}
}
});
function calculaterating(event) {
event = event[0]; //get the object from array
event.rating.count++;
event.rating.value = (event.rating.value * (event.rating.count - 1) + uservalue) / event.rating.count;
Event.update(
{_id: eventid},
{$set: {
'rating.count': event.rating.count,
'rating.value': event.rating.value
}},
function(err, response){
if (err) {
res.send(err);
return;
}
else {
res.json({msg: "rating updated"});
}
});
}
In events variable you will get the document that was updated in the new state. If you had passed {new: false} you will get the document as it was before the update.
in MY case
i was using the wrong method like below i was updating the record by
findOne , that can`t be possible , in my case , thats why issues
occurs
Solution: if you want to update the record , use .update() method,
and if you want to find records , then you can use .find() , .findOne() , don`t mismatch
domain.Cart.findOne({
UserId: req.body.UserId,
shopId: req.body.shopId,
},
{ $addToSet: { "productDetails": _productDetails } }
).exec(function (err, results) {
console.log(err, results)
callback(null, {
result: results,
msg: "productCount has been updated"
})
})

Resources