Mongoose: validation is not working for arrays

Created on 7 Feb 2018  路  13Comments  路  Source: Automattic/mongoose

Do you want to request a feature or report a bug?
bug

If the current behavior is a bug, please provide the steps to reproduce.

var bluebird = require("bluebird");
var mongoose = require("mongoose");
mongoose.set("debug", true);
mongoose.Promise = bluebird.Promise;

var db = mongoose.createConnection("mongodb://localhost/issue-123");


const MySchema = new mongoose.Schema({
    array: {
        type: [String],
        enum: ["qwerty"]
    },
});


var MyModel = db.model("MySchema", MySchema);


MyModel.create({array: ["qwerty", "asdfgh"]})
    .then(model => {
        console.log(model); // saved
    });

What is the current behavior?
model is saved with 2 values

What is the expected behavior?
should throw validation error because "asdfgh" is not in enum

Please mention your node.js, mongoose and MongoDB version.
node 8.9
mongoose 5.0.3

confirmed-bug

Most helpful comment

Man don't mix PHP in here :) and JFYI i'm not a vegan :鈥岲

as for your question: mixedEnums is not an array of strings, its not an array at all.
and for some weird cases like this schema can validate itself before compilation and print some reasonable message in console

All 13 comments

Hi.
Sorry, but you are doing wrong. You need to make Nested Schema, to use Sub Document.
Whatever, try use the following:

const SubStrSz = new mongoose.Schema({ value : { type : String, enum : ['qwerty', 'asdf'] } });
const MySchema = new mongoose.Schema({ array: [SubStrSz] });

Using that technique you will able to validate values inside of your array.

And, moreover, you can't just use keyword type to make your array.
All that you did is an object with two fields: "enum" and "type".
Both are keywords. So, you need to read the docs first about how to create schema.

As far as I can see you need to make changes, if you wish to use reserved word "type" as a key for you code: Type Key

I read exactly same doc :) http://mongoosejs.com/docs/guide.html#indexes
it says

  var animalSchema = new Schema({
    name: String,
    type: String,
    tags: { type: [String], index: true } // field level
  });

  animalSchema.index({ name: 1, type: -1 }); // schema level

so i assume tags type is array of strings

also in here http://mongoosejs.com/docs/schematypes.html#arrays
it says this is a valid way to define array-of-whatever

and it actually works, except of validation

Hi, glad to see you!
Yes, for your answer everything is ok, but for the first question, look deeper, that construction

array: { type: [String], enum: ["qwerty"] },
is very very differ from

tags: { type: [String], index: true }

All I wish to say, is that if you need to use that enum, you must do the following

array: [{ type: String, enum: ["qwerty"] }]

Kind Regards

Heya!
ok, i see i have to use
array: [{ type: String, enum: ["qwerty"] }]
in case i want validation to work, but still
array: { type: [String], enum: ["qwerty"] }
is a valid way to define array of strings and validation doesn't work so it's a bug
i'm ok with some warning in console but not silent ignoring of enum property

Hey ^)
Yepp, you have to use...
And NO, it is not a valid way to define key "array" as an array of strings with that enum.
Just because in your case there is definition of Schema.types.Mixed for "array" field, and then, inside of that array you are defining field "type" as an array of strings and field "enum" as an array, by default filled with "qwerty" string. And as if "enum" is a reserved keyword I have no idia how it will work for field "array", but you still will be able to use field "type", if you will re-define "typeKey" property of your shema in options.

Kind Regards

ok, i see lots of arguments why this is expected behaviour
but from my perspective its like someone just have to copy
https://github.com/Automattic/mongoose/blob/master/lib/schema/string.js#L75
to array? am I right?

Look
arrayOnTop : [{type: String, enum...}] -- // array is at the top level
the value with an array will be "arrayOnTop"

arrayDigIn : { arr : [String], enum...} -- // array is on the nested level
the value with an array will be arr, therefore "arrayDigIn.arr" -- will be full path.
or, if you prefer PHP notation arrayDigIn -> arr

that is that difference, the nested level matters, and enum must be on same level, or you will blow up everything in understanding you schemas for everybody else

If you have a lot of time, you are able, for sure, try to make a patch and then merge request.
But I think it will be hard, because the change you are trying to make is a breking change, it will break everything here, about how schemas are constructed.
And you don't need to copy that part of code to array, you need it to be casted on the over level. Mmmmagic. ;^)
Kind Regards

yeah I think I agree with @wentout here but i will label this as a discussion. I suppose @TrejGun has a point that if you put enum on the top level of a field that has the type [String] we should assume enum applies to values inside that string.

Thoughts @vkarpov15 ?

And what it will be then?

mixedEnums : {
  arr1 : [{
   type : String,
   enum : [...]
  }],
  arr2 : [{
   type : String,
   enum : [...]
  }],
  ...
  arrN : [{
   type : String,
   enum : [...]
  }],
  enum: [...]
}

?

Man don't mix PHP in here :) and JFYI i'm not a vegan :鈥岲

as for your question: mixedEnums is not an array of strings, its not an array at all.
and for some weird cases like this schema can validate itself before compilation and print some reasonable message in console

Hi ^)
Oh, sorry for that PHP joke, I'm not a fan of this lang too.
Ok, I see the reasons of high level enum for validation nested arrays.
But i'm unable to separate it in my head. May be then to use another keyword, or invent something extra-additional, for example -- plugin? And, i think then there are a lot of variants for this case: pre-compilation, as you say, validation, default values, mixins, etc and so on.

The correct way to do this is:

const MySchema = new mongoose.Schema({
        array: {
                type: [{ type: String, enum: ["qwerty"] }]
        },
});

but I agree this is a bit of a gotcha. Will fix :+1:

@vkarpov15 thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ChrisZieba picture ChrisZieba  路  76Comments

saudelog picture saudelog  路  79Comments

adeelzaman picture adeelzaman  路  82Comments

xpepermint picture xpepermint  路  115Comments

nparsons08 picture nparsons08  路  40Comments