Syncing key attributes between notes


(Bruno Moreira) #1

I’m trying to sync key attributes between notes with different assigned prototypes.

I have 2 prototypes: “pTypeA” and “pTypeB”; “pTypeA” has one key attribute “relatedToB” and “pTypeB” has one key attribute “relatedToA”.

The goal is to keep “relatedToA” and “relatedToB” synced.

Imagine I have a note of “pTypeA” named “one”; and a note of “pTypeB” named “two”. If I add the value “two” to note “one”'s “relatedToB” key attribute, I’d like note “two” to have “one” assigned to its “relatedToA” key attribute.

I think I understand the basic principle:

“pTypeA” prototype has the following rule:
$relatedToB=collect_if(all,$relatedToA.contains($Name(that)),$Name);

and “pTypeB” this one:
$relatedToA=collect_if(all,$relatedToB.contains($Name(that)),$Name);

This works, but I have to manually disable the rules, add the values to one note, enable the rules … (and sometimes it doesnt work). I guess it’s rules conflicting with each other, based on the order they are executed. I think I need some kind of fail-safe / conditionals…

This is how far I’ve gone: TBX file

(if you add the value “two” to note “one” $relatedToB attribute, and then enable the rules for note “two”, the latter will get “one” automatically assigned to its $relatedToA attribute. But it will not work if rules are enabled from the beginning)

I’d appreciate if you can point me in the right direction. Thanks!


(Mark Anderson) #2

I know this pattern and it’s worth saying it’s one to avoid if you can as it doesn’t scale well. That is due to the collect_if(all,$StringAttribute.contains($Name(that)),$Name) bit. Done as a rule, don’t forget the rule runs not only in the original but in every alias as well. If you’re not careful everything in the doc is constantly investigating every other thing.

So, it seems sensible to ask, if B knows (or can query that) A links to it, why does it need an explicit record in a different attribute of that fact? If we can extract the underlying need there may be a different way to achieve the aim.

Going back to the original point. The apparent mis-performance and out come of the code is likely to be a mix of:

  • interacting rules taking several cycles to flush data around all the code
  • possible unintended loops (A acts on B, which acts on A, etc…)
  • rules/actions running when not needed

In my research, I’m often looking to link items to others based on shared values, but as stated above this can hit performance as .contains() calls are the most computationally expensive actions. So some things to try…

  1. Run rules via a prototype. In your prototype put the code within this structure:

    if($RuleDisabled($Prototype)==false & ($IsPrototype==false) ){
    …rule code here…
    }

If the prototype rule is disabled, inheriting notes still evaluate the rule but bail due to the if condition (i.e. because ). Turning the rule on in the prototype lets the rule rule run. That way you can stop all those .contains() calls running when not needed.

  1. Scope. Do you really need to test “all”. In your model you’re looking at a map of aliases in an agent. Looking at your TBX, A links to B and C but B only to A and C only to A. So, if investigating A->B relationships you want to test only aliases of type A items in the map of interest. If thats in a map called “Map” and ‘A’ type items use prototype “pTypeA” then do something like:

$relatedToB=collect_if(find(inside("Map") & $Prototype=="TypeA" & $IsAlias),$relatedToA.contains($Name(that)),$Name);

Note how the find() call will check far fewer items than “all”. I also note this code completely replaces the contents of relatedToB.

When briefly looking at your TBX last night a was surprised to find ‘plan’ type links appearing in the map, which I think were due to an agent action (the map is in an agent). I assume those links are building on the logic of the $relatedToX data. Anyway, I thin this exercise shows that whilst we may build code to automate and annotate relationships, we need to do so with care. For instance, I’d work on the primary relationships. Then, turn on the rules in my prototypes one by one, let the cycle run (use the Agents & Rules Inspector tab) then turn off the rule. Only then enable the agent action to build links based on that info and turn that off when done. Most of these actions only need one cycle and are background noise the rest of the time.

If you’ve code that needs to run once when used and only used when needed, consider a stamp. A stamp is always ‘off’ until you the user deliberately apply it.

Please don’t read this as running down Tinderbox’s capabilities. Rather, I see it as a useful example of where we step across from the simple (e.g. set colour of all past-due events to red) to the more complex and where an always-on approach can be intrusive to other aspects of use.


(Bruno Moreira) #3

Thank you for your very informative explanation! (as usual). I was completely neglecting the power of stamps. I’m almost getting everything done, merging my two last posts / concerns, and will publish it in the “Tutorial and Examples” section of the forum.


(Mark Anderson) #4

Thanks. No worries, few things are obvious at outset. The ideas suggested above are the distillation of things that worked (after discarding many that didn’t. I’m really coming back to Stamps. Once you get used to the fact they ‘live’ in the Inspector, it’s otherwise just an action you run one what ever you select as many time and whenever you want, i.e. times that are hard to predict and thus schedule.