Mongodb/Mongoose: one field will not updated by empty string - node.js

i am new in MongoDB/Mongoose and have the following problem:
my schema is nested with:
profile: {
school: {type: String, required: false},
subject: {type: String, required: false},
graduation: {type: String, required: false}
}
now i want to update school, subject and graduation with:
userRouter.put('/:id', function(req, res, next){
var updateObject = req.body;
User.findByIdAndUpdate(req.params['id'], updateObject,{upsert: true}, (err, success)=> {
if(err)
res.status(404).send(err);
res.status(200).send();
});
});
i know the put method replace the object, but the patch method doesn't run in my code. Now, when i sent:
{"school": "", "subject": "c", "graduation": ""}
subject and graduation will be overwritten, but school will not be empty - it contains the old stuff.
Do you have a solution? Thanks.

Tested like this school and graduation were modified...
let updateObject = {
profile: {school: "", subject: "c", graduation: ""}
};
user.findByIdAndUpdate(
req.params['id'],
updateObject,
{upsert: true},
function(err, success) {
if (err)
res.status(404).send(err);
res.status(200).send();
}
);
Used this testing schema:
new mongoose.Schema({
created: {type: Date, required: true, default: Date.now},
modified: {type: Date, required: true, default: Date.now},
profile: {
school: {type: String, required: false},
subject: {type: String, required: false},
graduation: {type: String, required: false}
}
},
{
collection: 'db_test',
strict: 'throw',
toJSON: {getters: true},
toObject: {getters: true}
}
);

Related

How to filter documents on mongodb based on reference populated subdocuments?

I am trying to grab documents based on populated subdocuments.
Here are my models
// User model
var UserSchema = new mongoose.Schema({
username: {type: String, required: true, trim: true},
firstName: {type: String, required: true, lowercase: true},
lastName: {type: String, required: true, lowercase: true},
phone: {type: String, required: false},
email: {type: String, required: true},
password: {type: String, required: true},
blogs: {type: mongoose.Schema.Types.ObjectId, ref: 'Blogs'}
}, {timestamps: true});
// Blog Model
var BlogSchema = new mongoose.Schema({
description: String,
tags: [String],
other: [Object],
}, {timestamps: true});
This is how I am grabbing documents
fetchAllByFilter: async function(req, res) {
try {
let result = await Users.find({}).populate('blog');
return res.status(200).send(result);
} catch (err) {
return res.status(200).send({error: err});
}
},
Now my main question is, how would I grab Users based on their Blogs referenced documents?
For example, Find Users with Blogs that has Blog.tags of "food", "cars", "movies" and/or Blog.other of [{...SomeObject}, {...SomeOtherObject}]
looking at mongo docs match an array, you could make a utility function somewhat like this...
async function findByTag(tag) {
const blogIds = await Blog.find({ tags: tag }).select("_id");
const users = await User.find({
blogs: { $in: blogIds.map((blog) => blog._id) }
}).populate("blog");
}

How to make some fields not updatable once saved in mongoose?

I have build a schema as follows:
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true },
username: { type: String, required: true, unique: true },
userId: { type: Schema.Types.ObjectId, ref: 'User'},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
What I need here is once the fields such as email, username and userId are saved, should not be modified. Is there anything pre-build in mongoose for this kind of feature?
I have done some research on schema.pre('update', (next) => {}), but got nothing really useful/don't know if one can use for the mentioned feature. Any help on this matter is greatly appreciated. Thanks in advance.
There is an easier way
when you save the Schema, you can set the field as immutable, like this
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true, immutable:true },
username: { type: String, required: true, unique: true, immutable:true },
userId: { type: Schema.Types.ObjectId, ref: 'User', immutable:true},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
it won't throw any error, if you want it you should check it elsewhere, but when you try to modify the immutable fields, it wont be changed at all
for(const key in userUpdates) {
switch(key) {
case 'username':
case 'email':
throw new Error('These field/s cannot be changed anymore');
}
}
User.findByIdAndUpdate(id, userUpdates, { new: true, runValidators: true });

Mongoose query via populate

I have 2 models.
Model 1:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
passwordResetToken: String,
passwordResetExpires: Date,
facebook: String,
twitter: String,
tokens: Array,
profile: {
name: String,
gender: String,
location: String,
website: String,
picture: String
}
}, { timestamps: true });
Model 2:
const reviveSchema = new mongoose.Schema({
reviveShowName: {type: String, required: true},
reviveTitle: {type: String, required: true},
reviveCategory: {type: String, required: true},
reviveGoal: {type: Number, required: true},
revivePhoto: {type: String, required: true},
reviveVideo: {type: String},
reviveStory: {type: String},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
}
}, { timestamps: true });
How can I pass the author's name to the show view of a particular revive?
This is how I was getting to the show view before I realized that I needed the author's data to be passed to the view as well:
exports.showRevive = (req, res, next) => {
Revive.findById(req.params.id, (err, foundRevive) => {
if(err) {
console.log(err);
} else {
res.render('revive/show_revive', {revive: foundRevive});
}
});
};
That works just fine but then to get the author's data in the revive show view as well I tried this:
exports.showRevive = (req, res, next) => {
Revive.findById(req.params.id)
.populate('author')
.exec(function(err, foundRevive) {
if(err) {
console.log(err);
} else {
res.render('revive/show_revive', {revive: foundRevive});
}
});
};
That did not work... Can you guys please point me in the right direction? Thanks!

BrainTree node integration

I have the most Odd problem I have ever had. I just simple cannot find what is wrong with my code.
this is my user model
var userSchema = new Schema({
firstName: {type: String, required: true, trim: true},
lastName: {type: String, required: true, trim: true},
address: {
street: {type: String, required: true, trim: true},
city: {type: String, required: true, trim: true},
state: {type: String, min: 2, max: 2, required: false},
zipCode: {type: String, required: true, trim: true}
},
customerId: String,
subscription: {
id: String,
status: String,
planId: String,
price: Number,
nextBillingDate: Date
},
creditCard: {
token: {type: String, required: true}
},
email: {type: String, required: true, unique: true, trim: true},
password: {type: String, required: true, trim: true},
phone: {type: String, required: true, trim: true},
birthDate: {type: Date, required: true, trim: true},
role: {staff: Boolean, admin: Boolean, sAdmin: Boolean},
companies:[
{
type: Schema.Types.ObjectId,
ref: 'Company',
required: true
}
],
edited: {type: Date},
created: {type: Date, default: Date.now}
});
Now im creating a User Admin on my application with this code.
// BrainTree API
var nonce = req.body.payment_method_nonce;
gateway.customer.create({
firstName: user.firstName,
lastName: user.lastName,
company: req.body.compName,
email: req.body.email,
paymentMethodNonce: nonce
}, function (err, result) {
if(err) return handleError(err);
//NO ERROR FOUND
user.customerId = result.customer.id;
gateway.subscription.create({
paymentMethodToken: result.customer.paymentMethods[0].token,
planId: 'myId'
}, function (err, result) {
if(err){
throw err;
}
else {
//save subscription info
user.subscription.id = result.subscription.id;
user.subscription.status = result.subscription.status;
user.subscription.planId = result.subscription.status;
user.subscription.price = result.subscription.price;
user.subscription.nextBillingDate = result.subscription.nextBillingDate;
//transaction info
t.id = result.subscription.transactions[0].id;
t.amount = result.subscription.transactions[0].amount;
t.creditCard.maskedNumber = result.subscription.transactions[0].creditCard.maskedNumber;
t.created = result.subscription.transactions[0].createdAt;
saveAdmin();
}
});
now on user.customerId = result.customer.id;
I get this console error "Cannot read property 'id' of undefined", but that is not true. when I do a console.log(result.customer.id) I get the Id. I just don't know why is throwing the error.
I don't know if anybody can help me with this because but you never know.
Thanks in advance.
I solved the problem the return handleError(err) some how was executing the request twice. so I change the return handleError(err) with return console.error(err) and it solved the problem.

Mongoose query reference

I have the below schema structure for my application:
var User = new Schema({
name: {type: String, required: true},
screen_name: {type: String, required: true, index:{unique:true}},
email: {type: String, required: true, unique:true},
created_at: {type: Date, required: true, default: Date}
});
var Line = new Schema({
user: {type: Schema.ObjectId, ref: 'User'},
text: String,
entered_at: {type: Date, required: true, default: Date}
});
var Story = new Schema ({
sid: {type: String, unique: true, required: true},
maxlines: {type: Number, default: 10}, // Max number of lines per user
title: {type: String, default: 'Select here to set a title'},
lines: [Line],
created_at: {type: Date, required: true, default: Date}
});
var Story = db.model('Story', Story);
var User = db.model('User', User);
When i query a Story using the sid i want it to output the lines in that story but also the users screen_name.
Currently i'm using the below to query the story:
app.get('/api/1/story/view/:id', function(req, res){
Story.findOne({ sid: req.params.id }, function(err, story){
if (err) {
res.json(err);
}
else if(story == null) {
res.json(err);
}
else{
res.send(story);
}
});
});
Which just brings back the result like this:
{
"__v": 1,
"_id": "5117878e381d7fd004000002",
"sid": "DIY0Ls5NwR",
"created_at": "2013-02-10T11:42:06.000Z",
"lines": [
{
"user": "511782cab249ff5819000002",
"text": "TESTING",
"_id": "51178795381d7fd004000003",
"entered_at": "2013-02-10T11:42:13.000Z"
}
],
"title": "Select here to set a title",
"maxlines": 10
}
There is a user set-up with that _id but i'm not entirely sure how to output the story and for it to include the users screen_name or even the entire user information along with the output
{
"__v": 0,
"screen_name": "Tam",
"email": "test#test.com",
"name": "Tam",
"_id": "511782cab249ff5819000002",
"created_at": "2013-02-10T11:21:46.000Z"
}
I haven't tested it, but you should be able to use populate to get the full user doc for each lines element like this:
Story.findOne({sid: req.params.id})
.populate('lines.user')
.exec(function(err, story){ ...

Resources