7
votes

I'm trying to have an "optional" nested document in one of my models. Here's a sample schema

var ThingSchema = mongoose.Schema({
  name: String,
  info: {
      'date' : { type: Date },
      'code' : { type: String },
      'details' : { type: Object }
  }
})
var Thing = mongoose.model('Thing', ThingSchema);

In this case, I want the "info" property to be possibly null (not required), and will have application logic that checks if the info is null/undefined.

Unfortunately, even when the 'info' property is undefined in the document in mongo, mongoose still gives me back an object for the 'info' field within the retrieved model.

For example, When I run this code, the 'info' object is populated with an (empty) object.

var thingOne = new Thing({
  name: "First Thing"
})

thingOne.save(function(err, savedThing) {
    if (savedThing.info) {
        //This runs, even though the 'info' property 
        //of the document in mongo is undefined
        console.log("Has info!");
    }
    else console.log("No info!");
})

I tried defining ThingSchema like the following

var ThingSchema = mongoose.Schema({
  name: String,
  info: {
    type: {
      'date' : { type: Date },
      'code' : { type: String },
      'details' : { type: Object }
    },
    required: false

  }
})

Which allows me to have a undefined "info" property retrieved from the database, but doing this allows me to break the "info" schema and add random properties like

savedThing.info.somePropertyThatDoesNotExistInTheSchema = "this shouldn't be here";

Which will then get persisted to the database. Additionally, if the "info" property contains any further nested documents, updating them doesn't mark the Thing model as dirty, which means I have to manually mark the path as modified ( via thing.markModified(path) )

Is there some way to accomplish what I'm trying to do? Are there any best practices for this? Am I just using mongoose schemas completely incorrectly?

I need to either

  1. Retrieve a null "info" property for the Thing schema
  2. Have some way to test if the "info" property is actually null in mongo even though mongoose gives it a value when it is retrieved.
3
What if you use your first Schema and check with hasOwnProperty if your return item had 'info'? This doesn't mean it returns null but at least you can check whether there is more information. If this is not an option, please tell me more where you check for null/undefined so I can find another way. - Thomas Bormans

3 Answers

0
votes

Mongoose will cast any returned document to the schema used to define the model.

With regards to arbitrary paths being persisted to the database, you could use strict to throw an error rather than dropping it silently (which it should be doing by default).

0
votes

There is connection option ignoreUndefined in native mongodb driver, which could solve this issue. https://mongodb.github.io/node-mongodb-native/2.1/reference/connecting/connection-settings/

0
votes

Try using

const Thing = mongoose.model('Thing', ThingSchema)

Thing.create({ name: "First Thing" })

instead of

var thingOne = new Thing({ name: "First Thing" })
thingOne.save(...)

Here's how to use it

mongoosejs.com/...model_Model.create