Picture of my user model :
In my user model I have this as second argument to my user model, to delete __v and replace _id by id:
{
toJSON: {
transform: function (doc, ret) {
ret.id = ret._id;
delete ret._id;
delete ret.password;
delete ret.__v;
},
},
}
In my signin router I have something like this :
const existingUser = await User.findOne({email});
console.log("existingUser*", existingUser)
res.status(200).send(existingUser);
I got this from my console.log
{
_id: 5fe81e29fdd22a00546b05e3,
email: 'chs#hotmail.fr',
password: '0636b425ef0add0056ec85a5596eacf9ff0c71f8c2a1d4bad068a8679398e11870df12262722b911502eacb5fca23cef0cdd3b740481102ead50c58756d14a34.3f82d856ad93bc99',
__v: 0
}
But in postman I received this :
{
"email": "chs#hotmail.fr",
"id": "5fe81e29fdd22a00546b05e3"
}
I know that with transform, "if set, mongoose will call this function to allow you to transform the returned object".
But could someone explain to me when the 'transform' occurs to justify the difference between the console.log and the data I received in postman ?
Does this have something to do with asynchronous ?
res.status(200).send(existingUser); looks like expressjs (or look-alike) controller code, so i'll assume it is Express.
.send(body) method sends response to the client browser as a string (well, technically). So, before actual transmission, the body argument is converted to string if it isn't a string already. existingUser in your code isn't a string, it's a mongoose object, so express casts it to a string, effectively this will be similar to the following code:
res.status(200)
.send(
existingUser.toString() // .toString() here is the key
);
Under the hood, mongoose object's .toString() is proxied to .toJSON() method, so your code becomes equivalent to following:
...
.send(
existingUser.toJSON() // .toJSON() here
);
...and .toJSON() method is what takes into account the transform(doc, ret) option, that you specified for the mongoose schema.
console.log() on the other hand, does not use underlying .toString()/.toJSON() methods of the arguments. If you want to print to console the result, that would be received by the end consumer (postman, f.e.), then you should call the transform manually:
console.log(existingUser.toJSON()); // like this, transformed, but not stringified
console.log(existingUser.toString()); // or like this, but already stringified
console.log(JSON.stringify(existingUser, null, 3)); // or transform and then stringify with custom formatting (3-space tabulated instead of default single-line formatting)
The whole transform chain looks like this:
Model
-> `.toJSON()`
-> Mongoose transforms model internally into POJO
if custom transform is defined
-> Mongoose passes POJO to user defined `transform(doc, ret)`,
where `doc` - original document, `ret` - internally transformed POJO
-> `ret` is returned
else
-> POJO is returned
Related
I need to pass to the updateMany method in a Node/Typescript software a piece of query retrieved from the database:
{'$unset': {'extras' : {'origin': ''}}}
In the db, the above query is stored as a field of an Object:
"cleanup.aggregated_records_to_modify" : {
"update_fields_by" : "{'$unset': {'extras' : {'origin': ''}}}"
}
If I pass the update_fields_by to the updateMany mondodb Nodejs driver method, I have an error saying ""MongoError: the update operation document must contain atomic operators." (it receives a string instead of an object?!?); if, instead, I create an object variable:
const queryTemp = { $unset: { extras: { origin: "" } } };
to give to the updateMany, all goes well.
How can I retrieve the field from db and correctly pass it to the update method as an object?
If you use JSON.parse(foo) on your variable, to convert it from string to an object
There was a problem (a bug?) with the tool I use to manage MondoDB; I cannot store an object with a key starting with $ because I receive the error "Illegal argument: Invalid BSON field name $unset". I have add the $ symbol programmatically.
When I try to use Firebase.Firestore.set() with an object, I get an error saying "(FirebaseError): Function DocumentReference.set() called with invalid data. Unsupported field value: a custom ql object"
I'm using Firebase in a React class, and I tried creating the object right in the function and defining it separately as shown below:
if (authUser) {
const userObj = {
name: authUser.displayName,
uid: authUser.uid,
email: authUser.email,
emailVerified: authUser.emailVerified,
providerData: authUser.providerData
}
this.db.collection("users").doc(authUser.uid).set(userObj, { merge: true })
}
Most likely the providerData (which comes from the identity provider) contains data of a type that Firestore can't handle. To fix this problem, either don't store the provider data, or convert it into a compatible type with this hack:
providerData: JSON.parse(JSON.stringify(authUser.providerData))
Your problem is that you are trying to copy an instance of an object, (i guess is a graphql object) and firebase does not support that, you need to use primitive values. Make sure you copy strings on all the properties instead of pointers to the class.
I have created an ETL to transfer data from sybase to a mongoDB environment. In one place I am matching against a legacy id in order to populate the correct mongo ObjectId in the model.
This works for my first few thousand records, until I run into a record that's missing a legacy id, in which case I get an error because the model is expecting an objectId. Doing a null check in my ETL file isn't sufficient to handle this. I need to also handle the fact that the model is expecting a valid objectId. How would I do this?
My relevant ETL code looks like this:
let agency = await Agency.findOne({ agencyId: record.agent_house }).exec();
Which I drop in like this:
agency: {
id: agency._id ? agency._id : null,
// Other prop,
// Other prop
}
And the data then gets ported to the model, which looks like this:
agency: {
id: { type: mongoose.Schema.Types.ObjectId, ref: 'agencies' },
// other prop,
// other prop
}
How can I handle a situation where there is no value, which will cause the objectId assignment to fail even with the null check in place in the ETL file?
Using the mongoose ObjectId type, you can do:
agency: {
id: agency._id ? agency._id : new mongoose.mongo.ObjectId(),
// Other prop,
// Other prop
}
I was trying to console.log(record._id) all of records on my mongodb collection using Mongoose. I kept getting undefined for each of the _id values.
I struggled until I bumped into this post. Then I used console.dir to find the location of the _id and used that in my console.log:
MySchemaModel.find({}).then(function(records) {
records.forEach(function(record) {
console.log(record._doc._id); // <-- I added ._doc
});
});
But, this looks down-right hacky. Is there a better way to do this?
NOTE: This isn't just something that affects console.log. I'm just keeping the question narrow.
If you want to customize/edit record then you should use .lean() function.The .lean() function will turn it into a normal JavaScript object. If you don't use .lean() function then each record is still a mongoose document and _id behaves differently in that context. So can use like
MySchemaModel.find({}).lean().exec(function(error, records) {
records.forEach(function(record) {
console.log(record._id);
});
});
N.B: when use .exec() then first parameter used for error and second one for success data.
Mongoose assigns each of your schemas an id virtual getter by default
which returns the documents _id field cast to a string, or in the case
of ObjectIds, its hexString. If you don't want an id getter added to
your schema, you may disable it passing this option at schema
construction time.
Source: Mongoose Docs
var schema = new Schema({ name: String }, { id: false });
var Page = mongoose.model('Page', schema);
var p = new Page({ name: 'mongodb.org' });
console.log(p.id); // '50341373e894ad16347efe01'
I guess the issue is with .then promise, I have never seen that.
MySchemaModel.find({}).then
So just try simple .exec call with callback.
MySchemaModel.find({}).exec(function(records) {
records.forEach(function(record) {
console.log(record._id);
});
});
The problem is that each record is still a mongoose document and _id behaves differently in that context. The .lean() function will turn it into a normal JavaScript object.
MySchemaModel.find({}).lean().then(function(records) {
records.forEach(function(record) {
console.log(record._id);
});
});
you can also use the .map() method :
MySchemaModel.find({}).exec(function(records) {
console.log(records.map(record => record._id);
});
if you are using a model you don't get the full object but an instance of _doc as record
so you should directly
console.log(record._id)
or
console.log(record._id.valueOf())
but when you return record as response you get the full object so it's better to use .find().lean()
I'm creating an application in Node that has some CRUD components. On one of my data objects, I have a save() method that is meant to update a record if the object has an id that is found in the collection, or upsert a new document if not. Additionally, if doing an upsert I'd like to get back the _id for the document generated by Mongo.
It seems that findAndModify would do this, and, indeed, it does return an _id value from the database. In my query clause, I am filtering by _id. If my data object doesn't have an id, Mongo correctly does an upsert, however, no matter what _id value it returns, in addition to the the keys I am setting in the update clause, it also sets the _id on the document based on what value I used in the query clause. Some code for clarity:
User.prototype.save = function(callback) {
var that = this;
var args = {
'query' : { _id: this.getId() }, //getId() returns empty string if not set
'update' : { $set : {
firstName : this.firstName,
lastName : this.lastName,
email : this.email
//_id : this.getId()
// which is blank, is magically getting added due to query clause
}},
'new' : true,
'upsert' : true,
'fields' : {'_id' : true}
};
this.db.collection(dbName).findAndModify(args, function(err, doc){
if(!that.getId()) {
that.setId(doc._id);
}
if (typeof(callback) === "function"){
callback.call(that);
}
});
}
I'm simply looking for the semantics of update that also happens to return a Mongo-generated _id on upsert. I do not want the values of the query clause to additionally be appended as if they were in the update map. Is there any way to achieve what I am getting at?
You can generate the _id client side, with new new require('mongodb').ObjectID()
Then you can just do a regular upsert (no need to do findandmodify) because you already have the _id.
However, if you are using findAndModify, keep in mind that the node driver accepts the arguments to this function positionally, not as an object (like in the regular mongo shell).
The correct format to do findandmodify with the node driver looks like this:
collection.findAndModify(criteria, sort, update[, options, callback])
(options and callback are optional params). Full docs here:
https://github.com/mongodb/node-mongodb-native/blob/master/docs/insert.md