question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

HowTo: Dynamic number of Filter.OnChange expressions

See original GitHub issue

I have a situation where I am matching a variable number of values before a rule can run it’s ‘update’.

What I want is a way to pass this variable number of values into a the Filter.OnChange call?

This is what I have

            plateModule plate = null;
            IEnumerable<DoubleVariable> values = null;

            When()
                .Match<plateModule>(() => plate)    // Find the owner
                .Query(() => values, x => x
                    .Match<DoubleVariable>(              // Start a list of vars
                        dv => dv.OwnerPart == plate,   // check that this belongs to the owner
                        dv => dv.ValueSet                      // check that it has a value set
                        )
                    .Collect()                                         // aggregate the list
                    .Where(c => c.Count() == plate.DoubleVariables.Count)  // check that all have been accounted for
                );

and then want to place the values into the Filter.OnChange(.... with monitoring the DoubleVariable.Value field. e.g. is this valid ?

                plate.DoubleVariables.ForEach(dv => Filter()
                    .OnChange(() => dv.Value)
                );

Or is there an Actual way ? Especially as the plate. will be null

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
snikolayevcommented, Oct 17, 2019

OnChange accepts one or more keys to monitor for changes. But the number of keys must be known at rule compile time. Since you have an unbound collection, you need to flatten it to a single key to pass it to the OnChange filter. I showed you flattening into a string as a naive example of flattening a collection to a single immutable value. It’s also not that bad from performance perspective, since this string (or whatever flattened value you produce) will only be generated when the filter condition is evaluated, which is when the rule already produced a full match. Imagine what would the engine need to do to support OnChange for collections. It would need to compare two snapshots of that collection, element by element, to determine if anything changed. That is exactly what you can do by implementing a ChangeKey class that wraps your collection and implements IEquatable. That’s also in spirit similar to what you were proposing with the CreateOnChanges snippet.

public class ChangeKey : IEquatable<ChangeKey>
{
    private readonly double[] _items;

    public ChangeKey(IEnumerable<DoubleVariable> items)
    {
        //must snapshot, as the change key must be immutable
        _items = items.Select(x => x.Value).ToArray();
    }

    public bool Equals(ChangeKey other)
    {
        //implement element-wise comparison
    }
}

And then in the filter:

Filter()
    .OnChange(() => new ChangeKey(plate.DoubleVariables));

And as before, you can wrap this in an extension method to read better.

0reactions
Smurf-IVcommented, Oct 22, 2019

Thanks for taking time out for this…

There are few things. First (and it’s unrelated to filters, but helpful to clarify) - you are loading the rule twice:

repository.Load(x => x.From(Assembly.GetExecutingAssembly()));
repository.Load(x => x.From(typeof(RavenRule)));

That was a “copy and paste” and forgetting to “tidy up” error !

Now to the actual filter. You have two instances of TestModuleBool, that you called apple1 and apple2, and you insert both into the session (along with the variables defined in them). But only the first one has the value set. So, this creates one activation, and generates a change key for it.

That is correct, to try an emulate “Late updates” of multiple variables later on.

The second TestModuleBool does not create an activation, because of your match conditions:

.Match<IVariable>(iv => iv.OwnerPart == calc && iv.ValueSet)

Agreed.

It’s important to remember that each match creates its own activation, and the filter is applied at the activation level. Then you are updating apple2 and setting the value, which creates the second activation (and generates a change key for it).

OK.

If you want to see the change key in action, update one of the facts with an existing activation. For example:

apple2.DoubleVariables.First().SetValue(1);
session.UpdateAll(apple2.Variables);
session.Fire();     //This one creates a new activation, because of SetValue above, so the rule fires

And I put this before the next part: apple2.DoubleVariables.First().SetValue(2);

session.UpdateAll(apple2.Variables);
session.Fire();     //This one updates the existing activation, but nothing changed, so the rule doesn't fire

And Now I see the Equality being fired. Now I understand why I was not seeing the firing, because internally (I am assuming) in the NRules compiler, it is being “Very” efficient in the way it needs to call the filters, and hence the need to not calling the Do functionality if it has only been evaluated once…

Again thanks for going through this with me.

Read more comments on GitHub >

github_iconTop Results From Across the Web

angularjs - dynamically change the filter expression
I want to dynamically assign a different filter expression based on the user. So user2 will have {isCommon: 'M2'}. angularjs · Share.
Read more >
Building a Dynamic Filter with ES6 JavaScript | by Tyler Burdsall
Conclusion. With this knowledge you can build a dynamic filter depending on any of the user's inputs. Plus, you now have a flexible...
Read more >
Filter on a data source with a dynamic number of f...
In my app, users can select a number of different text terms to add them as filters. Then the user can search a...
Read more >
Create a dynamic filter option
Enter a number to designate the placement of this dynamic filter option in the filter option choice list. Roles, Select the role a...
Read more >
Designer Desktop: Dynamic filtering with different parameters
Hey everyone. I am trying to dynamically filter a date based on some previous formulas validation. Here's a visual example:
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found