Changing mongoose model causes code to error - javascript

In my user schema I have a property:
verification: {
phoneVerification: {
verified: Boolean,
code: String,
},
however, if I change it to this:
verification: {
phoneVerification: {
verified: Boolean,
code: String,
carrier: String,
type: String
},
It changes my Javascript output which ends up causing an error for me.
let user = await User.findOne({ _id: req.user._id }, '-salt -password').exec();
user.verification.phoneVerification = {};
console.log(user.verification);
This code at first outputs:
{ phoneVerification: null,
mailVerification: { verified: false, code: '1c7d55d3d2e64ae98e82' } }
Then ends up outputting:
{ mailVerification: { verified: false, code: '1c7d55d3d2e64ae98e82' } }
After my model has been changed.
Because of the fact that phoneVerification isn't instantiated, my code is giving me back an error.
It's very weird, what is causing this to happen?
Edit:
I'm asking why adding another subproperty to the property on the user model, I can no longer instantiate this verification.phoneVerification object to eventually save properties to it. It's not a duplicate of above question.

Related

Mongoose update nested object in array of record

I am currently having a problem where I am trying to update an of a nested array in a Mongoose record.My schema is as follows:
const customerSchema = new mongoose.Schema({
kimCustomerId: {
type: Number,
required: true
},
addresses: [
{
created: Date,
updated: Date,
addressInfo: {
type: { type: String },
typeOfAddress: String,
careOf: String,
address: String,
addressRow1: String,
addressRow2: String,
zipcode: String,
city: String,
countryCode: String,
physicalAddressType: String,
validFrom: Date,
validTo: Date
}
}
],
.....
As you can see, the adrress array for each record holds many addresses. I want to be able to pass through an update object and update the properties inside the addressInfo nested object inside a particular array object. Here is my query as it stands:
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: { 'addresses.$': addressData } },
{ new: true }
);
and an example of an object I pass through to update a record:
{
addressId: officialAddressExists.addressId,
addressInfo: {
validTo: new Date().toISOString()
}
}
What I want to happen is, when I pass this object to the schema method, I want to select the correct address by the values 'kimCustomerId' and 'addressId' (which I have working fine) then only update the values of the 'addressInfo' nested object that I have passed and keep the ones not passed as they are, in this case the 'validTo' field but it can be any number of them updated. It is overwriting the whole 'addressInfo' nestedObject at the moment so I presume I have to do some kind of set operation on that nested object as well but I am unsure how.
Is anyone able to point me in the right direction here?
Thanks!
There is no straight way to do this in query, you can do it in your client side, something like,
// Sample body request
let addressData = {
addressId: 1,
addressInfo: {
validTo: new Date().toISOString(),
typeOfAddress: "Home",
address: "ABC"
}
};
let set = {};
for (let key in addressData.addressInfo) {
set["addresses.$.addressInfo." + key] = addressData.addressInfo[key];
}
console.log(set);
Pass set variable in to your query,
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: set },
{ new: true }
);

Is there a way to remove sub-document from object without passing the parentId?

Is there a way to remove sub-document from object without passing the parentId?
So I have a document which looks like this:
name: {
type: [String],
trim: true,
required: [true, 'Please add your name']
},
experience: [
{
title: {
type: String,
trim: true,
required: [true, 'Please add an experience title']
}
}
],
Each of this document can have several objects in the experience array.
Adding and updating has been easy for me because I usually just need to pass the Id of the parent document.
Now I would like to delete any object of said experience array by proving only its id...not the parentId.
Hopefull this code can help explain what I'm looking for:
const resume = await Resume.findByIdAndUpdate(
{
// _id: req.params.id, // <=== is the parent id...
experience: { _id: req.params.exp_id }
},
{
$pull: { experience: { _id: req.params.exp_id } }
},
{
new: true,
runValidators: true
}
);
You can see in the code above that I passed the _id of the parent document which if I continue this way, it works great but for my needs I need to only pass the _id of the subdocument.
Is that actually possible?
Yes you can, using {} searches in all parent documents.
parent.updateMany({}, {$pull, {children: {_id: id}}})
Please also make sure the child _id is a string and not ObjectId. If that's the case you need to first convert your string to ObjectId

Populating subfield from other collections (not refs)

I am trying to populate subfields of a document, which are not defined as refs. The problem is that mongoose keeps returning null, whenever I try to fetch the document and populate the fields.
I will try to make this a generic question. I haven't found an answer anywhere online.
schemaA:
const schemaA = new Schema({
before: {
type: Object,
default: {}
},
after: {
type: Object,
default: {}
}
});
module.exports = SchemaA = mongoose.model("schemaA", schemaA);
schemaB:
const schemaB = new Schema({
someField: {
subFieldA: {
type: String
},
subFieldB: {
type: String
}
}
});
module.exports = SchemaB = mongoose.model("schemaB", schemaB);
And an example document that would exist in schemaA is:
_id: ObjectId('5e4ab79d9d3ce8633aedf524')
before: {
someField: {
subFieldA: ObjectId('5e4ab74f9d3ce8633aedf2eb'),
subFieldB: ObjectId('5e4ab74f9d3ce8633aedf2ep')
},
}
after: {
someField: {
subFieldA: ObjectId('5e4ab74f9d4ce8633aedf2eb'),
subFieldB: ObjectId('5e4ab74f9d3ce8639aedf2ep')
},
}
date: 2020-02-17T15:56:13.340+00:00
My query:
const schemaAs = await SchemaA.find()
.populate(
"before.someField.subFieldA, before.someField.subFieldB, after.someField.subFieldA, after.someField.subFieldB"
)
But this query returns null. What am I doing wrong?
You are looking for Dynamic References.
This lets you set what collection you are referencing as a property to each individual document, instead of hard coding it to one specific collection.
As far as I know, it is not possible to populate a property without any reference.

Check for not required property existing in mongoose model variable

So, I have this mongoose scheemas structure:
const execStatusScheema = new mongoose.Schema(...)
const notifyScheema = new mongoose.Schema({
...
sms: {
type: Boolean,
required: true
},
smsStatus: {
type: execStatusScheema
},
telegram: {
type: Boolean,
required: true
},
telegramStatus: {
type: execStatusScheema
},
voice: {
type: Boolean,
required: true
},
voiceStatus: {
type: execStatusScheema
}
})
const Notify = mongoose.model('Notify', notifyScheema)
module.exports.Notify = Notify
as you can see in Notify scheema smsStatus, voiceStatus and telegramStatus are not required. If sms is false, the smsStatus property is not assiged in my code, and for voice and telegram the same behavior. I want to check in Notify some of these propertys. I do the follow:
const uncomplitedNotifies = await Notify.find(...).select('smsStatus voiceStatus telegramStatus sms voice telegram')
uncomplitedNotifies.forEach(async notify => {
console.log(notify)
if ('telegramStatus' in notify) {
console.log('123')
}
...
Result is:
{ _id: 5ba07aaa1f9dbf732d6cbcdb,
priority: 5,
sms: true,
telegram: false,
voice: false,
smsStatus:
{ status: 'run',
_id: 5ba07aaa1f9dbf732d6cbcdc,
errMessage: 'Authentication failed',
statusDate: '2018-9-18 12:10:19' } }
123
Ok, I find one, and its ok, but my if statment is true even I have no this property in my object. I guess it check in scheema object, where its declared, but I want to check in my 'real' object I got by query.
I also try this checking case, but the same result:
if (typeof something === "undefined") {
alert("something is undefined");
}
How can I check this object correctly ?
The in operator checks the object's own properties and its prototype chain. The unset properties are in the prototype chain of your object, but not on the object's own properties:
const hasTelegramStatus = 'telegramStatus' in document; // true
const hasTelegramStatus = document.hasOwnProperty('telegramStatus') // false
One option is to convert the query into an object by doing document.toObject(), which will remove the prototype chain and only return the own properties.

Mongoose embedded document updating

I have a problem with embedded document update.
My defined Schemas:
var Talk = new Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
comments: {
type: [Comments],
required: false
},
vote: {
type: [VoteOptions],
required: false
},
});
var VoteOptions = new Schema({
option: {
type: String,
required: true
},
count: {
type: Number,
required: false
}
});
Now I would like to update vote.count++, with given Talk id and VoteOption id. I have the following function to do the job:
function makeVote(req, res) {
Talk.findOne(req.params.id, function(err, talk) {
for (var i = 0; i < talk.vote.length; i++) {
if (talk.vote[i]._id == req.body.vote) {
talk.vote[i].count++;
}
}
talk.save(function(err) {
if (err) {
req.flash('error', 'Error: ' + err);
res.send('false');
} else {
res.send('true');
}
});
});
}
Everything executes, I get back the res.send('true'), but the value on count does not change.
When I did some console.log I saw that it changed the value, but the talk.save just doesn't save it in db.
Also I'm quite unhappy about the cycle just to find _id of embedded doc. In the mongoose documentation I read about talk.vote.id(my_id) but that gives me error of not having an id function.
When updating a Mixed type (which seems to be anything else than a basic type, so that also includes embedded documents), one has to call .markModified on the document. In this case, it would be:
talk.markModified("vote"); // mention that `talk.vote` has been modified
talk.save(function(err) {
// ...
});
Hope this helps someone in the future since I couldn't find the answer very quickly.
Reference:
... Mongoose loses the ability to auto detect/save those changes. To "tell" Mongoose that the value of a Mixed type has changed, call the .markModified(path) method of the document passing the path to the Mixed type you just changed.
It's because you are trying to save your talk object before the callback which increments count has been fired. Also, did you make sure to instantiate your Talk schema? eg:
var talk = new Talk();
However, if all you want to do is increment your count variable, mongo supports atomic, in-place updates which you may find useful:
talk.find( { _id : req.body.vote }, { $inc: { count : 1 } } );
have a look at:
http://www.mongodb.org/display/DOCS/Updating#Updating-%24inc

Categories

Resources