RIF example UC9: BPEL Orchestration of Rule-Based Web Services
Kindly send comments on this page to GaryHallmark
Contents
Summary
This use case is about rules that score a credit report represented as an XML document.
The source rules are production rules (PR) with actions other than assert, suggesting that we invent a PR dialect that extends RIF Core in order to translate these rules to RIF. It turns out that most of the rules can be restated as PR with only assert actions, and these restated PR can be translated to RIF Core augmented with aggregation. That is the route we will take.
We will express the translated rules in a "human readable" RIF because their elaboration as XML probably does not help understand the key issues of mapping production rules to RIF at this point.
Background
Use case BPEL Orchestration of Rule-Based Web Services is about rules that score a credit report represented as an XML document. The sources are provided as Oracle Rule Language (RL), a production rule language with its roots in CLIPS but with a more modern Java-like syntax. (Note that business users do not program in RL directly.)
The use case is loosely based on a commercial credit application.
Source data and rules
The actual application has about 100 rules. Each rule interprets one or a few credit report attributes, and typically increments an overall credit score variable. We show just a fragment of the credit report document, and a redacted ruleset in RL containing the definition of the credit score variable, a variable indicating whether credit is approved, and two rules with priorities.
RL relies on the Java API for XML Binding (JAXB) to translate XML schema to Java classes, and to translate XML instance documents to Java objects.
First we show the "original" ruleset in RL (before transforming to assert actions).
JAXB processing of credit report schema
The schema of a simple credit report document such as the following
<credit-report> <name>Joe Slacker</name> <ssn>923764388</ssn> <income>45000</income> ... </credit-report>
results in a Java class such as the following
class CreditReport { String name; String ssn; BigDecimal income; }
Original production rules
The following RL ruleset then references the credit report
ruleset creditScoring { int score = 0; boolean approved = false; rule r1 { // If applicant's income is between 40000 and 50000 then add 40 priority = 1; if (fact CreditReport cr && cr.income > 40000 && cr.income <= 50000) { score += 40; } } rule r2 { // approve if total score is over 300 priority = 0; // must execute after the scoring rules if (true) { approved = score > 300; } } }
Production rules using assert actions and aggregation
The above can be rewritten to use only assert actions and aggregation:
ruleset creditScoring2 { class ScoreIncrement { int value; } class Approved {} rule r1 { // If applicant's income is between 40000 and 50000 then add 40 if (fact CreditReport cr && cr.income > 40000 && cr.income <= 50000) { assert(new ScoreIncrement(value: 40)); } } rule r2 { // approve if total score is over 300 if (aggregate (fact ScoreIncrement si) : sum(si.value) score && score > 300) { assert(new Approved()); } } }
Translation of PR with only assert actions into RIF Core
The following translation assumes the following
- frame syntax for translating Java and RL class references
xpath builtins differentiated from logical functions with preceding '&'
- a new SUM builtin that can perform closed-world aggregation
- rules with no universally quantified variables do not need an enclosing "forall"
ScoreIncrement[value->40] :- Exists ?income ( And ( CreditReport[income->?income] &numeric-greater-than(?income 40000) &numeric-greater-than(50000 ?income) )) Approved[] :- Exists ?increment ?score ( And ( ScoreIncrement[value->?increment] ?score = &SUM(?increment) &numeric-greater-then(?score 300) ))
Issues
A few issues with the above translation are worth disussion.
Naming
How do we attach the ruleset and rule names? There is no place in the ASN or concrete syntax at present.
I don't know what to do about namespace prefixes in the human-readable syntax. I omitted them.
Datatypes
Literals such as 300 and 40000 are not tagged as 300^^int or 40000^^BigDecimal. Is this required?
Builtins and ordering of conjuncts
Order matters. CreditReport[income->?income] must come before &numeric-greater-than(?income 40000) so that ?income is bound.
Aggregation
Adding &SUM is easy syntactically, but the semantics are non-monotonic. How do we extend Core semantics to support aggregation?
Translation of arbitrary PR
Neither r1 nor r2 from the original creditScoring ruleset has a head (neither rule asserts a fact into working memory). R2 doesn't even have a body. How can these actions be expressed?
A notion of priority is needed because r2 must execute after r1 in order to read the total score.
Changes
GaryHallmark rewrote most of this page on June 22, 2007 to rephrase as PR with assert actions and aggregation and to use the "latest" Core syntax (not all approved)