Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support $[] operator in updates #378

Open
drone1 opened this issue May 20, 2020 · 11 comments · May be fixed by #442
Open

Support $[] operator in updates #378

drone1 opened this issue May 20, 2020 · 11 comments · May be fixed by #442

Comments

@drone1
Copy link

drone1 commented May 20, 2020

This appears to be an issue with simpl-schema and not Collection2, according to Meteor-Community-Packages/meteor-collection2#407.

I'm trying to use the $[] operator to update all elements in an array with 1 update() call.

let test = new Mongo.Collection('test')
test.attachSchema(new SimpleSchema({
  'arrayOfObjects': { type: Array, defaultValue: [] },
  'arrayOfObjects.$': Object,
  'arrayOfObjects.$.name': String,
}))

test.update(
  { _id: 'rickandmorty' },
  { $set: {
    _id: 'rickandmorty',

    arrayOfObjects: [
      {name: 'Rick'},
      {name: 'Morty'},
    ]
  } },
  { upsert: true }
)

// Set all arrayOfObject name's at once:
test.update(
  { _id: 'rickandmorty' },
  { $set: { 'arrayOfObjects.$[].name': 'Marge Simpson' } }
)

This apparently fails due to a missing key in the schema:

Error: arrayOfObjects.$[].name is not allowed by the schema (arrayOfObjects.$[].name) in test update
    at getErrorObject (packages/aldeed:collection2/collection2.js:491:17)
    at doValidate (packages/aldeed:collection2/collection2.js:463:13)
    at Collection.Mongo.Collection.<computed> [as update] (packages/aldeed:collection2/collection2.js:196:14)
    at module (imports/api/catalog/catalog.js:559:6)
    at fileEvaluate (packages/modules-runtime.js:336:7)
    at Module.require (packages/modules-runtime.js:238:14)
    at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
    at module (server/imports/test.js:1:661)
    at fileEvaluate (packages/modules-runtime.js:336:7)
    at Module.require (packages/modules-runtime.js:238:14)
    at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
    at module (server/imports/index.js:1:8)
    at fileEvaluate (packages/modules-runtime.js:336:7)
    at Module.require (packages/modules-runtime.js:238:14)
    at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
    at module (server/main.js:1:8)
    at fileEvaluate (packages/modules-runtime.js:336:7)
    at Module.require (packages/modules-runtime.js:238:14)
    at require (packages/modules-runtime.js:258:21)
    at /Library/WebServer/Documents/meteor/.../.meteor/local/build/programs/server/app/app.js:25654:1
    at /Library/WebServer/Documents/meteor/.../.meteor/local/build/programs/server/boot.js:401:38
    at Array.forEach (<anonymous>) {
  invalidKeys: [
    {
      name: 'arrayOfObjects.$[].name',
      type: 'keyNotInSchema',
      value: 'Marge Simpson'
    }
  ],
  validationContext: ValidationContext {
    _simpleSchema: SimpleSchema {
      pick: [Function: pickOrOmit],
      omit: [Function: pickOrOmit],
      _constructorOptions: [Object],
      _validators: [Array],
      _docValidators: [],
      _validationContexts: [Object],
      _cleanOptions: [Object],
      _schema: [Object],
      _depsLabels: {},
      _schemaKeys: [Array],
      _autoValues: [Array],
      _blackboxKeys: Set {},
      _firstLevelSchemaKeys: [Array],
      _objectKeys: [Object],
      messageBox: [MessageBox],
      version: 2
    },
    _schema: {
      arrayOfObjects: [Object],
      'arrayOfObjects.$': [Object],
      'arrayOfObjects.$.name': [Object]
    },
    _schemaKeys: [ 'arrayOfObjects', 'arrayOfObjects.$', 'arrayOfObjects.$.name' ],
    _validationErrors: [ [Object] ],
    _deps: {}
  },
  sanitizedError: errorClass [Error]: arrayOfObjects.$[].name is not allowed by the schema (arrayOfObjects.$[].name) in test update [400]
      at getErrorObject (packages/aldeed:collection2/collection2.js:497:28)
      at doValidate (packages/aldeed:collection2/collection2.js:463:13)
      at Collection.Mongo.Collection.<computed> [as update] (packages/aldeed:collection2/collection2.js:196:14)
      at module (imports/api/catalog/catalog.js:559:6)
      at fileEvaluate (packages/modules-runtime.js:336:7)
      at Module.require (packages/modules-runtime.js:238:14)
      at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
      at module (server/imports/test.js:1:661)
      at fileEvaluate (packages/modules-runtime.js:336:7)
      at Module.require (packages/modules-runtime.js:238:14)
      at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
      at module (server/imports/index.js:1:8)
      at fileEvaluate (packages/modules-runtime.js:336:7)
      at Module.require (packages/modules-runtime.js:238:14)
      at Module.moduleLink [as link] (/Users/.../.meteor/packages/modules/.0.15.0.itei65.v8gbn++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/reify/lib/runtime/index.js:52:22)
      at module (server/main.js:1:8)
      at fileEvaluate (packages/modules-runtime.js:336:7)
      at Module.require (packages/modules-runtime.js:238:14)
      at require (packages/modules-runtime.js:258:21)
      at /Library/WebServer/Documents/meteor/.../.meteor/local/build/programs/server/app/app.js:25654:1
      at /Library/WebServer/Documents/meteor/.../.meteor/local/build/programs/server/boot.js:401:38
      at Array.forEach (<anonymous>) {
    isClientSafe: true,
    error: 400,
    reason: 'arrayOfObjects.$[].name is not allowed by the schema (arrayOfObjects.$[].name) in test update',
    details: '[{"name":"arrayOfObjects.$[].name","type":"keyNotInSchema","value":"Marge Simpson"}]',
    message: 'arrayOfObjects.$[].name is not allowed by the schema (arrayOfObjects.$[].name) in test update [400]',
    errorType: 'Meteor.Error'
  }
}

If you go down the path of error's, after 3 or so you might end up with a few more lines in your schema, which are clearly wrong, but "fix" the issue:

  ...
  'arrayOfObjects.$[]': Array,
  'arrayOfObjects.$[].$': Object, 
  'arrayOfObjects.$[].name': String,

Versions of stuff:
Meteor: 1.10.2
aldeed:collection2: 3.0.6
simpl-schema: 1.7.3

@aldeed
Copy link
Collaborator

aldeed commented May 20, 2020

Yeah basically all the operators that existed when the package was written are supported but there's never been an effort to add support for newer operators like $[]. $[<id>] is not supported either. It would probably be good to list out all that are supported in the readme.

I'll keep this open as an enhancement request if someone wants to make it work.

@aldeed aldeed changed the title $[] operator not working as expected Support $[] operator in updates May 20, 2020
@ee-kapranov
Copy link

How this field key validation can be omitted?

@ee-kapranov
Copy link

@drone1 If you downvoted me - then you know how to update subdocument in array not using $[]. Could you help?

@drone1
Copy link
Author

drone1 commented Jun 13, 2020 via email

@ee-kapranov
Copy link

Got it. I'm trying to solve problems, caused by array rewriting. In parallel processes.

@drone1
Copy link
Author

drone1 commented Jun 13, 2020 via email

@ee-kapranov
Copy link

Thank you. I'll look there.

@kfritsch
Copy link

kfritsch commented Sep 9, 2020

@aldeed, I might have a quick solution for supporting $[]. While trying to understand how mongodb modifiers are handled
in SimpleSchema I found that you generalize keys that target a specific array element ("foo.1.bar") towards a key that would target an array element selected by the query selector ("foo.$.bar") using MongoObject.makeKeyGeneric(key). This is valid since all array elements share the same schema definition anyways (except custom validators, that make use of the array index for validation - not sure if that would be possible). Since all array elements share the same schema definition, it does not matter that all elements are supposed to be updated. It should suffice to check if updating one element in that way would be valid. So "foo.$[].bar" could be replaced by "foo.$.bar" as well. This would only require to change the regex to include the $[] notation. Support for $[] could also be added this way by allowing anything within those brackets (e.g. $[.*] ). I did not make a PR since this involves a separate package and there might be good reasons why MongoObject.makeKeyGeneric(key) should not handle the $[] operator this way, for different use cases.

@Floriferous
Copy link

It would be great to be able to use the arrayFilters option from mongoDB once this works!

https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/

@kfritsch
Copy link

Made a PR longshotlabs/mongo-object#12 to add support for any positional operators as described in my earlier comment

@kfritsch kfritsch linked a pull request May 10, 2021 that will close this issue
@minhna
Copy link

minhna commented Jul 21, 2021

This kind of temporary workaround. I tried and it works:
I define a "fake" field, for example:

'members.$[]': {
    type: Object,
    optional: true,
    blackbox: true,
  },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants