How to not pass the value by function when using mongoose - javascript

There is an api as below.
try catch is omitted here.
exports.test = async(req,res) => {
const {clientId} = req.body;
// There is a function that makes an object to use repeatedly.
function errorReason(arg1) {
const errorLogs = new ErrorLogs({
clientId,
reason: arg1,
});
return errorLogs;
}
errorReason("server error").save();
And this is the type of clientId.
clientId: mongoose.Schema.Types.ObjectId
When I can pass obejctId or empty String for clientId as request.body.
But when I send empty string, it occurs an error.
validation failed: clientId: Cast to ObjectID failed for value \"\"
at path \"clientId\"
How can I make correct function of errorReason? I don't want to save "null" or "empty string" for clientId, even though clientId is empty string.
I hope there isn't clientId for 'new ErrorLogs', when clientId is empty string.
function errorReason(arg1) {
const errorLogs = new ErrorLogs({
reason: arg1,
});
return errorLogs;
}
Thank you so much for reading it.

You can achieve this by making the required field in your schema consume a function. This function decides whether the field is required depending on the type. This will allow you to save an empty string.
If you want to save an empty string:
to save an empty string:
const mongoose = require('mongoose');
const errorSchema = new mongoose.Schema({
clientId: {
type: mongoose.Schema.Types.ObjectId,
required: function(){
return typeof this.clientId === 'string' ? false : true
},
}
});
const Errors = mongoose.model('errors', errorSchema)
Update: Seems I misunderstood your intention. If you just want to not set the field clientId in the document, pass undefined to the Schema when you have an empty string. Mongoose will not save the field (unless you have it required, in which case mongoose will throw an error)
exclude empty string field from saved document:
function errorReason(arg1) {
const errorLogs = new ErrorLogs({
clientId: clientId || undefined, // handles '', undefined, null, NaN etc
reason: arg1,
});
return errorLogs;
}
errorReason("server error").save();

You should use code like bellow.
exports.test = async(req,res) => {
const {clientId} = req.body;
// There is a function that makes an object to use repeatedly.
function errorReason(arg1) {
const errorLogs = new ErrorLogs({
clientId : clientId, // add here clientId for asign clientId to field
reason: arg1,
});
return errorLogs;
}
if(!!clientId){ //Here !! means not equal to blank,null,undefine and 0
errorReason("server error").save();
}

Related

getting error ValidatorError: Path `id` is required. in mongo DB

I am getting this error while saving my document on collection
ValidatorError: Path id is required.
here is my code
https://codesandbox.io/s/lively-tree-hd0fo
const BlogPost = new Schema({
id: { type: String, required: true, unique: true },
empid: String,
date: Date
});
BlogPost.pre("save", function(next) {
var blog = this;
console.log();
var data = `${blog.empid}-${blog.date}`;
blog.id = crypto
.createHash("md5")
.update(data)
.digest("hex");
next();
});
getting the error when I am trying to save data.
a
pp.get("/saveData", async () => {
try {
var blog = new BlogPostModel({
empid: "test123",
date: "19-Jul-2019"
});
console.log("before save");
let saveBlog = await blog.save(); //when fail its goes to catch
console.log(saveBlog); //when success it print.
console.log("saveBlog save");
} catch (error) {
console.log(error);
}
});
In Validation docs, it says:
Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default.
And in Save/Validate Hooks, it says:
The save() function triggers validate() hooks, because mongoose has a built-in pre('save') hook that calls validate(). This means that all pre('validate') and post('validate') hooks get called before any pre('save') hooks.
So it will validate before your pre('save') hook and give the error because you didn't provide the required field. You can solve it by changing pre('save') to pre('validate').
Editted sandbox: https://codesandbox.io/s/agitated-lederberg-rth1k
when you are saving your blog documents you are not passing id field.
id is automatically generated by mongo, you don't need to specify it.
Remove id from BlogPost schema.
But if you want to give your own id then pass a unique id.
const BlogPost = new Schema({
empid: String,
date: Date
});

Update Array attribute using Mongoose

I am working on a MEAN stack application in which i defined a model using following schema:
var mappingSchema = new mongoose.Schema({
MainName: String,
Addr: String,
Mapping1: [Schema1],
Mappings2: [Schema2]
},
{collection : 'Mappings'}
);
I am displaying all this data on UI and Mapping1 & Mapping2 are displayed in the 2 tables where I can edit the values. What I am trying to do is once I update the values in table I should update them in database. I wrote put() api where I am getting these two updated mappings in the form of object but not able to update it in database. I tried using findAndModify() & findOneAndUpdate() but failed.
Here are the Schema1 & Schema2:
const Schema1 = new mongoose.Schema({
Name: String,
Variable: String
});
const Schema2 = new mongoose.Schema({
SName: String,
Provider: String
});
and my put api:
.put(function(req, res){
var query = {MainName: req.params.mainname};
var mapp = {Mapping1: req.params.mapping1, Mapping2: req.params.mapping2};
Mappings.findOneAndUpdate(
query,
{$set:mapp},
{},
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.log(object);
}
});
});
Please suggest the best to way update those two arrays.
UPDATE :
I tried this
var mapp = {'Mapping2': req.params.mapping2};
Mappings.update( query ,
mapp ,
{ },
function (err, object) {
if (err || !object) {
console.log(err);
res.json({
status: 400,
message: "Unable to update" + err
});
} else {
return res.json(object);
}
});
what I got is
My array with size 3 is saved as String in Mapping2 array.
Please help. Stuck badly. :(
From Mongoose's documentation I believe there's no need to use $set. Just pass an object with the properties to update :
Mappings.findOneAndUpdate(
query,
mapp, // Object containing the keys to update
function(err, object) {...}
);

JSON object architecture looks different when I pass it to the client side

Here is my Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var messageSchema = new Schema({
requestNumber: String,
requestedDateTime: String,
reasons: String,
state: String,
hospital: String,
phone: String,
status: {type: String, default: 'Pending'},
latestUpdate: Date,
createdAt: {type: Date, default: Date.now}
});
module.exports = mongoose.model('Requests', messageSchema);
Below I am returning the collection with three components in it
ipcMain.on('load-requests', function(event) {
hosSchemaModel.find(function(err, hosSchema) {
if (err) {
console.log('inside error') // return res.send(err);
} else {
event.sender.send('requests-results', hosSchema) // this line of code passes hosSchema to the client side
console.log(hosSchema[0].state) //prints the state attribute of the first component in the collection without any errors.
}
});
});
When I try to console.log(hosSchema) in the server, I get the following printed to the terminal:
and I could successfully access the properties such as status of the first component in the collection by referring to its index hosSchema[0].status.
Below I am trying to print hosSchema to the console (in the front-end)
ipcRenderer.on('requests-results', (event, hosSchema) => {
console.log(hosSchema)
})
I get the result different from what they were looking in the terminal. below is the picture
and hosSchema[0].status returns undefined.
My questions are:
1) why hosSchema[0].status doesn't work in the front-end?
2) what is the correct way to access the properties in the client-side?
All you have to do in the front end is to use hosSchema[0]._doc.status instead of hosSchema[0].status

Sequelize is transforming my string field to a number if the contents represent a number

My string field is getting cast to a number if the contents of the field is a number. For example, if I have store the text 11 in the field Table.value, and I execute the following code:
let Sequelize = require('sequelize');
var sequelize = new Sequelize('sqlite://dbName', {
host: 'localhost',
dialect: 'sqlite',
storage: './service/data.db'
});
var Table = sequelize.define('Table', {
value: {
type: Sequelize.STRING
}
});
Table.findAll().then(results => {
console.log(results[0].value, typeof results[0].value);
});
... the result will be 11 'number'. If I change that same field to 11a, then it returns the correct type (STRING).
This seems like a bug to me (it should return as a STRING because that's how I defined it). Doesn't seem like anyone has encountered this issue from Sequelize's Issue tracker, but maybe I'm missing something?

Why can't I delete a mongoose model's object properties?

When a user registers with my API they are returned a user object. Before returning the object I remove the hashed password and salt properties. I have to use
user.salt = undefined;
user.pass = undefined;
Because when I try
delete user.salt;
delete user.pass;
the object properties still exist and are returned.
Why is that?
To use delete you would need to convert the model document into a plain JavaScript object by calling toObject so that you can freely manipulate it:
user = user.toObject();
delete user.salt;
delete user.pass;
Non-configurable properties cannot be re-configured or deleted.
You should use strict mode so you get in-your-face errors instead of silent failures:
(function() {
"use strict";
var o = {};
Object.defineProperty(o, "key", {
value: "value",
configurable: false,
writable: true,
enumerable: true
});
delete o.key;
})()
// TypeError: Cannot delete property 'key' of #<Object>
Another solution aside from calling toObject is to access the _doc directly from the mongoose object and use ES6 spread operator to remove unwanted properties as such:
user = { ...user._doc, salt: undefined, pass: undefined }
Rather than converting to a JavaScript object with toObject(), it might be more ideal to instead choose which properties you want to exclude via the Query.prototype.select() function.
For example, if your User schema looked something like this:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
name: {
type: String,
required: true
},
pass: {
type: String,
required: true
},
salt: {
type: String,
required: true
}
});
module.exports = {
User: mongoose.model("user", userSchema)
};
Then if you wanted to exclude the pass and salt properties in a response containing an array of all users, you could do so by specifically choosing which properties to ignore by prepending a minus sign before the property name:
users.get("/", async (req, res) => {
try {
const result = await User
.find({})
.select("-pass -salt");
return res
.status(200)
.send(result);
}
catch (error) {
console.error(error);
}
});
Alternatively, if you have more properties to exclude than include, you can specifically choose which properties to add instead of which properties to remove:
const result = await User
.find({})
.select("email name");
The delete operation could be used on javascript objects only. Mongoose models are not javascript objects. So convert it into a javascript object and delete the property.
The code should look like this:
const modelJsObject = model.toObject();
delete modlelJsObject.property;
But that causes problems while saving the object. So what I did was just to set the property value to undefined.
model.property = undefined;
Old question, but I'm throwing my 2-cents into the fray....
You question has already been answered correctly by others, this is just a demo of how I worked around it.
I used Object.entries() + Array.reduce() to solve it. Here's my take:
// define dis-allowed keys and values
const disAllowedKeys = ['_id','__v','password'];
const disAllowedValues = [null, undefined, ''];
// our object, maybe a Mongoose model, or some API response
const someObject = {
_id: 132456789,
password: '$1$O3JMY.Tw$AdLnLjQ/5jXF9.MTp3gHv/',
name: 'John Edward',
age: 29,
favoriteFood: null
};
// use reduce to create a new object with everything EXCEPT our dis-allowed keys and values!
const withOnlyGoodValues = Object.entries(someObject).reduce((ourNewObject, pair) => {
const key = pair[0];
const value = pair[1];
if (
disAllowedKeys.includes(key) === false &&
disAllowedValues.includes(value) === false
){
ourNewObject[key] = value;
}
return ourNewObject;
}, {});
// what we get back...
// {
// name: 'John Edward',
// age: 29
// }
// do something with the new object!
server.sendToClient(withOnlyGoodValues);
This can be cleaned up more once you understand how it works, especially with some fancy ES6 syntax. I intentionally tried to make it extra-readable, for the sake of the demo.
Read docs on how Object.entries() works: MDN - Object.entries()
Read docs on how Array.reduce() works: MDN - Array.reduce()
I use this little function just before i return the user object.
Of course i have to remember to add the new key i wish to remove but it works well for me
const protect = (o) => {
const removes = ['__v', '_id', 'salt', 'password', 'hash'];
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
and i use it as I said, just before i return the user.
return res.json({ success: true, user: await protect(user) });
Alternativly, it could be more dynamic when used this way:
const protect = (o, removes) => {
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
return res.json({ success: true, user: await protect(user, ['salt','hash']) });

Categories

Resources