Mongoose Subdocument array pushing - node.js

My scenario is if person1 accepting person2 deal means..the person1_id will save inside that person2 particular deal field accepted,i have tried the code it was working perfectly if a accepted user(person2) has one deal but in case of more than one deal it was updating but deleting other deals (i.e,the suppose the person2 having 3 deals means if person1 accepting 3rd deal the accepted user id was updating in 3rd deal and the 1st and 2nd deal was deleted).Anyone please help me how to save only the updated deal array
var incomingUser = req.user;//accepting user accesstoken in header(person1)
if(req.params.id){
var id = req.params.id;//deal id
console.log("DealId:"+id + "Acceptinguser:"+incomingUser.name);
User.findOne(
{
"deals": {
$elemMatch: {
_id: id
}
}
},
function(err, data){
console.log("Dealer:" +data.name);
console.log("deal:"+ data.deals);
if(err){
console.log("User not found");
res.send(new restify.ResourceNotFoundError('failed','Deal not found'));
return next();
}
var dealObj = _.filter(data.deals, { id: id })[0];
console.log("Deal Obj" + dealObj);
var acceptingUser = incomingUser;
console.log("accepting user:" +acceptingUser._id);
dealObj.accepted = acceptingUser._id;
console.log("accept id: "+ dealObj.accepted);
data.deals = dealObj;
console.log("data:"+ data.deals);
data.save(function (err, result){
console.log("Result:" + result);
if(err){
console.log("Internal error");
res.send(new restifyc.InternalError('failed','Error accepting'));
return next();
}
console.log("saved");
res.send(200,{user: result});
return next();
});
});
}
}
And my schema is
var dealSchema = new mongoose.Schema({
shopName: {type: String,required: true},
deal: {type: String,required: true},
price:{type: Number,required: true},
start:{type: Date,default: Date.now},
end:{type: Date},
expiry:{type: Date},
comments:{type: String},
accepted: {type:mongoose.Schema.Types.ObjectId, ref:'user'},//person1 _id
rejected: {type:mongoose.Schema.Types.ObjectId, ref: 'user'}
});
var userSchema = new mongoose.Schema({
name: { type: String,required: true},
phone: { type: Number, required: true,unique: true},
email:{type: String},
password: {type: String},
deals:[dealSchema]
}, {collection: 'user'});
mongoose.model('Deal', dealSchema);
mongoose.model('user', userSchema);

Yep in order to update specifically what you need you can use the <array>.$ for the specified position of the element:
User.update(
"deals": {
$elemMatch: {
_id: id
}
}, {
"$set": {
"deals.$" : {/*your deal data*/}
}
}, function(err, doc) {
});
More details on how to use the $ wildcard https://docs.mongodb.org/manual/reference/operator/update/positional/

Related

How to update a field-List in MongoDB using Mongoose and Nodehs?

Hello dear Stackoverflow team.
I am trying to patch a user, which can handle several "devices". I am using nodeJs with Express and Mongoose (MongoDB). My User model is the following:
const userSchema = new Schema({
name: {type: String, required: true},
lastname: {type: String, required: true},
email: {type: String, required: true, trim: true, lowercase: true, unique:
true},
password: {type: String, required: true, minlength: 5},
userTyp: {type: String, required: true,
enum: {values: ['Administrator', 'Doctor','Patient','Optiker'], message:
'{VALUE} is not supported' }},
image: {type: String},
devices: [ {device: {type: Schema.Types.ObjectId, ref: "Device"}} ]
});
and I want to have something like this everytime i do a patch:
{
"user": {
"_id": "6138cd30ffc5239bba72e6c0",
"name": "Fernando",
"lastname": "Gonzalez",
"email": "f.gonzalez#unitransferklinik.de",
"password": "Hol087+/*",
"userTyp": "Administrator",
"image": "sdsadsadsa/asdfasdas",
"devices": [
{
"device": "6138c7587ab4b5fc4d369230"
},
{
"device": "6138c7587ab4b5fc4d365210"
}
],
}
}
How can I implement in my function:
const updateUser = async (req, res, next) => {
const { name, lastname, email, password, userTyp, device } = req.body;
const userID = req.params.userID;
let updatedUser;
try {
updatedUser = await User.findById(userID);
}catch(err){
console.log(err);
return next(new HttpError('Something happend.', 500));
}
updatedUser.name = name;
updatedUser.devices = [device, ...updatedUser.devices];
try{
updatedUser.save();
}catch (err) {
return next(new HttpError('It could not uodate device.', 500));
}
});
res.status(200).json({user: updatedUser.toObject( {getters: true} )});
};
In easy words, I want to updated the list everytime that i do a patch with a new device, and I can fetch later all the device list per user.
Thanks a lot!
regards,
Eliot
You can use findOneAndUpdate function of mongoose library
const dynamicModel = libMongoose.model(collection_name, userSchema);
var filter = { userID: req.params.userID };
var update = { name: name, devices : [...device, ...updatedUser.devices]};
//callback approach
dynamicModel.findOneAndUpdate(filter, update, (err, resp) => {
if(err) {
console.log("Error while updating record " + JSON.stringify(err));
}
if(!resp) {
console.log("Couldn't find record");
} else {
console.log("Updated data to DB");
}
});
You can also refer here for async await, Hope this helps!

Moongoose schema usage for update

Here is my schema
var DrivingSchema = new Schema({
title: { type: String, required: true },
permalink: {type: String, required: true},
phone: {type: Number, required: true},
mobile: {type: Number},
bike: {type: Boolean, default: false }
});
I used this schema for adding data. It worked fine.
But when I have to update data, I couldn't use this schema because it gave new _id. Here is my controller for update.
DriveModel.findOne({permalink: permalink}, function(err, data) {
if (err)
res.send(err);
var newData = new DriveModel({
title: title,
phone: phone,
mobile: mobile,
bike: bike});
DriveModel.update({_id:data._id}, newData, function(err, result) {
if (err)
res.send(err);
else{res.redirect('/centres/'+permalink);}
});
});
This controller didn't work because of _id conflict. Mongoose Schema documentation suggests to use _id: false in schema but it again works for update not for new insertion of data. Now, how could I solve this issue? Do I have to build another schema just for update or is there anyway to handle with same schema?
Try this one:
var elements = {"title": title, "phone": phone, "mobile": mobile, "bike": bike};
DriveModel.findOne({"permalink": permalink}, function(err, data) {
if (err) {
res.end(err);
}
for(elem in elements) {
data[elem] = elements[elem];
}
data.save(function(err, place) {
if(err) {
res.end(err);
} else {
res.redirect('/centres/'+permalink);
}
});
});

Mongoose one-to-many - not quite sure how to implement it

I'm relatively new to Mongoose (2 days at it) and I want to make a one-to-many relationship, as in one person can come from one country, one country has many people.
So, this is what I've got:
var userSchema = new Schema({
name: String,
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
country: {
type: Schema.Types.ObjectId,
ref: 'Country'
}
});
var User = mongoose.model('Person', userSchema);
var countrySchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
created_at: Date,
updated_at: Date,
people: [{
type: Number,
ref: 'User'
}]
});
var Country = mongoose.model('Country', countrySchema);
var UK = new Country({
name: 'UK'
});
usa.save(function(err) {
var user = new User({
username: 'James',
password: 'Bond',
country: UK._id
});
user.save(function(err) {
});
});
Now I have two questions: 1) I've seen that ref can sometimes be an ObjectId or just a number - what's the differences? 2) when saving the data, in my case, I saved country to a person (by _id), how do I save a person to a country? Should I update the instance of the model?
Thanks
UPDATE:
since this question has been marked as a duplicate, let me rephrase the question: consider the official example in this link: http://mongoosejs.com/docs/populate.html
The idea is that one person has many stories, and one story has one author (person). So, the saving would be as follows:
var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
});
That's from the official documentation - my question is, where or how do we save story1 to the Author? Author is created before the Story, so, shouldn't the Author be updated with story1._id???
UPDATE 2:
I figured out that if I use only type: Schema.Types.ObjectId and never type: Number, that I can do just this:
var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
aaron.stories.push(story1._id);
aaron.save(function (err) {
if (err) return handleError(err);
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
This actually works in a dummy example... are there any problems if there were too many posts in a request that IDs could have get lost/duplicated? What is the shortcoming of this approach?
1) I've seen that ref can sometimes be an ObjectId or just a number - what's the differences?
Please refer to this question Why do they use an ObjectId and a Number in the Mongoose Population example?
where or how do we save story1 to the Author
aaron.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// save id of story1 into person here, also you should use `update` operation with `$push` operator.
aaron.stories.push(story1._id);
aaron.save(function(err){
if (err)
handleError(err);
else
console.log('save person successfully...');
})
});
});
The results
> db.stories.find()
{ "_id" : ObjectId("56f72f633cf1e6f00159d5e7"), "title" : "Once upon a timex.", "_creator" : 0, "fans" : [ ], "__v" : 0 }
> db.people.find()
{ "_id" : 0, "name" : "Aaron", "age" : 100, "stories" : [ ObjectId("56f72f633cf1e6f00159d5e7") ], "__v" : 1 }

Mongoose.populate() not showing any change in DB

I have been trying to populate from a user table and have been unsuccessful. Any help would be appreciated.
I am checking a variable isProvider
if(true)
then the data is saved in a provider table
else
in a customer table.
I want the user table to be an Auth table, so I want to populate a field called "userId" in these models. The id is being saved. When i print the results of populate, It shows a populated json but when i see it in the database it shows only the Id. I want to access the details of user table through the photographer table. How do i achieve this ?
User model
/*
* Title: User model
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
//Data model
var UserSchema = new Schema({
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
token: String,
mobile: String,
type: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
isPhotographer: {type: Boolean, default: false},
verified: {type: Boolean, default: false}
});
UserSchema.pre('save', function(next) {
var user = this;
if(this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if(err) {
return next(err);
}
bcrypt.hash(user.password, salt, null, function (err, hash) {
if ( err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.methods.comparePassword = function (passw, cb) {
bcrypt.compare(passw, this.password, function( err, isMatch) {
if(err) {
return cb(err);
}
cb(null, isMatch);
});
};
UserSchema.plugin(passportLocalMongoose);
user = mongoose.model('User', UserSchema);
module.exports = user;
Provider model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var providerSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
ref: 'User'
},
firstName: String,
lastName: String,
profilePicture: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GFS'
},
email: String,
phone: Number,
address: String,
dob: Date,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
providerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
provider= mongoose.model('provider', providerSchema);
module.exports = provider;
Customer model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var customerSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
firstName: String,
lastName: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
customerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
customer = mongoose.model('Customer', customerSchema);
module.exports = customer;
Controller
if(user.isProvider) {
var provider= new providermodel({
userId: user._id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
provider.save(function(err, docs) {
if(!err) {
pprovidermodel.findOne({_id: provider._id}).populate('userId').exec(function(err, docs) {
if(err) {
console.log(err);
}
else {
console.log(docs); ----> **Here populate works, but no changes in the database**
console.log("SO " + docs.userId.email);
}
})
}
})
}else {
var customer = new customermodel({
userId: user.id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
customer.save(function(err) {
if(!err) {
customermodel.findOne({}).populate('userId').exec(function(err, docs)
{
console.log(err);
console.log(docs);
})
}
})
}
I think it's right. Populate don't change values in database only retrieve values when code is running.

Mongoose - Better solution with appending additional information

I have two Schemas:
var ProgramSchema = new Schema({
active: Boolean,
name: String,
...
});
var UserSchema = new Schema({
username: String,
email: { type: String, lowercase: true },
...
partnerships: [{
program: { type: Schema.Types.ObjectId, ref: 'Program' },
status: { type: Number, default: 0 },
log: [{
status: { type: Number },
time: { type: Date, default: Date.now() },
comment: { type: String },
user: { type: Schema.Types.ObjectId, ref: 'User' }
}]
}]
});
Now I want to get all Program docs, but also append 'status' to each doc, to return if the program is already in a partnership with the logged in user.
My solution looks like this:
Program.find({active: true}, 'name owner image user.payments', function (err, p) {
if(err) { return handleError(res, err); }
})
.sort({_id: -1})
.exec(function(err, programs){
if(err) { return handleError(res, err); }
programs = _.map(programs, function(program){
var partner = _.find(req.user.partnerships, { program: program._id });
var status = 0;
if(partner){
status = partner.status;
}
program['partnership'] = status;
return program;
});
res.json(200, programs);
});
The req.user object contains all information about the logged in user, including the partnerships array.
To get this solution to work, I have to append
partnership: Schema.Types.Mixed
to the ProgramSchema.
This looks a bit messy and thats why I am asking for help. What do you think?
When you want to freely modify the result of a Mongoose query, add lean() to the query chain so that the docs (programs in this case) are plain JavaScript objects instead of Mongoose doc instances.
Program.find({active: true}, 'name owner image user.payments')
.lean() // <= Here
.sort({_id: -1})
.exec(function(err, programs){ ...
Then you can remove partnership from your schema definition. Your query will also execute faster.

Resources