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

Nested autovalue not working as expected #302

Closed
Floriferous opened this issue Sep 4, 2018 · 9 comments
Closed

Nested autovalue not working as expected #302

Floriferous opened this issue Sep 4, 2018 · 9 comments

Comments

@Floriferous
Copy link

Floriferous commented Sep 4, 2018

autoValue does not seem to work as expected in nested objects. I might've missed some obscure configuration some where though.

Here's my schema:

autoValueFunc = function() {
  const { isSet, value, operator } = this.field('trigger');

  if (operator === '$set' && value === false) {
    return 0;
  }

  return undefined;
};

{
  trigger: Boolean,
  triggered: {
    type: Number,
    optional: true,
    min: 0,
    max: 100,
    autoValue: autoValueFunc,
  },
  nestedTriggered: Object,
  'nestedTriggered.triggered': {
    type: Number,
    optional: true,
    min: 0,
    max: 100,
    autoValue: autoValueFunc,
  },
}

Now in my tests, if I initialize both triggered and nestedTriggered.triggered to some non-zero value, and then update trigger from true to false, triggered is set to 0, but nestedTriggered.triggered is still non-zero.

The really weird part, is that if I log the autoValueFunc, I can see that it is correctly triggered for both field, with the correct path being taken at the conditional, but nestedTriggered.triggered is simply not affected.

I would expect both to behave in the exact same way.

@naschpitz
Copy link

I guess this is the same issue that I have: #277

@naschpitz
Copy link

@Floriferous , did you manage to solve this issue? I'm still facing it today with the latest versions of collection2 and simpl-schema.

@vtsio87
Copy link
Contributor

vtsio87 commented May 29, 2020

As I understand it after checking the code, the principal is that autoValue functions will be called in either of these two cases:

  1. the key that has the autoValue option is a top-level field (in your case the triggered key)
  2. the parent key of the key that has the autoValue option is provided in the document or modifier to be cleaned (in your case the nestedTriggered key is the parent for the nestedTriggered.triggered key)

So the question here is whether or not @aldeed wants this behaviour as the default one or not.

If so, possible solutions to your problem would be to either set an autoValue function for the parent key also, or provide the parent key with your document to be cleaned. I know that both of them are not ideal solutions as you have to be very cautious not to mess with existing values of the parent key.

On the other hand, if this is actually a bug then another question arise. Would you like to run all autoValue functions regardless of their nested level i.e. even 3 or 4 levels deep?

@naschpitz
Copy link

naschpitz commented May 29, 2020

@vtsionis , I don't think "2." is true.

Changelog 1.0.0 says:

Now, an autoValue/defaultValue will run only if the object in which it appears exists. Usually this is what you want, but if you are relying on the previous behavior, you can achieve the same thing by making sure that all ancestor objects have a defaultValue: {}.

So I guess using defaultValue: {} on parent should be enough to trigger child's autovalue execution.
Child's autovalue won't run even if defaultValue: {} is present on parent.

@vtsio87
Copy link
Contributor

vtsio87 commented May 29, 2020

Dear @naschpitz, I can provide you with my exact test scenarios and the results.

/* scenario 1 - without the parent key present */
// document or modifier to be cleaned
const testDoc = {
  $set: {
    trigger: false,
  },
};

// cleaned document or modifier
const cleanedDoc = {
  $set: {
    trigger: false,
    triggered: 0,
  },
};

/* scenario 2 - with the parent key present */
// document or modifier to be cleaned
const testDoc = {
  $set: {
    trigger: false,
    nestedTriggered: {},
  },
};

// cleaned document or modifier
const cleanedDoc = {
  $set: {
    trigger: false,
    nestedTriggered: {
      triggered: 0.
    },
    triggered: 0,
  },
};

If you noticed a different behaviour, please let me know so I can proceed with more testing.

In case of a simple document provided with no Mongo operator, the autoValue function will still be called for the nestedTriggered.triggered key. It will return undefined and eventually the nestedTriggered will be set to {}, but still be called.

Again, I'm pointing out that this is not probably what you want to achieve eventually and definitely not ideal, but overall it's nice to know what, when and why is called.

@naschpitz
Copy link

@vtsionis, did you try to call update? For my case autovalue will run just fine during insert, but won't for update. I don't know why there is this behavior difference when calling a method or another.

In my scenario I have a 'dates' object, which contains 'createdAt' and 'updatedAt'. Both are set during insertion, but for updates 'updatedAt' will stay the same, it's autovalue will not run again, even if 'dates' contains defaultValue: {}.

PS.: thanks a lot for your patience.

@vtsio87
Copy link
Contributor

vtsio87 commented Jun 2, 2020

@naschpitz Sorry for the delay... Could you provide your schema along with your autoValue function? That way I can have a clearer idea on what's wrong.

@naschpitz
Copy link

@vtsionis, sorry for the delay, I've been working in a completely unrelated projected that required my full attention the last days.

const DatesSchema = new SimpleSchema({
   createdAt: {
       type: Date,
       label: "Created At",
       optional: true,
       autoValue: function () {
           if (this.isInsert)
               return new Date();
           
           else if (this.isUpsert)
               return {$setOnInsert: new Date()};

           else
               this.unset();  // Prevent user from supplying their own value
       }
   },
   updatedAt: {
       type: Date,
       label: "Updated At",
       optional: true,
       autoValue: function () {
           return new Date();
       }
   },
});

If you do something like:

const Parent = new SimpleSchema({
   otherStuff: {
   ...
   }
   dates: {
       type: DatesSchema,
       label: "Dates",
       optional: true,
       defaultValue: {}
   }
});

Then "updatedAt" won't run it's autovalue function when it is updated, thus "updatedAt" value will remain always the same.

If there was no subschema, therefore "createdAt" and "updatedAt" would be in the root schema, "updatedAt"' autovalue will run as it should.

This behavior of not running nested autovalue functions doesn't happen with aldeed:collection2@2.10.0 and aldeed:simple-schema@1.5.4.

@aldeed
Copy link
Collaborator

aldeed commented Feb 25, 2024

This should be fixed in 3.4.5. This is a tricky situation, and it's possible that others will now complain that they did not expect autoValue to be called, but I do say in the docs that autoValue functions are always called, which gives them the opportunity to adjust the modifier. So in theory anyone who doesn't want this should be able to detect the situation in the autoValue function and avoid changing the value.

@aldeed aldeed closed this as completed Feb 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants