It seems Mongoose is doing something really funky internally.
1 var Foo = new mongoose.model(\'Foo\', new mongoose.Schema({a: String, b: Number}));
2 var foo
_doc exist on the mongoose object.
Because mongooseModel.findOne returns the model itself, the model has structure (protected fields). When you try to print the object with console.log it gives you only the data from the database, because console.log will print the object public fields.
If you try something like JSON.stringify then you get to see inside the mongoose model object. (_doc, state ...)
In the case where you want to add more fields in the object and it's not working
const car = model.findOne({_id:'1'})
car.someNewProp = true // this will not work
If later you set the property to the object car and you didn't specify in the Model Schema before then Mongoose model is validating if this field exists and if it's the valid type. If the validation fails then the property will not be set.
Maybe I misunderstood your original question, but now it looks like the nature of your question changed, so the below information isn't relevant, but I'm leaving it. :)
I tested your code and it works fine for me. Mongoose doesn't execute any special code when you set properties that aren't part of the schema (or a few other special properties). JavaScript currently doesn't support calling code for properties that don't yet exist (so Mongoose can't get in the way of the set of the goo
property for example).
So, when you set the property:
foo.goo = { c: 1 };
Mongoose isn't involved. If your console.log
was something other than the code you displayed, I could see that it might report incorrectly.
Additionally, when you send
the results back as JSON, JSON.stringify
is being called, which calls toString
on your Mongoose Model. When that happens, Mongoose only uses the properties defined on the schema. So, no additional properties are being sent back by default. You've changed the nature of the data
array though to directly point at the Mongoose data, so it avoids that problem.
When you set the property goo
using Mongoose, quite a few things happen. Mongoose creates property getters/setters via the Object.defineProperty
(some docs). So, when you set the goo
property, which you've defined as a [String]
, a few things happen:
MongooseArray
) which will contain the array data. In the example you provided, since you didn't pass an array, it will be created.toString
on the data passed as part of the cast.So, the results are that the document now contains an array with a toString
version of the object you passed.
If you checked the contents of the goo
property, you'd see that it's now an array with a single element, which is a string that contains [object Object]
. If you'd picked a more basic type or matched the destination property storage type, you would see that a basic equality check would have worked.
I was stuck on this today... Drove me nuts. Not sure if the below is a good solution (and OP has mentioned it too), but this is how I overcame this issue.
My car object:
cars = [{"make" : "Toyota"}, {"make" : "Kia"}];
Action:
console.log("1. Cars before the color: " + car);
cars.forEach(function(car){
car.colour = "Black"; //color is NOT defined in the model.
});
console.log("2. Cars after the color: " + car);
Problematic console output:
1. Cars before the color: [{"make" : "Toyota"}, {"make" : "Kia"}];
2. Cars after the color: [{"make" : "Toyota"}, {"make" : "Kia"}]; //No change! No new colour properties :(
If you try to pass in this property that was undefined in the model, via doc (e.g. car._doc.color = "black"), it will work (this colour property will be assigned to each car), but you can't seem to access it via EJS (frontend) for some reason.
Solution: (Again, not sure if this is the best way... but it worked for me): Add in this new property (colour) in the car model.
var carSchema = mongoose.Schema({
make: String,
color: String //New property.
})
With the model redefined, everything worked as normal / expected (no _doc 'hacks' needed etc.) and I lived another day; hope it helps someone else.
There is some weirdness with Mongoose models and you have to check that Mongoose doesn't already have a model created in it's models array.
Here is my solution:
import mongoose from 'mongoose';
createModel = (modelName="foo", schemaDef, schemaOptions = {})=> {
const { Schema } = mongoose;
const schema = Schema(schemaDef, schemaOptions);
const Model = mongoose.models[modelName] || mongoose.model(modelName, schema);
return Model;
}
I use my own mongoose model class and base class for my models. I made this and it should work for you.