0

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 :

  • read all the data

  • still write arbitrary data (ie. not necessarily defined in the schema)

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.

Community
  • 1
  • 1
nha
  • 17,623
  • 13
  • 87
  • 133
  • 1
    Is disabling [`strict`](http://mongoosejs.com/docs/guide.html#strict) an option? – robertklep Aug 06 '15 at 10:42
  • @robertklep as it is (this collection being the central point in our db), it would be *very* risky (this collection has a lots of fields.subfields, and is linked to about every other collection). – nha Aug 06 '15 at 10:57

2 Answers2

1

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.

Blakes Seven
  • 49,422
  • 14
  • 129
  • 135
  • Can I use the validate function to insert a default value instead for that particular subfield (settings.subfield) ? – nha Aug 06 '15 at 13:26
  • @nha a "pre" save hook would be better for default data, or general manipulation. That will actually fire after validation. There is also a "pre" validate hook you could use as well, not that I see the point as if you are setting it in hooks then the data should be valid. – Blakes Seven Aug 06 '15 at 13:28
  • Then this pre-save hook may be the solution to my problem, thanks – nha Aug 06 '15 at 13:32
  • Wait no sorry it doesn't work with a pre-save hook in fact. I would need something more like a pre-fetch hook since I have existing data already. – nha Aug 06 '15 at 13:49
  • @nha I'm only answering the question you asked, which is about validating a certain key only and ignoring the others. I have also very nicely given a "pre save" example with an additional edit to my answer. If that leads you to other questions then [Post another question](http://stackoverflow.com/questions/ask) or several. – Blakes Seven Aug 06 '15 at 13:52
  • Sorry if my question wasn't clear, I actually need two things. One is a behaviour similar to the "type", which you actually answered. The other part is a behaviour similar to the "default", which returns some default data when there is none in the database. Does that makes sense ? – nha Aug 06 '15 at 13:55
  • @nha I just told you I "edited" the answer to include a "default" value. Read the answer before commenting. – Blakes Seven Aug 06 '15 at 14:04
  • Thank you for your edit. It would work if every entry in my collection was actually saved again. But it doesn't work otherwise for existing data, while using a `default` field would, correct ? Again I am sorry if I misunderstand, and very thankful of your help. – nha Aug 06 '15 at 14:07
0

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
});
Hiren S.
  • 2,793
  • 15
  • 25