Collection2: Validating fields against each other in embedded arrays?

Hi guys,

I just ran into a problem with collection2+simple-schema where the custom() is not called on $set updates against embedded arrays.

This example shows the problem (based on https://github.com/aldeed/meteor-simple-schema#validate-one-key-against-another)

SimpleSchema.messages({
  'passwordMismatch': 'Passwords do not match'
})

MySchema = new SimpleSchema({
  accounts: {
    type: [Object],
    optional: true,
  },
  'accounts.$.password': {
    type: String,
    label: 'Enter a password',
    min: 8
  },
  'accounts.$.confirmPassword': {
    type: String,
    label: 'Enter the password again',
    min: 8,
    custom: function () {
      console.log('inside confirmPassword custom')
      if (this.value !== this.siblingField('password').value) {
        return 'passwordMismatch'
      }
    }
  }
})

Collection = new Mongo.Collection('myschema')
Collection.attachSchema(MySchema)

Collection.remove({})
var id = Collection.insert({
  accounts: [
    {password: '12345678', confirmPassword: '12345678'}, 
    {password: '23456789', confirmPassword: '23456789'}
  ]
})

// BUG???
// THIS CALL GOES THRU WITHOUT custom() being called
// .. meaning: we now have invalid data
Collection.update(id, {$set: {
  'accounts.1.password': '9999999999999'
}})
console.log(Collection.findOne().accounts)

Am I doing something wrong here? Any ideas?

I have created an issue on github: https://github.com/aldeed/meteor-collection2/issues/318

Hmm collection2 allows array updates as a whole and you are actually not supposed to be able to update bu index… hmm…

Did you check the github issue tracker or post this over there?

@serkandurusoy: I just checked the issues and I found this issue here, which might be related (https://github.com/aldeed/meteor-collection2/issues/157).

It links to the docs right here https://github.com/aldeed/meteor-collection2#subobjects-and-arrays-of-objects. I’ll play around with it later… strange stuff

1 Like

Ran into the same problem, but only ended up explicitly cleaning a subschema that was in an array. If you figure it out let me know :slight_smile:

OK I played around with it a bit more
and tried 2 workarounds.

WORKAROUND 1 works, but it sucks with complex schemas, because the schema looses it’s protection-functionality… after initially defining it I want to forget about its rules and just use it… when using WORKAROUND 1, I need to think too much about what fields the schema needs for a correct validation (NOTE: it does NOT give us a hint that we are using it correctly)

WORKAROUND 2 is more elegant, but DOES NOT WORK. Maybe there is some kind of library to dynamically build a $set out of an object? Any ideas?

This is the code:

// WORKAROUND 1: (WORKING, BUT SUCKS)
// Let's provide the fields the schema itself needs for full validation
//  this means that we need to fully understand the schema-definition
//  when using it in a useCase.
//  
//  This still **sucks**, as we **need to think too much** when using the schema.
var doc = Collection.findOne(id)
Collection.update(id, {$set: {
  'accounts.1.password': doc.accounts[1].password,  // load the current value from db
  'accounts.1.confirmPassword': '9999999999999',    // pass in the NEW value
}})
console.log(Collection.findOne().accounts)

/*
// WORKAROUND 2: (NOT WORKING, BUT MORE ELEGANT)
// Let's try to provide SimpleSchema with a full set
// as indicated in Mongos Docs: mongo supports this https://docs.mongodb.org/manual/tutorial/modify-documents/#replace-the-document
var doc = Collection.findOne(id)
doc.accounts[1].confirmPassword = '9999999999999'
// replace the WHOLE doc so that SimpleSchema has all fields
// this throws an ``Error: When the modifier option is true, all validation object keys must be operators. Did you forget `$set`?``
Collection.update(id, doc) // does NOT seem to work in meteor
console.log(Collection.findOne().accounts)
*/

.#2 is indeed the suggested method of updating documents when you are using collection2 or in fact any kind of orm-ish tool. And I think astronomy does the same thing as well.

  • get a reference to the doc to be updated
  • set the properties that will change
  • replace the whole doc

I guess you could rewrite #2 as Collection.update(id, {$set: doc} }