问题
In my Mongoose Schema I'm trying to simulate a dictionary offersInCategory
that looks like this:
offersInCategory = { "Electronics": 2, "Furniture": 5 };
Mongoose doesn't support dictionaries so I'm forced to use object literals in an array instead, like so:
offersInCategory: [{
category: {
type: String,
enum: ['Furniture', 'Household', 'Electronicts', 'Other']
},
val: {
type: Number,
min: 0
}
}]
My problem with this approach is it feels unintuitive. Furthermore it doesn't prevent my model from creating multiple entries for the same category, as so:
offersInCategory = [ { category: "Furniture", val: 2 }, { category: "Furniture", val: 0} ]
Ideally I'd have my offersInCategory property be structured like this:
offersInCategory : {
"Furniture" : 0,
"Electronics" : 4
}
But I don't know how to restrict it so that only certain keys can be assigned to the offersInCategory
object (kind of like an enum for keys, not values) with no duplicates. I also don't know how to ensure the values for my restricted keys are numbers in a specific range. How can this be accomplished?
回答1:
Option 1 (with "dictionaries"):
You can use the Object
constructor as a SchemaType to use an object instead of an array of objects. Here's an example that applies to your situation using SchemaType#validate:
offersInCategory: {
type: Object,
validate: object => { //our custom validator, object is the provided object
let allowedKeys = ['Furniture', 'Household', 'Electronicts', 'Other'];
let correctKeys = Object.keys(object).every(key => allowedKeys.includes(key)); //make sure all keys are inside `allowedKeys`
let min = 5;
let max = 10;
let correctValues = Object.values(object).every(value => value > min && value < max); //make sure all values are in correct range
return correctKeys && correctValues; //return true if keys and values pass validation
}
}
This does not apply duplicate key checks because an object can not have duplicate keys, the later key present just overrides the previous key:
> let foo = { bar: 4, bar: 5}
< Object { bar: 5 }
As you can see, the bar: 4
key that is assigned earlier is overridden by the later key.
Option 2 (with array): You can use SchemaType#validate to implement your custom validation on a certain document path. Here's an example of what you want:
offersInCategory: [{
validate: {
validator: array => { //our custom validator, array is the provided array to be validated
let filtered = array.filter((obj, index, self) => self.findIndex(el => el.category === obj.category) === index); //this removes any duplicates based on object key
return array.length === filtered.length; //returns true if the lengths are the same; if the lengths aren't the same that means there was a duplicate key and validation fails
},
message: 'Detected duplicate keys in {VALUE}!'
}
category: {
type: String,
enum: ['Furniture', 'Household', 'Electronicts', 'Other'] //category must be in this enum
},
val: {
type: Number,
min: 0, //minimum allowed number is 0
max: 10 //maximum allowed number is 10
}
}]
And if you test this, it will get rid of objects in the array with duplicate keys (keeping the earlier one) and checks to see if the array holds only objects with unique category
keys.
来源:https://stackoverflow.com/questions/44268350/how-to-validate-object-keys-and-values-in-mongoose-schema