Coder Social home page Coder Social logo

Comments (5)

Clayton7510 avatar Clayton7510 commented on May 18, 2024

Yes, you should be able to re-use a RuleBook by repeatedly calling run in the same thread.
Currently, RuleBook will chain the facts supplied to each rule on the run method.

from rulebook.

typhoon1978 avatar typhoon1978 commented on May 18, 2024

Ok, probably I'm using it in a wrong way but I try to explain as better as I know.

The problem I meet is this: I have a collection of rules that will evaluate a list of input objects, so my RuleBook represent a list of rules I want to "test" on all those objects that, according to tests, will be clustered setting a PeopleCluster enum value through the Consumer implementation.

If I instantiate it once, I have "repeated objects" in output not corresponding to their input objects.
If I instantiate it in the loop I have the correct behavior or better the behavior I expect.

And this is the example...

I have a testCases method which returns a list of NameValueReferableMap where MyBean is a simple bean with properties the rules will test through their predicates:

List<NameValueReferableMap<MyBean>> factsList = testCases();

All those beans are loaded from database and populated.

Then, I create my rules book EvaluationRuleBook which is an extension of CoRRuleBook, which contains a set of rules in defineRules method:

@Override
public void defineRules() {

	addRule(RuleBuilder.create().withFactType(MyBean.class).withResultType(MyResult.class)
				.when(ageLessThan(18)).then(unevaluated()).stop().build());
	addRule(RuleBuilder.create().withFactType(MyBean.class).withResultType(MyResult.class)
				.when(peopleFromUS()).then(evaluatedWith(true, true, PeopleCluster.F_1))
				.build());
         // and so on
}

Then I'm going to test this implementation:

RuleBook<MyResult> evaluationRuleBook = RuleBookBuilder.create(EvaluationRuleBook.class)
					.withResultType(MyResult.class)
					.withDefaultResult(new MyResult(null, false, false, null))
					.build();
factsList.forEach(x -> {
		evaluationRuleBook.run(x);
		evaluationRuleBook.getResult().ifPresent(result -> {
		MyResult ref = result.getValue().getRef();
		System.out.println(String.format("Item: %s, PeopleCluster: %s", ref.toString(), ref.getCluster().name()));
});

The unexpected is that in my loop I have the same number of input objects but "repeated": It seems return value is stored and for some reason not overwritten.

I expect that for every single object I "run" I have a return result referred to its input value.

On the contrary, if I instantiate the evaluationRuleBook inside the loop so that a new instance is built and used for each element of the factsList I have the expected results.

Is it my fault? Am I using it in a wrong way? Is the code sufficient to understand my problem?

Thanks in advance,
Alessandro.

from rulebook.

typhoon1978 avatar typhoon1978 commented on May 18, 2024

I repeated tests and understood that when rules predicates are not met, it returns the last not-null Result stored.

I expected a default Result like:

new MyResult(null, false, false, null)

as declared:

RuleBook<MyResult> evaluationRuleBook = RuleBookBuilder.create(EvaluationRuleBook.class)
					.withResultType(MyResult.class)
					.withDefaultResult(new MyResult(null, false, false, null))
					.build();

from rulebook.

typhoon1978 avatar typhoon1978 commented on May 18, 2024

Ok, I understood and now I need your help to understand if my concept of "default result" is the same of yours.

Method setDefaultResult is called inside withDefaultResult, so it's called once when we build the RuleBook and it stores the "default" return value in the same property used for the real return value.

When some rule is met and we have a real result, that default result is overwritten by the real return value and so we don't have a default result anymore.

What I needed was to store a default return value for the whole RealBook life-cycle and I thought that withDefaultResult method, called on the RealBook level, could be used for this purpose.

So the final question is... is the default result valid until we have a not-null result to return? or simply is it a bug?

Thanks in advance,
Alessandro.

from rulebook.

Clayton7510 avatar Clayton7510 commented on May 18, 2024

You are correct that withDefaultResult sets the initial result for the RuleBook by calling setDefaultResult. Within RuleBook, the Result object contains a reference to an object of the result type you specified. The withDefaultResult method initializes the Result object and allows that object to be chained across rules within a RuleBook. It is in effect an entity for that RuleBook (think EE's definition from DDD).

So, a default Result is never actually overwritten. However, the value of the Result object can be changed.

If you want to get the reference to the current result value, then modifying your example, you could do something like this.

public class EvaluationRuleBook extends CoRRuleBook<MyResult> {
    @Override
    public void defineRules() {
        addRule(RuleBuilder.create().withFactType(MyBean.class).withResultType(MyResult.class)
                .when(facts -> facts.getOne().getAge() <18) //assuming only one fact is used
                .then(f -> {})
                .stop()
                .build());
        addRule(RuleBuilder.create().withFactType(MyBean.class).withResultType(MyResult.class)
                .when(facts -> facts.getOne().isFromUSA()) //still assuming only one fact is used
                .then((facts, result) -> result.getValue().getPeopleInUSA().add(facts.getOne()))
                .build());
    }
}

public class MyBean {
    public MyBean(String name, int age, boolean american) {
        this.name = name;
        this.age = age;
        this.american = american;
    }

    private String name;
    private int age;
    private boolean american;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isFromUSA() {
        return american;
    }

    public void setFromUSA(boolean fromUSA) {
        american = fromUSA;
    }

    @Override
    public String toString() {
        return "MyBean{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", american=" + american +
                '}';
    }
}

Then you could run it using something like the following.

    public static void main(String args[]) {
        RuleBook<MyResult> evaluationRuleBook = RuleBookBuilder.create(EvaluationRuleBook.class)
                .withResultType(MyResult.class)
                .withDefaultResult(new MyResult())
                .build();

        MyBean[] beans = new MyBean[] {
                new MyBean("Herbert", 25, true),
                new MyBean("Edgar", 53, false),
                new MyBean("Joe", 17, true),
                new MyBean("Buck", 20, true)};

        for (MyBean bean : beans) {
            NameValueReferableMap<MyBean> facts = new FactMap<>();
            facts.setValue(bean.getName(), bean);

            evaluationRuleBook.run(facts);
        }

        evaluationRuleBook.getResult().ifPresent(result ->
            result.getValue().getPeopleInUSA().forEach(System.out::println));
    }

Using the above example, the MyBean instances for Herbert and Buck are in the rulebook's result upon completion of the loop and as a consequence they are printed to the console.

Personally, however, I would probably set different facts and run through the set of rules all at once instead of repeatedly calling your RuleBook subclass. Of course, I may have misinterpreted your exact application. :-)

Did that help to clarify?

from rulebook.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.