Mongoose: Dynamically adding fields in the schema doesn't permit to add fields as usual

Created on 9 Jan 2014  ·  15Comments  ·  Source: Automattic/mongoose

Hi,

When I dynamically add a field to a schema and instantiate it to a model, mongoose is not aware of any change made on this new field when I call save.

var schema = new mongoose.Schema({
  name: String,
  firstname: String
});

var person = db.model('persons', schema);
var a = new person();
a.name = "John";
a.firstname = "Doh";

a.save(function(err, savedGuy) {

  schema.add({
    field1: String
  });
  // schema is now updated with the field1
  var personV2 = db.model('persons', schema);

  personV2.findById(savedGuy['_id'], function(err, john) {

    // won't work
    john.field1 = 'Custom field 1 value';
    // will work
    john.set ('field1', 'Custom field 1 value');

    });
});

I create dynamic but persistent schemas in my project. I can't fill any data until I restart node. It looks like a bug for me.
The purpose of schema.add function should be to avoid the use of set() which seems useful only for arrays properties that can't be detected by mongoose.

I need right now to intensively manipulating objects which is not pretty at all. So my questions are : is this an issue ? If so, is this possible to think about a fix ?

Best regards and thank you for your work !

Most helpful comment

Two solutions:

  1. Use Mixed types as @aheckmann metioned above;
  2. Mongoose Strict
    Disable the strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db. NOTE: do not set to false unless you have good reason.
    var thingSchema = new Schema({..}, { strict: false });
    var thing = new Thing({ iAmNotInTheSchema: true });
    thing.save() // iAmNotInTheSchema is now saved to the db!!

All 15 comments

by design. schemas are compiled into models and are not "live". if you aren't sure about your schema you can always used Mixed types.

I understand that but if I edit a schema (I mean create a new schema from the current one) and compile it to a new model, then it is not "live", it is a new model which should comply with what mongoose is designed for. Why wouldn't I be allowed to do that ?

Two solutions:

  1. Use Mixed types as @aheckmann metioned above;
  2. Mongoose Strict
    Disable the strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db. NOTE: do not set to false unless you have good reason.
    var thingSchema = new Schema({..}, { strict: false });
    var thing = new Thing({ iAmNotInTheSchema: true });
    thing.save() // iAmNotInTheSchema is now saved to the db!!

thanks @lijinma,

I wanted to keep the strict option to avoid unwanted fields.

Using the _set_ method was the only way to assign some data to my freshly added fields.

:+1: @lijinma :heart:

Seems disable mongoose strict conflict with discriminator?

Error: Discriminator options are not customizable (except toJSON & toObject)
...

I want to add some fields on fly if some plugins loaded, use the original model, is this possible?

@fishmacs can you clarify? I don't understand your issue.

@vkarpov15 thanks!

Say I have some discriminated User models:

var UserSchema = new Schema({
    username: String;
    password: String;
});

var AdminSchema = new Schema ({
   email: String
});

var User = mongoose.model('User', UserSchema);
User.discriminator('Admin', AdminSchema);

Then there is a Course model in another module, this Course model tries to add more fields to User:

var CourseSchema = new Schema({
   name: String;
   teacher: String;
   ......
});

var Course = mongoose.model('Course', CourseSchema);

mongoose.model('User').schema.add({
  'courses': [{type: ObjectId, ref: 'Course'}]
});

I hope _user.courses_ can represent the courses selected by the user, but user.courses is always undefined.

I tried to add _{strict: false}_ to UserSchema(in fact, I'm not sure what strict means, if it is what I want), but seems it conflict with discriminator:

Error: Discriminator options are not customizable (except toJSON & toObject)
   at /myproj/courseapp/node_modules/mongoose/lib/model.js:783:15
   at /myproj/courseapp/node_modules/mongoose/lib/model.js:786:7
   at Function.discriminator (/myproj/courseapp/node_modules/mongoose/lib/model.js:797:5)
   at Object.<anonymous> (/myproj/courseapp/src/models/user.js:77:6)

I also tried Mixed type, but no help.

1) Adding paths to the model's schema after the model has been compiled is unsupported. Please don't do that.
2) strict mode
3) When you use discriminator(), you need to pass the same options you passed to CourseSchema and UserSchema. You can change the toJSON and toObject schema options, but not strict. Admittedly this is somewhat cumbersome and will likely change in the future (#3414). For more information on discriminators, check out my blog post on discriminators.

@vkarpov15 Thanks!
And, great post on discriminator! :+1:

I added a "subscription" property but Node isn't returning any of the new data. I used Robo 3T to login to MongoDB and manually entered the data to the existing few records but when I run a query it shows every property except for "subscription". I also added a new record after changing the schema/reloading Node to see if that made a difference but still the same issue.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const serviceProviderSchema = new Schema({
    name: String,
    logoFile: {
        fieldname: String,
        originalname: String,
        encoding: String,
        mimetype: String,
        size: Number,
        bucket: String,
        key: String,
        acl: String,
        contentType: String,
        contentDisposition: String,
        storageClass: String,
        serverSideEncryption: String,
        metadata: { fieldName: String },
        location: String,
        etag: String
    },
    description: String,
    address: {
        street: String,
        unit: String,
        city: String,
        province: String,
        postalCode: String
    },
    contact: {
        mainPhone: String,
        fax: String,
        placementPhone: String,
        placementPhoneExtension: String,
        mainEmail: String,
        placementEmail: String
    },
    perDiems: [{
        label: String,
        price: Number,
        description: String
    }],
    subscription: {
        type: String,
        subscriberSince: String,
        renewals: [{
            begin: String,
            expiry: String,
            transactionId: String
        }]
    },
    residences: [{
        _id: {type: Schema.Types.ObjectId, ref: 'residence'},
        name: String
    }],
    users: [{
        _id: {type: Schema.Types.ObjectId, ref: 'user'},
        role: String
    }]

});

const ServiceProvider = mongoose.model('serviceProvider', serviceProviderSchema);
module.exports = ServiceProvider;

@nosliwsirhc Read this: http://mongoosejs.com/docs/faq.html#type-key

TLDR, do this instead:

    subscription: {
        type: { type: String },

Awesome, didn’t know that it was a reserved keyword. Made the adjustment, for me I changed it from “type” to “subscriptionType”, now it works!

On Jan 5, 2018, at 8:07 PM, Valeri Karpov notifications@github.com wrote:

@nosliwsirhc https://github.com/nosliwsirhc Read this: http://mongoosejs.com/docs/faq.html#type-key http://mongoosejs.com/docs/faq.html#type-key
TLDR, do this instead:

subscription: {
    type: { type: String },


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/Automattic/mongoose/issues/1867#issuecomment-355710126, or mute the thread https://github.com/notifications/unsubscribe-auth/AEb8x6-mqlhLu-RnJ5amsfc1kxI_ibxJks5tHsdNgaJpZM4BYnzl.

How to push in schema? like this:
````
const supported = require('../consts');

var XSchema = new mongoose.Schema(
return () => {for(let items in supported) {
supported[items]: {
default: 'x',
type: string
}
}}
);
````

@hassantauqeer I don't understand your example, it's pretty far off from syntactically valid JavaScript. Maybe you're looking for eachPath? https://mongoosejs.com/docs/api.html#schema_Schema-eachPath . If not, please clarify what you mean.

Was this page helpful?
0 / 5 - 0 ratings