Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    larryPlayabl
    @larryPlayabl
    If you want an exclusive Or, you can use a strict boolean || in a condition.
    Alternately, if you just want to restrict your rule to firing one time, you can assert a fact in the Do() that then prevents future rule activations.
    LunicLynx
    @LunicLynx_twitter
    Complete noob questions incoming. You have been warned!
    I want to use NRules to process events and create Incidents from it.
    1. How do i process theses event do i create a session for each event or do i process all at once?
      Also i tried to create a rule that uses a trigger event as trigger and checking that no other event exists that resolves this trigger. The rule for this got quiet complicated so i guess i'm doing this wrong.
      I now think that it would be better to create an incident from each trigger event and also consinder the incidents in the rule engine, which brings me back to question 1. (Do i process incidents 1 by 1 or all at once?)
    2. Should i create a session for each object i consinder an rule activation originator?
      If i go ahead i end up with a list of incidents, filled with information from the trigger event.
      I now want to build notification rules for incidents.
      The general rule would be to notify for each incident immediately. And then specific rules for exceptions. e.g. the general rule should not be activated anymore.
    3. How do i create catch all rules which i can override?
    4. Am i on the wrong track?
    Sergiy Nikolayev
    @snikolayev
    @LunicLynx_twitter this is actually a fairly complex question, because you are trying to use events. Generally speaking, NRules is not a CEP engine, and does not have built-in support for events. Normally, NRules is used with facts; unlike events facts describe the state of the world, so they can be inserted into an NRules session and be kept there while they remain valid. Events on the other hand describe things that happened at a point in time, so what you ideally want is to insert them into the session and then remove as soon as no more rues can match them (or just discard the session and create a new one for the next batch of events). Whether you create a session per event or for a batch of events will determine what kind of rules you can write. If you insert each event into its own session, you won't be able to write rules that match multiple events at once (e.g. any aggregation over events using Query clause). Once you have your events in the session, you'll run the rules execution cycle, which will create an incident out of each event, and run any subsequent rules on those created incidents (provided incidents are also inserted into the session as facts). Once all rules have fired you can discard the session and any new events arriving from that point on will have no relation to the events that arrived earlier. I can't say I understand your actual problem well (e.g. I don't know what you mean by "catch all rules"), but hopefully this clarifies some of your questions on how to structure the session.
    LunicLynx
    @LunicLynx_twitter
    @snikolayev thanks, that helped a lot! Will investigate CEP engines. By catch allfor example: Catch all: If it is an incident raise a notification. More specific rule: When it is an incident concerning memory wait for 10 minutes before raising a notification. If the latter rule applies, don't evaluate the former. Otherwise i have to explicity exclude the case for the latter rule in the former which makes it kind of complex.
    LunicLynx
    @LunicLynx_twitter

    In pseudo code something like this

    if (incident.Reason == "memory")
    {
        if (incident.Created < now.AddMinutes(-10))
        {
            // notify (specialized rule)
        }
        // don't notify (specialized rule)
    }
    else
    {
        // notify (catch all)
    }

    Not sure if this clears things up or makes it more confusing :see_no_evil:

    larryPlayabl
    @larryPlayabl

    @LunicLynx_twitter A pattern like this is definitely possible. One way you might do this is to retract your event facts when they're "handled" by a rule. Alternately, you could mark up your events when they're handled, so that your catch-all rule won't ever match on them.

    Then you'd need some kind of gating factor to ensure that your higher specificity rule will always be able to act first. You could use time for this, as you described above, by tracking a "createdTime" in your fact, which you match on in your catch-all rule. Alternately, you could use the NRules agenda ordering mechanism to ensure that your catch-all rules will fire later than your higher-specificity ones.

    One note about time in specific is that, if you want to use it, you'll need to have a Time Fact in your session, which you're updating at some interval to tell the rule engine to re-evaluate anything that's time dependent.

    Sergiy Nikolayev
    @snikolayev
    @LunicLynx_twitter another point is that you can have your "catch all" rule to have a lower priority, so that it first matches other rules if it can. Then, as @larryPlayabl mentioned, your action must invalidate the match conditions, by either setting some "IsHandled" property on the "incident" facts, and ensuring that all rules only match incidents that don't have this flag set.
    RajkumarViven
    @RajkumarViven
    Is Possible build NRules: building a rule for complex types?I have to write following model.public class Person
    {
    public string Name { get; set; }
    public int Age { get; set; }
    public List<Car> Cars { get; set; }
    }
    public class Car
    {
    public int Year { get; set; }
    public string Make { get; set; }
    }

    I have using this code in session Person p1 = new Person("Jim", 31);
    p1.Cars = GetCars(4);
    Person p2 = new Person("Bob", 29);
    p2.Cars = GetCars(4);

    session.Insert(p1);
    session.Insert(p2);
    public class CarTest : Rule
    {
    public override void Define()
    {
    Person person = null;
    IEnumerable<Car> cars = null;

    When()
      .Match<Person>(() => person, p => p.Age < 30)
      .Query(() => cars, x => x
         .Match<Car>(c => c == person.Cars.Find(f=> f.Make == c.Make && f.Year == c.Year), c => c.Year > 2016)
         .Collect()
         .Where(p => p.Any()));
    Then()
      .Do(ctx => DoSomethingWithNewCarsThatBelongToYoungPeople(cars));

    }

    private static void DoSomethingWithNewCarsThatBelongToYoungPeople(IEnumerable<Car> cars)
    {
    foreach (var car in cars)
    {
    //Do Something
    }
    }
    }
    this the code.pls help to solve this

    Sergiy Nikolayev
    @snikolayev
    @RajkumarViven If I understand you correctly, you want all cars across all people that satisfy the person's age and car's year conditions. How about this?
    When()
      .Query(() => cars, x => x
         .Match<Person>(p => p.Age < 30)
         .SelectMany(p => p.Cars)
         .Where(c => c.Year > 2016)
         .Collect()
         .Where(p => p.Any()));
    RajkumarViven
    @RajkumarViven
    Thanks bro. its working now but i need to send Person Object to action how i can do this? Person Object also an Collection ie List or IEnumarable
    RajkumarViven
    @RajkumarViven
    hi @Nik I want All the persons as List with cars as List across all people that satisfy the person's age and car's year conditions
    RajkumarViven
    @RajkumarViven
    Difference between and Do and Action in N Rule?.can you provide a sample
    RajkumarViven
    @RajkumarViven
    @snikolayev pls provide the solution
    Sergiy Nikolayev
    @snikolayev

    I think in this case you are better off using forward chaining. You would need two rules for that. First, you have a rule similar to the one you had originally, where you match a single person along with their cars, and wrap the result in a new fact, which you yield from the right-hand side of the rule.

    When()
        .Match<Person>(() => person, p => p.Age < 30)
        .Let(() => cars, () => person.Cars.Where(c => c.Year > 2016))
        .Having(() => cars.Any());
    Then()
        .Yield(ctx => new YoungPersonWithNewCar(person, cars));

    Then another rule matches these new facts and collects them.

    When()
        .Query(() => youngPeopleWithNewCars, q => q
            .Match<YoungPersonWithNewCar>()
            .Collect()
            .Where(c => c.Any()));
    Then()
        .Do(ctx => DoSomethingWithNewCarsThatBelongToYoungPeople(youngPeopleWithNewCars));
    RajkumarViven
    @RajkumarViven
    Thanks @snikolayev its working fine
    @snikolayev can we use multiple Then() in Our rule because we need to call Multiple Actions based on conditions like
    if age <30 we can call the action x()
    else if age >30 we can call the action y()
    RajkumarViven
    @RajkumarViven
    Is there any options to call the actions like that?
    RajkumarViven
    @RajkumarViven
    ?
    RajkumarViven
    @RajkumarViven
    is there any option @snikolayev ?
    Sergiy Nikolayev
    @snikolayev
    You would need to write separate rules that deal with different conditions.
    RajkumarViven
    @RajkumarViven
    ok thanks for your update
    larryPlayabl
    @larryPlayabl

    @snikolayev I saw that you're doing some work on rete optimization, such as this commit here:

    NRules/NRules@6001f4a

    If it'd be helpful, I'd be happy to try a new version with our project and provide a comparison using the Unity 3D Engine's profiler again, as I did in this issue:

    NRules/NRules#200

    If there are two commits you'd like me to do an A/B comparison with, just let me know.

    Or I can wait a bit if there's more you're planning to do.

    larryPlayabl
    @larryPlayabl
    Do you think that changes made in this commit will help with the performance issues I described in that issue?
    Sergiy Nikolayev
    @snikolayev
    @larryPlayabl the optimizations I'm working on will reduce the amount of expression re-evaluations in the rete graph when propagating updates. This does not reduce allocations in join nodes, but you may be able to observe the improvements just due to the reduction of the number of joins. This is however just the first step in improving the performance of the rete graph, so I'm also planning to address allocations more directly.
    larryPlayabl
    @larryPlayabl
    @snikolayev That makes sense. Glad to hear you're planning on continuing improvements, including focusing on allocations. Let me know if I can help with a round of profiling at some point.
    przemekwojcik
    @przemekwojcik
    @snikolayev is there any easy way to find out because of which PatternElement a rule was fired? Once a rule is fired i have an ActionContext available, it has multiple properties. I see something like ActionContext.Rule.LeftHandSide.ChildElements[0]...Conditions[0] - is there a way to find out has that condition fired during processing of a fact?
    larryPlayabl
    @larryPlayabl

    @przemekwojcik Check out ISession.Events.LhsExpressionEvaluatedEvent. This event fires every time a When (LHS) expression is evaluated, and references all rules that contain this expression. You can monitor these events over time to see what is matching for a given rule.

    In a rete net, you have to do a bit of legwork to figure out exactly which pattern matches cause a rule to fire because expressions can be shared between multiple rules, and their results are cached.

    Therefore you need to build some tooling to do monitor and associate evaluations with a given rule firing.

    The NRules debugger visualizer can help to illustrate this for you.
    Sergiy Nikolayev
    @snikolayev
    One thing I would like to add to what @larryPlayabl said is that a rule fires when ALL the patterns on the left-hand side matched. So, it's a bit incorrect to ask "which of them caused the rule to fire", because the answer is "all of them".
    larryPlayabl
    @larryPlayabl
    Good point! I guess I was thinking about the case of an Or, in which case it's a bit less clear.
    Paweł Adamczuk
    @PawelAdamczuk
    Hello! Could somebody point me to a resource that would explain why a concept of a RuleSet is needed? I am making a proof of concept for a product and I can just use a single Rule Set but I suppose this class exists for a reason. I'm pretty new to Business Rules in general.
    Ivan Li
    @ivanycli
    hello. i'm a new nrule user. i wanted to know if it's possible for a rule to add an object into the ctx which can be used by another rule. i'm defining a series of checks across rules and looking for a way to capture all the results in single entity which i'm inserting into the context when the first rule fires. is it better to insert the various results into the ctx then do a session.query from the main caller? i'm open to better approaches to handling this scenario. thanks!
    Sergiy Nikolayev
    @snikolayev
    @PawelAdamczuk a rule set is just a collection of rules. You can then decide which rules to compile based on which rule set they are in. The notion of the rule set does not exist at the DSL level or at execution time (when compiled to a rete graph). In the future rule sets may start playing a bigger role, but right now it's limited to just organizing and filtering rules at a rule model level.
    @ivanycli have you seen docs on forward chaining? https://github.com/NRules/NRules/wiki/Forward-Chaining
    Paweł Adamczuk
    @PawelAdamczuk
    Thank you!
    Ivan Li
    @ivanycli
    Thanks @snikolayev , I'll review the forward chaining documentation
    lucaszacutti
    @lucaszacutti
    Hi! I'm totally newy with nrules, Can I have nested rules? I would like to test a condition applicable to several nested rules and any nested rule with it own sub-condition and/or other nested rules. Is that possible?
    larryPlayabl
    @larryPlayabl
    @lucaszacutti You can't have a nested rule, but you can have rule logic shared between multiple rules. One way to do this is with a base class to a rule. Another is to call utility methods or extension methods that add clauses to your rule. You'll need to pass any context into such utility methods. Check out this page for an example: https://github.com/NRules/NRules/wiki/DSL-Extensions
    tnghub3
    @tnghub3
    Does it support decision table?
    Similar to decision table with drools and brms
    Sergiy Nikolayev
    @snikolayev
    @tnghub3 no, it doesn’t
    tnghub3
    @tnghub3
    Any recommendations on how to do it with nrules?
    larryPlayabl
    @larryPlayabl

    @snikolayev I'm curious to experiment a bit with some resource pooling to try to reduce allocations in NRules. I'd love to hear your thoughts on it when you have a moment.

    One low-hanging-fruit spot I'd like to try is Tuple reuse. It's very clear where Tuples are constructed, and they could instead come from a pool of Tuples which can be reused. Any ideas to help me track down the point/points in the code where can a Tuple be safely returned to the pool for reuse?

    Sergiy Nikolayev
    @snikolayev
    @tnghub3 I suggest you step back and look at the problem you are trying to solve. Decision table is just a tool; see if you can reframe your problem in a way that can be solved with predicate logic/production rules. If so, you can use NRules to encode that logic. If decision tables is the only way to solve your problem, I would look for an appropriate library/framework outside NRules that would allow you to do that.
    Sergiy Nikolayev
    @snikolayev
    @larryPlayabl I think it's a great idea to explore resource pooling. But I don't think Tuples is a good candidate. Tuples in NRules are stateful and long lived. They are also small and not expensive to create. The candidates I'm thinking about are arrays of facts created during expression evaluation, which are allocated and then collected right away. Another candidate is fact enumerator in a tuple, which is created numerous times during fact propagation through the graph. Also, fact lists and tuple lists created during propagation are good candidates for pooling; though these are harder to tackle due to the recursive nature of propagation. One other idea I have is to convert fact propagation from recursive to iterative, at which point pooling fact/tuple lists should be much easier.
    larryPlayabl
    @larryPlayabl

    @snikolayev Thanks for the pointers. Yes, it makes sense to start with the easiest options that offer the greatest optimization for amount of effort and risk. I'll openly admit that I'm not especially well versed in this kind of optimization, and would definitely want to keep you in the loop as I experiment to make sure I'm going in the right direction. I know there can be subtle details in the implementation that may make all the difference in how much allocation savings we're actually reaping.

    The object arrays created in the LHSExpression Invoke methods look like they'd be pretty straightforward to pool. Do you believe that swapping these arrays out for List<object> that are cleared and repopulated each time would offer an allocation advantage?

    If that won't help significantly, I could also imagine caching a Dictionary<ITuple, object[]> to be used by these LHSExpression methods. I believe this would work because the object[] will always be the same size for the Tuple. Obviously, this cache would need to be updated whenever a given tuple's facts change. That could either be done by iterating through them and updating them on each call to Invoke, or by checking for a "dirty" flag on the tuple which would indicate that the facts have changed since the last invoke, though that'd be more complex.

    For the Tuple.Facts IEnumerable, I could give each Tuple a List<Fact> that's cleared and repopulated by walking the tuples on each call to get the IEnumerable. However, again, maybe there'd be a more efficient way to do that and only update the Facts list as needed. Think that'd be possible?