nha
nha

Reputation: 18005

mongoose - have optional additional implicit fields

I have a schema with a field in which I can store anything : new Schema({settings : {}}).

I have a database with I want to keep this ability to add data without adding new fields, but for some of the fields have default values if they are not present.

I can do the following :

new Schema({
   settings : {
       key : { type : String, default: "abc" }
       // I want to be able to add data that contains more than just "key"
   }
});

I just want to make sure that when requesting the data from this schema, I will still get all the data, and not just the keys explicitly defined ?

It seems to work, but I want to make sure that I can still :

Are there rules on mongo/mongoose that would prevent me from doing one of these two things (I'm very unsure for the writing part) ? If there is such a "feature", how can it be done ?

Note : I saw this question. Correct me if I am wrong, but the fields are not implicit (like in the first case with {}), and have to be defined (it's actually the opposite question).

Edit : I now saw also this question that addresses my concerns (even if the accepted solution sounds more like a workaround to me). But in my case I already have data stored so (1 - disable strict) would mean writing a lot of validation code to be safe (because a lot of keys, this is the biggest collection of the app), and (2 - mixed schemas) would require to migrate the data of this specific sub-element... In short : I would still welcome a solution to my particular problem.

Upvotes: 0

Views: 505

Answers (2)

Blakes Seven
Blakes Seven

Reputation: 50406

I think you will want to build your own custom validation here rather than rely on the defauly schema type validation methods. Luckily, mongoose has a facility for this:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var testSchema = new Schema({
  settings: {}
});

var Test = mongoose.model( 'Test', testSchema, "test" );

testSchema.path('settings').validate(function(value) {
  return Object.keys(value).indexOf("key") != -1;
},'Error "settings" must contain "key" as "settings.key"');


//var test = new Test({ settings: { "key": "something" } });
var test = new Test({ settings: { } });

test.save(function(err) {
  try {
    if (err) throw err;
    console.log(test);
  } catch (e) {
    console.log(e);
  }
});

So basically, I have set up the validate function there for the "settings" path in the schema to look for the presence of "key" with it's own object. Where that "key" does not exist, an exception is reported in the errors.

Like any such errors, it will be returned within the err object when you .save() the object, thus blocking the write. It can be then be acted on to handle the error however you want, with the message that was defined reported.

So that is a self contained "test" where you can alternately uncomment the valid data for the object and successfully save that object without errors being reported.


Alternately you can do a "pre save" to fill in some default data:

testSchema.pre("save",function(next) {
  this.settings = (this.settings) ? this.settings : {};
  if (Object.keys(this.settings).indexOf("key") == -1)
    this.setting.key = "abc";
  next();
});

Which fills in a default if it is not already there.

Upvotes: 1

Hiren S.
Hiren S.

Reputation: 2832

Try to use like this way

new Schema({
    settings : {}
});

var modelObj = new myModel();
modelObj.settings.key = "keyval";
modelObj.settings.key1 = "keyval";
modelObj.settings.key2 = "keyval";
modelObj.settings.key3 = "keyval";
modelObj.save(function(err){
    //handle
});

Upvotes: 0

Related Questions