Search code examples
javascriptmeteorsimple-schema

SimpleSchema match any type but null


I'm planning to make a collection to hold different app-wide settings, like, say, amount of logged in users today, Google analytics tracking ID, etc. So I made a schema like this:

options_schema = new SimpleSchema({
    key: {
        type: String,
        unique: true
    },
    value: {
    },
    modified: {
        type: Date
    }
});

Now the main problem is that I want value to be of any type: Number, String, Date, or even custom Objects. Though it has to be present, can't be null.

But of course it gets angry about not specifying the type. Is there a workaround for this?


Solution

  • You can use Match patterns for your fields' type which allow you to do pretty much anything :

    const notNullPattern = Match.Where(val => val !== null)
    
    value : {
      type : notNullPattern
    }
    

    (See Arrow functions)

    Note that this will allow everything but null, including undefined.
    Defining patterns this way allow you to use them everywhere in your application including in check :

    check({
      key : 'the key',
      modified : Date.now(),
      value : {} // or [], 42, false, 'hello ground', ...
    }, optionsSchema)
    
    Match.test(undefined, notNullPattern) //true
    Match.test({}, notNullPattern) //true
    Match.test(null, notNullPattern) //false
    

    A more general solution to exclude one value would simply be:

    const notValuePattern =
      unwantedValue => Match.Where(val => val !== unwantedValue))
    

    The use of which is similar to the above:

    Match.test(42, notValuePattern(null)) // true
    

    Note that due to the use of the identity operator === it will notably fail for NaN:

    Match.test(NaN, notValuePattern(NaN)) // true :(
    

    A solution could be:

    const notValuePattern =
      unwantedValue => Match.Where(val => Number.isNaN(unwantedValue)?
        !Number.isNaN(val)
        : val !== unwantedValue
      )
    

    Should you want a solution to exclude some specific values in a schema (kind of the contrary of Match.OneOf), you could use the following:

    const notOneOfPattern = (...unwantedValues) => 
      Match.Where(val => !unwantedValues.includes(val)
    )
    

    This uses Array.prototype.includes and the ... spread operator. Use as follow:

    Match.test(42, notOneOfPattern('self-conscious whale', 43)) // true
    Match.test('tuna', notOneOfPattern('tyranny', 'tuna')) // false
    Match.test('evil', notOneOfPattern('Plop', 'kittens')) // true
    
    const disallowedValues = ['coffee', 'unicorns', 'bug-free software']
    Match.test('bad thing', notOneOfPattern(...disallowedValues)) // true