I have a document from a mongoose find that I want to extend before JSON encoding and sending out as a response. If I try adding properties to the doc it is ignored. The properties don't appear in Object.getOwnPropertyNames(doc) making a normal extend not possible. The strange thing is that JSON.parse(JSON.encode(doc)) works and returns an object with all of the correct properties. Is there a better way to do this?
Mongoose Models inherit from Documents, which have a toObject() method. I believe what you're looking for should be the result of doc.toObject().
http://mongoosejs.com/docs/api.html#document_Document-toObject
Another way to do this is to tell Mongoose that all you need is a plain JavaScript version of the returned doc by using lean() in the query chain. That way Mongoose skips the step of creating the full model instance and you directly get a doc you can modify:
MyModel.findOne().lean().exec(function(err, doc) {
doc.addedProperty = 'foobar';
res.json(doc);
});
JohnnyHK suggestion:
In some cases as #JohnnyHK suggested, you would want to get the Object as a Plain Javascript.
as described in this Mongoose Documentation there is another alternative to query the data directly as object:
const docs = await Model.find().lean();
Conditionally return Plain Object:
In addition if someone might want to conditionally turn to an object,it is also possible as an option argument, see find() docs at the third parameter:
const toObject = true;
const docs = await Model.find({},null,{lean:toObject});
its available on the functions: find(), findOne(), findById(), findOneAndUpdate(), and findByIdAndUpdate().
NOTE:
it is also worth mentioning that the _id attribute isn't a string object as if you would do JSON.parse(JSON.stringify(object)) but a ObjectId from mongoose types, so when comparing it to strings cast it to string before: String(object._id) === otherStringId
the fast way if the property is not in the model :
document.set( key,value, { strict: false });
A better way of tackling an issue like this is using doc.toObject() like this
doc.toObject({ getters: true })
other options include:
getters: apply all getters (path and virtual getters)
virtuals: apply virtual getters (can override getters option)
minimize: remove empty objects (defaults to true)
transform: a transform function to apply to the resulting document before returning
depopulate: depopulate any populated paths, replacing them with their original refs (defaults to false)
versionKey: whether to include the version key (defaults to true)
so for example you can say
Model.findOne().exec((err, doc) => {
if (!err) {
doc.toObject({ getters: true })
console.log('doc _id:', doc._id)
}
})
and now it will work.
For reference, see: http://mongoosejs.com/docs/api.html#document_Document-toObject
To get plain object from Mongoose document, I used _doc property as follows
mongooseDoc._doc //returns plain json object
I tried with toObject but it gave me functions,arguments and all other things which i don't need.
The lean option tells Mongoose to skip hydrating the result documents. This makes queries faster and less memory intensive, but the result documents are plain old JavaScript objects (POJOs), not Mongoose documents.
const leanDoc = await MyModel.findOne().lean();
not necessary to use JSON.parse() method
You can also stringify the object and then again parse to make the normal object.
For example like:-
const obj = JSON.parse(JSON.stringify(mongoObj))
I have been using the toObject method on my document without success.
I needed to add the flattenMap property to true to finally have a POJO.
const data = document.data.toObject({ flattenMaps: true });
Related
Looking for clean way to convert a javascript object containing arrays as values to a search param compatible query string. Serializing an element from each array before moving to the next index.
Using libraries such as querystring or qs, converts the object just fine, but handles each array independently. Passing the resulting string to the server (which I cannot change) causes an error in handling of the items as each previous value is overwritten by the next. Using any kind of array notation in the query string is not supported. The only option I have not tried is a custom sort function, but seems like it would be worse than writing a custom function to parse the object. Any revision to the object that would generate the expected result is welcome as well.
var qs = require("qs")
var jsobj = {
origString:['abc','123'],
newString:['abcd','1234'],
action:'compare'
}
qs.stringify(jsobj,{encode:false})
qs.stringify(jsobj,{encode:false,indices:false})
qs.stringify(jsobj,{encode:false,indices:false,arrayFormat:'repeat'})
Result returned is
"origString=abc&origString=123&newString=abcd&newString=1234&action=compare"
Result desired would be
"origString=abc&newString=abcd&origString=123&newString=1234&action=compare"
I tried reorder your json:
> var jsobj = [{origString: 'abc', newString: 'abcd' }, {origString: '123',
newString: '1234' }, {action:'compare'}]
> qs.stringify(jsobj,{encode:false})
'0[origString]=abc&0[newString]=abcd&1[origString]=123&1[newString]=1234&2[action]=compare'
But I don't know if this is a good alternative for your problem.
Chalk this up to misunderstanding of the application. After spending some more time with the API I realized my mistake, and as posted above by others, order does no matter. Not sure why my first several attempts failed but the question is 'answered'
I was trying to use includes method to check existing MongoDB Id in an array but it is always returning false.
Is it a some kind of bug in the includes method or am I doing something wrong below is my code
let user = req.user._id;
let keyword= req.query.q;
let userSearchHistory = await UserSearchHistory.findOne({searchKeywords: keyword}).exec();
if (!userSearchHistory.users.includes(user))
{
}
where users in my database is an array with ObjectId refering to user collection
I also tried converting user object
let user= mongoose.Types.ObjectId(req.user._id);
But still the same result.
Converting ObjectIds to String working for me but then I have to remove the references in my model what is the proper way to handle this ?
This is probably happening because userSearchHistory.users returns an array of objectIds.
You can try replacing userSearchHistory.users.includes(user) with userSearchHistory.users.map(user=>user.toString()).includes(user).
This way you will be able to convert the array of objectIds to array of strings before applying 'includes' function.
Edit : Did you try trimming spaces in 'req.user._id' by using req.user._id.trim() and then casting it to objectId?
I want to get references in mongoDB using nodejs/mongoose.
In the documentation I read that there are two options: Manual References or DBRefs.
Since they state, its recommended to use Manual References, I decided to set up a schema in the following way:
var schema = new mongoose.Schema({
name : String,
reference : mongoose.Schema.ObjectId
});
Question: If I retrieve an array of these objects from my collection, how do I resolve the references in a good practice way?
My Idea was to use Nimble and parallelize the necessary requests. I wanted to do something like
flow.parallel(functions, function() {
return result;
});
where I dynamically fill an array of functions
var functions = []
which I pass then to nimble.
(kind of this SO-question: Javascript Array of Functions)
Question: Is this practical? The array of functions-thing seems kind of not really the way to go to me. But I don't see any alternative, since nimble needs to be called with a static number of functions.
You can use Mongoose's support for reference population to efficiently follow references.
var schema = new mongoose.Schema({
name : String,
reference : { type: mongoose.Schema.ObjectId, ref: 'OtherModel' }
});
var MyModel = mongoose.model('MyModel', schema);
MyModel.find().populate('reference').exec(function(err, docs) {...});
In the above example, the reference field of each docs element gets populated with referenced doc.
var currentVideos = this.get('items').pluck('video');
// TODO: Why does pluck not work here?
var currentVideosDurations = _.map(currentVideos, function (currentVideo) {
return currentVideo.get('duration');
});
var test = _.pluck(currentVideos, 'duration');
console.log("Test:", test);
I was wondering why my second pluck doesn't work, but my map works fine? I thought these were equivilant usages.
Here's a screenshot of a console log showing this.get('items') and the array of currentVideos.
The backbone model object doesn't store the properties you get from the model at the top level javascript object. The currentVideo backbone model object actually stores the attributes deeper within the javascript object, in (currentVideo.attributes).
_.pluck(currentVideos, 'duration') checks for the top level attribute (e.g. currentVideo['duration']), which doesn't exist.
The distinction is that Backbone Model objects are more sophisticated than basic javascript objects and don't get attributes by just retrieving object['attrName'].
I thought these were equivilant usages.
Nope. pluck is accessing properties with that name, but get is a method invocation. However, to shorten the map you can use invoke:
var currentVideosDurations = _.invoke(currentVideos, "get", "duration");
I am using Express and I am looking for a convenient way to convert this kind of object (which comes on the request req.body.myObject):
{
"name": "Foo",
"someNumber": "23",
"someBoolean": "on"
}
Into an instance of this Schema:
var myObjectSchema = new Schema({
name: String,
someNumber: Number,
someBoolean: Boolean
});
Notice that the first object comes from the request, so its made entirely by Strings.
Is there some nice way to achieve this? If not, would you have any suggestions on how to implement this feature as a middleware???
By referring to this thread Mongoose : Inserting JS object directly into db I figured out that yes, theres a built in feature for this.
You simply build a new model passing request values (from the form) as parameters:
function add(req, res){
new Contact(req.body.contact).save(function(err){
console.log("Item added");
res.send();
});
};
It automatically converts stuff for you!
I know this answer has already been accepted, but I wanted to point out that mongoose takes care of most of the casting for you... most of the time. While it's convenient that mongoose does this, it abstracts away the true behavior of mongo. For example, mongoose lets you do something like this:
PersonModel.findById("4cdf00000000000000007822", ...);
However, if you tried to query the database directly (without mongoose), this would not work:
PersonCollection.find({_id: "4cdf00000000000000007822"}, ...);
This is because ObjectIds are not strings... they are objects. Internally, mongoose converts that string to an ObjectId and then performs a query against the database so that the final query looks kinda like this:
PersonCollection.find({_id: ObjectId("4cdf00000000000000007822")}, ...);
Also, each path in a schema has a "caster" method. This is a private method, but it's darn handy when you need it. PLEASE NOTE THAT THE caster METHODS DESCRIBED BELOW ARE UNDOCUMENTED AND CAN CHANGE WITHOUT WARNING. USE AT YOUR OWN RISK (sorry for yelling):
// Use PersonModel.schema.paths() to get all paths and loop over them if you want
var key = "name";
var pathObj = PersonModel.schema.path( key );
if( !pathObj ) pathObj = PersonModel.schema.virtualpath( key );
if( !pathObj ) { /* not found: return, continue, exit, whatever */ }
// UNDOCUMENTED: USE AT YOUR OWN RISK
var caster = pathObj.caster || pathObj;
var castedValue = caster.cast( req.body.name );
Why do I know this? Because if you want to use some of the more advanced features of mongo such as aggregation, you will need to cast your own values as you build the pipeline. I have also needed to manually cast values for certain queries which used the $in operator... maybe this is not needed any more. Point is, if you are having trouble getting the results you expect, try casting the values yourself.
Provided the schema is static, one could theoretically go the lazy, non-sophisticated way and just hardcode the values instead of passing the object itself:
var someObject = {
name: "Foo",
someNumber: "23",
someBoolean: "on"
}
var myObjectSchema = new Schema({
name: someObject.name,
someNumber: parseInt(someObject.someNumber, 10),
someBoolean: (someObject.someBoolean == "on")
});
Possibly not the answer you were looking for, but might be something to consider if nothing better is available.