Application of rule engine Drools in post-loan collection business

Author: vivo Internet Server Team - Feng Xiang

In daily business development work, we often encounter scenarios where decisions are made based on business rules. In order to free developers from the development and maintenance of a large number of rule codes, the maintenance and generation of rules are handed over to business personnel. To achieve this goal, we usually use a rule engine to help us achieve it.

This article mainly introduces the concept of rule engine and the relationship between Kie and Drools, focusing on the writing of rule files in Drools and the principle of matching algorithm Rete. At the end of the article, it shows how the rule engine is used in the collection system and the main problems to be solved.

1. Business background

1.1 Introduction to collection business

Consumer loans are an important business segment in the vivo wallet. When there are overdue cases that need to be processed, we will collect case statistics and import them into the collection system. A series of rules are defined in the collection system to help the business side according to the customer's overdue To formulate collection strategies based on the principles of degree, risk compliance assessment, operating costs and maximizing returns. For example, "case division rules" will assign different types of cases to different queues according to the rules, and then assign them to various collection positions and collectors through the queues, and finally the collectors will carry out the collection. Below I will introduce in detail in combination with specific scenarios.

1.2 Introduction to rule engine

1.2.1 Introduction of questions

Case: According to the above divisional rules, we list the following rule sets:

Code implementation: implement the above rule set with code

1 2 3 4 5 6 7 8 9 10 11 12 13 if(overdueDays>a && overdueDays<b && overdueAmt <W){     taskQuene = "A queue"; } else if(overdueDays>c && overdueDays<d && overdueAmt <W){     taskQuene = "B queue"; } else if(overdueDays>e && overdueDays<f && overdueAmt <W){     taskQuene = "C queue"; } else if(overdueDays>h && overdueDays<g && overdueAmt <W){     taskQuene = "D queue"; } ......

Business changes:

 

  1. Condition fields and result fields can grow and change frequently.
  2. The rule sets listed above are just one type of rules. In fact, there are many other types of rule sets in our system.
  3. The rules are best maintained by business personnel and can be modified at any time without the intervention of developers, let alone restarting the application.

 

Problems arise: It can be seen that if there are many rules or more complex scenarios, a lot of if else codes like this need to be written in the code, and it is not easy to maintain. Once new conditions or rules are changed, a lot of code needs to be changed.

At this point, we need to introduce a rule engine to help us separate the rules from the code, so that developers can be freed from the code logic of the rules, and the maintenance and setting of the rules can be managed by business personnel.

1.2.2 What is a rule engine

Developed from the inference engine, the rule engine is a component embedded in the application, which separates the business decision from the application code and writes the business decision using a predefined semantic module.

Interpret business rules by receiving data input, and finally make business decisions based on business rules. Commonly used rule engines are: Drools, easyRules and so on. In this article, we mainly introduce Drools.

2. Drools

2.1 Overall introduction

2.1.1 Introduction to KIE

Before introducing Drools, we have to mention a concept KIE. KIE (Knowledge Is Everything) is a comprehensive project that integrates some related technologies and is also the core of each technology. It includes what I will talk about today. Drools.

Technical composition:

 

  1. Drools is a business rule management system with a rule engine based on forward chain and backward chain reasoning, allowing fast and reliable evaluation of business rules and complex event processing.
  2. jBPM is a flexible business process management suite that allows you to model your business goals by describing the steps that need to be performed to achieve those goals.
  3. OptaPlanner is a constraint solver that optimizes use cases such as employee scheduling, vehicle routing, task assignment, and cloud optimization.
  4. UberFire is an Eclipse-based rich client platform web framework.

 

2.1.2 Introduction to Drools

The basic function of Drools is to match incoming data or facts against the conditions of a rule and determine whether and how to execute the rule.

Advantages of Drools: It is easy to learn and master based on Java, and it can dynamically generate rule scripts through decision tables, which is very friendly to business personnel.

Drools uses the following basic components:

 

  • rule (rules): User-defined business rules, all rules must at least include the conditions to trigger the rules and the operations specified by the rules.
  • Facts: Data entered or changed into the Drools engine, which matches rule conditions to execute applicable rules.
  • production memory: memory used to store rules.
  • working memory: The memory used to store facts.
  • Pattern matcher (matcher): pattern match all the rules in the rule base with the fact object in the working memory, and put them into the agenda after the match is successful
  • Agenda (agenda): store the rules activated after the matcher is successfully matched for execution.

 

When a user adds or updates information about a rule in Drools, that information is inserted into the working memory of the Drools engine in the form of one or more facts. The Drools engine pattern matches these facts against rule conditions stored in production memory.

When the rule conditions are met, the Drools engine activates and registers the rules in the agenda, then the Drools engine prioritizes them and prepares them for execution.

2.2 Rules

2.2.1 Rule file analysis

DRL (Drools Rules Language) are business rules defined in drl text files. It mainly includes: package, import, function, global, query, rule end, etc. Drools also supports Excel file format.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package  //Package name, this package name is just a logical package name and does not have to be consistent with the physical package path.    import   //import class same as java    function  //custom function    query  //  Inquire    global   //global variable    rule "rule name"  //Define the rule name, the name is unique and cannot be repeated     attribute //rule attribute     when         //rule condition     then         //trigger behavior end    rule "rule2 name"    ...
  • function

The methods in the rule file are similar to the methods defined in our usual code, which improves the reuse of rule logic.

Use Cases:

1 2 3 4 5 6 7 8 9 10 function String hello(String applicantName) {     return "Hello " + applicantName + "!"; }    rule "Using a function"   when     // Empty   then     System.out.println( hello( "James" ) ); end
  • query

A query in a DRL file is a search of the Drools engine's working memory for facts related to the rules in the DRL file. Add the query definition in the DRL file, and get the matching results in the application code. Queries search for a defined set of conditions and do not require when or then specifications.

Query names are global to the KIE library and therefore must be unique among all other rule queries in the project. Return query results ksession.getQueryResults("name"), where "name" is the query name.

Use Cases:

rule:

1 2 3 4 5 6 7 query "people under the age of 21"     $person : Person( age < 21 ) end      QueryResults results = ksession.getQueryResults( "people under the age of 21" ); System.out.println( "we have " + results.size() + " people under the age  of 21" );
  • global variable global

To set a global value in the working memory of the Drools engine via the KIE session configuration, declare the global variable above the rule in the DRL file and then use it in the action (then) section of the rule.

Use Cases:

1 2 3 4 5 6 7 8 9 10 11 12 13 List<String> list = new ArrayList<>(); KieSession kieSession = kiebase.newKieSession(); kieSession.setGlobal( "myGlobalList", list );      global java.util.List myGlobalList;    rule "Using a global"   when     // Empty   then     myGlobalList.add( "My global list" ); end
  • rule attribute

 

  • pattern matching

When the facts are inserted into the working memory, the rule engine will match the facts with the patterns in the rule base, and then Agenda will execute the (then) part of the rules in the reasoning algorithm for the rules that match successfully.

  • when

The "when" part of the rule, also known as the left side of the rule (LHS), contains the conditions that must be met to perform the action. If the when part is empty, it defaults to true. If multiple rule conditions can be used (and,or), the default conjunction is and. If the bank requires the loan applicant to be over 21 years old, then the when condition of the rule is Applicant(age < 21)

1 2 3 4 5 6 7 rule "Underage"   when     application : LoanApplication()//Indicates that there is an Application fact object and the age attribute satisfies <21     Applicant( age < 21 )   then     // Actions end
  • then

The "then" part of the rule, also known as the right side of the rule (RHS), contains the actions to be performed when the condition part of the rule is met. For example, banks require loan applicants to be over 21 years old (Applicant( age < 21 )). Refuse loan if not satisfied setApproved(false)

1 2 3 4 5 6 7 rule "Underage"   when     application : LoanApplication()     Applicant( age < 21 )   then     application.setApproved( false ); end
  • built-in method

Drools mainly operates the fact data in the working memory through the insert and update methods to achieve the purpose of controlling the rule engine.

After the operation is completed, the rule engine will re-match the rules. The rules that did not match successfully may match successfully after we modify the data.

Note: These methods will cause re-matching, which may cause an infinite loop problem. It is best to set the attribute no-loop or lock-on-active attribute to avoid it when writing.

(1)insert:

Function: insert fact data into the working memory, and let the relevant rules re-match

1 2 3 4 5 6 7 8 rule "Underage"   when     Applicant( age < 21 )   then     Applicant application = new application();     application.setAge(22);     insert(application);//Insert the fact re-matching rules, and the rules with age>21 are directly triggered end

(2)update:

Function: modify the fact data in the working memory and make the relevant rules match again

1 2 3 4 5 6 7 8 rule "Underage"   when     Applicant( age < 21 )   then     Applicant application = new application();     application.setAge(22);     insert(application);//Insert the fact re-matching rules, and the rules with age>21 are directly triggered end

comparison operator

2.3 Project introduction

2.3.1 Introduction of configuration files

There needs to be a configuration file to tell where the code rule file drl is. In drools, this file is kmodule.xml, which is placed in the resources/META-INF directory.

Description: kmodule is a new configuration and convention method introduced after 6.0 to build the KIE library instead of using the previous programmatic builder method.

1 2 3 4 5 6 7 8 9 10 11 <kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns="http://www.drools.org/xsd/kmodule">   <kbase name="KBase1" default="true" packages="org.domain.pkg1">     <ksession name="KSession2_1" type="stateful" default="true"/>     <ksession name="KSession2_2" type="stateless" default="false"/>   </kbase>   <kbase name="KBase2" default="false" packages="org.domain.pkg2, org.domain.pkg3">     <ksession name="KSession3_1" type="stateful" default="false">     </ksession>   </kbase> </kmodule>
  • Kmodule can contain one or more kbase s, corresponding to drl rule files.

  • Kbase is the repository of all application knowledge definitions, including several rules, processes, methods, etc. A unique name is required, which can be any string.

The default attribute of KBase indicates whether the current KBase is the default one. If it is the default one, the KBase can be found without the name, but each module can only have one default KBase at most.

There can be one or more ksessions under KBase, and the name attribute of ksessions must be set and must be unique.

  • packages is the path under the resource directory where the drl file is located. Multiple packages are separated by commas. Usually, the drl rule file will be placed in the resource directory of the project.

2.3.2 Use in code

KieServices: You can access all Kie construction and runtime interfaces, and use it to obtain various objects (for example: KieContainer) to complete rule construction, management and execution.

KieContainer: KieContainer is a KModule container that provides methods for obtaining KBase and creating KSession. The method of obtaining KSession still uses KBase to create KSession internally.

KieSession: KieSession is a dialogue connection to the rule engine, through which it can communicate with the rule engine and initiate the operation of executing rules. For example: use the kSession.insert method to insert the fact (Fact) into the engine, which is the Working Memory, and then use the kSession.fireAllRules method to notify the rule engine to execute the rules.

1 2 3 4 5 6 7 KieServices kieServices = KieServices.Factory.get(); KieContainer kContainer = kieServices.getKieClasspathContainer(); KieBase kBase1 = kContainer.getKieBase("KBase1"); //Get the specified KBase KieSession kieSession1 = kContainer.newKieSession("KSession2_1"); //Get the specified KSession kieSession1.insert(facts);//Rules are inserted into working memory kSession.fireAllRules();//start execution kSession.dispose();//close conversation

Explanation: The above case is the API of Kie used (version after 6.x)

2.4 Pattern Matching Algorithm-RETE

The Rete algorithm was invented by Dr. Charles Forgy and documented in his doctoral dissertation in 1978-79. The Rete algorithm can be divided into two parts: rule compilation and runtime execution.

The compiled algorithm describes how to process the rules in production memory to generate an efficient decision network. In non-technical terms, decision networks are used to filter data as it travels through the network.

Nodes at the top of the network will have many matches, and as the network goes down, there will be fewer and fewer matches, and at the very bottom of the network are terminal nodes.

The official description of the RETE algorithm is relatively abstract, and here we illustrate it with a specific case.

2.4.1 Case Description

Suppose you have the following fact objects:

A(a1=1,a2="A")

A(a1=2,a2="A2")

B(b1=1,b2="B")

B(b1=1,b2="B2")

B(b1=2,b2="B3")

C(c1=1,c2="B")

Existing rules:

1 2 3 4 5 6 7 8 rule "Rete"   when     A(a1==1,$a:a1)     B(b1==1,b1==$a,$b:b2)     C(c2==$b)   then     System.out.print("successful match"); end

Bete network:

 

2.4.2 Node description

1.Root Node: The root node is where all objects enter the network

2.one-input-node (single input node)

[ObjectTypeNode]: The object type node is the successor node of the root node, used to determine whether the type is consistent

[AlphaNode]: Used to judge text conditions, for example (name == "cheddar", strength == "strong")

[LeftInputAdapterNode]: Take an object as input and propagate a single object.

3.two-input-node (double input node)

[BetaNode]: It is used to compare two objects, which may be of the same or different types. The join node used in the above case is a type of betaNode. The join node is used to connect the left and right inputs. The left input is a list of fact objects, and the right input is a fact object. The join node is compared according to the object type or object field. BetaNodes also have memory. The input on the left is called Beta Memory and it remembers all incoming object lists. The input on the right is called Alpha Memory and it remembers all the fact objects passed in.

4.TerminalNode:

Indicates that a rule has matched all of its conditions, and a rule with an "or" condition generates subrules for each possible branch of logic, so a rule can have multiple terminal nodes.

2.4.3 RETE network construction process

 

  1. Create a virtual root node
  2. Take out a rule, e.g. "Rete"
  3. Take out a pattern such as a1==1 (pattern: refers to the condition of the when statement, where the when condition may be a large condition composed of several smaller conditions. The pattern refers to the smallest atomic condition that cannot be further divided) , check the parameter type (ObjectTypeNode), if it is a new type, add a type node;
  4. Check the conditional constraints of the mode: for a single-type constraint a1==1, check whether the corresponding alphaNode already exists, if not, add the constraint as a successor node of the chain as an alphaNode;
  5. If it is a multi-type constraint a1==b1, then create the corresponding betaNode, whose left input is LeftInputAdapterNode, and the right input is the alphaNode of the current chain;
  6. Repeat 4 until all constraints of the pattern are processed;
  7. Repeat 3-5 until all modes are processed, create a TerminalNode, and connect the end of each mode chain to TerminalNode;
  8. Wrap the (Then) part into an output node.

 

2.4.4 Runtime Execution

 

  1. Take a working memory element WME (Working Memory Element, WME for short) from the working memory and put it into the root node for matching. WME is an element built for facts, an element used to match patterns represented by non-root nodes.
  2. Traverse each alphaNode and ObjectTypeNode, if the constraints are consistent with the WME, store the WME in the matching memory of the alphaNode and propagate it to its successor nodes.
  3. Match each betaNode, match the object list in the left memory with the object in the right memory according to the node constraints, and if the condition is met, merge the fact object with the left object list and pass it to the next node.
  4. After both and 3 are completed, the list of fact objects enters the TerminalNode. The corresponding rules are activated and the rules are registered into the agenda (Agenda).
  5. The rules in Agenda are executed according to the priority.

 

2.4.5 Sharing mode

The following is an example of pattern sharing, two rules share the first pattern Cheese( $cheddar : name == "cheddar" )

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 rule "Rete1" when     Cheese( $cheddar : name == "cheddar" )     $person : Person( favouriteCheese == $cheddar ) then     System.out.println( $person.getName() + " likes cheddar" ); end    rule "Rete2" when     Cheese( $cheddar : name == "cheddar" )     $person : Person( favouriteCheese != $cheddar ) then     System.out.println( $person.getName() + " does not like cheddar" ); end

Network diagram: (the type on the left is Cheese, and the type on the right is Person)

 

2.4.6 Summary

The rete algorithm essentially achieves performance improvement by sharing rule nodes and caching matching results.

[Status Save]:

For each change in the fact set, its matching state is saved in alphaMemory and betaMemory. When the fact set changes next time (most of the results do not need to change), a large number of repeated calculations are avoided by fetching values ​​from memory.

The Rete algorithm is mainly designed for systems whose fact sets do not change much. When the change of the fact sets is very drastic each time, the effect of rete's state preservation algorithm is not ideal.

[Node Sharing]:

For example, in the above case, different rules contain the same pattern and can share the same node.

[hash index]:

Every time an AlphaNode is added to an ObjectTypeNode successor, it will add the literal value as key to the HashMap with the AlphaNode as the value. When a new instance comes in to an ObjectType node, instead of propagating to each AlphaNode, the correct AlphaNode can be retrieved from the HashMap, avoiding unnecessary literal checks.

There is a problem:

 

  1. There is a problem of repeated state saving, and the fact that multiple patterns have been matched must be stored in the node cache of these patterns at the same time, which will take up more space and affect the matching efficiency.
  2. Not suitable for frequently changing data and rules (data changes cause frequent changes in the temporary facts saved by nodes, which will make rete lose the advantage of incremental matching; data changes make various optimization methods for rule networks such as indexing and conditional sorting lose effect ).
  3. The rete algorithm uses alphaMemory and betaMemory to store the calculated intermediate results, sacrificing space for time, thus speeding up the system. However, when dealing with massive data and rules, the beta memory grows exponentially according to the conditions of the rules and the number of facts, so when there are many rules and facts, system resources will be exhausted.

The matching algorithm used in the early version of Drools is Rete. Since 6.x, the phreak algorithm has been introduced to solve the problems caused by Rete.

For the phreak algorithm, you can see the official introduction: https://docs.drools.org/6.5.0.Final/drools-docs/html/ch05.html#PHREAK

3. Application in collection business

3.1 Problem Solving

The example from the question at the beginning of the article can be realized by writing drl rule scripts, and each rule change only needs to modify the drl file.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package vivoPhoneTaskRule;    import com.worldline.wcs.service.rule.CaseSumNewWrapper;       rule "rule1"     salience 1     when         caseSumNew:CaseSumNewWrapper(overdueDD > a && overdueDD < b && overdueAmt <= W)     then         caseSumNew.setTaskType("A queue"); end    rule "rule2"     salience 2     when         caseSumNew:CaseSumNewWrapper(overdueDD > c && overdueDD < d && overdueAmt <= W)     then         caseSumNew.setTaskType("B queue"); end    rule "rule3"     salience 3     when         caseSumNew:CaseSumNewWrapper(overdueDD > e && overdueDD < f && overdueAmt <= W)     then         caseSumNew.setTaskType("C queue"); end    rule "rule4"     salience 4     when         caseSumNew:CaseSumNewWrapper(overdueDD > h && overdueDD < g && overdueAmt > W)     then         caseSumNew.setTaskType("D queue"); end

Generate a new question:

Although the problem of rule maintenance can be solved by writing drl, it is obviously difficult for business personnel to write such a set of rule scripts, so how to do it in the collection system, let's continue to look down.

3.2 Design of rules

3.2.1 Decision table design

The collection system has developed a set of decision table solutions, which abstracts the conditions and result statements in drl into structured data for storage, and creates a visual page on the front end for business personnel to edit without writing rule scripts. For example, to add a rule:

Allocate the cases whose overdue days are greater than a day and less than b days and the total overdue amount is less than or equal to c to the A queue.

 

Each row in the table corresponds to a rule, and business personnel can modify and add according to the rules, and can also expand the decision table according to the condition definition.

The main components of the decision table are:

 

 

  • Rule condition definition: defines the conditions used in some rules, such as: overdue days, overdue amount, etc.
  • Rule result definition: defines the results of some rules, such as: which queues are assigned to, the time spent in the queue, etc.
  • Condition field: The condition field that needs to be used when editing a rule (select from the condition definition list).
  • Comparison operators and values: comparison operators include: <, <=, >, >=, ==, !=, temporarily do not support contain, member Of, match, etc.

 

Conditional values ​​currently contain numbers and characters. Conditional field + comparison operator + value constitutes a conditional statement.

  • Result: The final result after the condition is met is the field value in the result definition.

3.2.2 Rule generation

The collection system provides the function of visual page configuration to dynamically generate scripts (business personnel edit decision tables according to condition definitions and result definitions to formulate corresponding rules).

Core process:

1. Parse the corresponding fact object mapping file according to the rule type, and encapsulate it into condition entity entitys and result entity resultDefs. The content of the file is as follows:

fact object mapping xml

1 2 3 4 5 6 7 8 9 10 11 12 13 <rule package="phoneTask">     <entitys>         <entity note="collectionCaseInfo" cls="com.worldline.wcs.service.rule.FactWrapper" alias="caseSumNew">             <attribute attr="caseSumNew.overdueDD" />             <attribute attr="caseSumNew.totalOverdueAmt"/>         </entity>     </entitys>     <resultDefs>         <resultDef key="1" seq="1" enumKey="ruleTaskType">             <script><![CDATA[caseSumNew.setTaskType("@param");]]></script>         </resultDef>     </resultDefs> </rule>

2. Query the complete data of the rule set according to the rule type

3. Integrate the rule set data with the object after xml parsing, and assemble it into a drl script

4. Save the assembled script to the database rule set table

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 /** * Generate rule script * rule Basic rule information: including rule table field name definitions, etc. * def Business personnel specifically enter the conditions and results of the rule set and other data */ public String generateDRLScript(DroolsRuleEditBO rule, DroolsRuleTableBO def) {         //Parse the fact object mapping XML file, generate condition definition and result definition         RuleSetDef ruleSetDef = RuleSetDefHelper.getRuleSetDef(rule.getRuleTypeCode());            // 1. Declare a rule package         StringBuilder drl = new StringBuilder("package ").append(rule.getRuleTypeCode()).append(";\n\n");         HashMap<String, String> myEntityMap = Maps.newHashMap(); // k,v => caseSumNew,CaseSumNewWrapper         // 2. Import the entity corresponding to the execution class         ruleSetDef.getEntitys().forEach(d -> {             String cls = d.getCls();             drl.append("import ").append(cls).append(";\n\n");             myEntityMap.put(d.getAlias(), cls.substring(cls.lastIndexOf('.') + 1));         });         // 3. Rule script comments         drl.append("// ").append(rule.getRuleTypeCode()).append(" : ").append(rule.getRuleTypeName()).append("\n");         drl.append("// version : ").append(rule.getCode()).append("\n");         drl.append("// createTime : ").append(DateUtil.getSysDate(DateUtil.PATTERN_TIME_DEFAULT)).append("\n\n");            Map<String, String> myResultMap = def.getResultDefs().stream().collect(Collectors.toMap(DroolsRuleCondBO::getCondKey, DroolsRuleCondBO::getScript));            // 4. Write the rules         AtomicInteger maxRowSize = new AtomicInteger(0); // total number of rules         rule.getTables().forEach(table -> {             String tableCode = table.getTableCode();             table.getRows().stream().filter(r -> !Objects.equals(r.getStatus(), 3))            .forEach(row -> {                 // 3.1. Rule attribute and priority                 drl.append("// generated from row: ").append(row.getRowCode()).append("\n");                 //TODO needs to ensure that row.getRowSort() is not repeated, otherwise the same rule number will be generated                 drl.append("rule \"").append(rule.getRuleTypeCode()).append("_").append(tableCode).append("_TR_").append(row.getRowSort()).append("\"\n");  // pkg_tableCode_TR_rowSort                 drl.append("\tsalience ").append((maxRowSize.incrementAndGet())).append("\n");                    // 4.2. Condition judgment                 drl.append("\twhen\n");                 // One line for each entity, merged with multiple conditions                 // when=condEntityKey:cls(condKeyMethod colOperator.drlStr colValue), where cls=myEntityMap.value(key=condEntityKey)                 drl.append(                         row.getColumns()                                 .stream().collect(Collectors.groupingBy(d -> d.getCondition().getCondEntityKey()))                                 .entrySet().stream()                                 .map(entityType -> "\t\t" + entityType.getKey() + ":" + myEntityMap.get(entityType.getKey()) + "(" +                                         entityType.getValue().stream()                                                 .filter(col -> StringUtils.isNotBlank(col.getColValue())) // exclude invalid conditions                                                 .sorted(Comparator.comparing(col -> col.getCondition().getCondSort())) // to sort                                                 .map(col -> {                                                     String condKey = col.getCondition().getCondKey();                                                     String condKeyMethod = condKey.substring(condKey.indexOf('.') + 1);                                                     String[] exec = ParamTypeHelper.get(col.getColOperator()).getDrlStr(condKeyMethod, col.getColValue());                                                     if (exec.length > 0) {                                                         return Arrays.stream(exec).filter(StringUtils::isNotBlank).collect(Collectors.joining(" && "));                                                     }                                                     return null;                                                 })                                                 .collect(Collectors.joining(" && ")) + ")\n"                                 )                                 .collect(Collectors.joining()));                    // 4.3. Rule result                 drl.append("\tthen\n");                 row.getResults().forEach(r -> {                     String script = myResultMap.get(r.getResultKey());                     drl.append("\t\t").append(script.replace("@param", r.getResultValue())).append("\n"); // Replace @param with resultValue                 });                 drl.append("end\n\n");             });         });         return drl.toString();     }

3.2.3 Rule Execution

Core process:

 

1 2 3 4 5 6 7 8 9 //Core process code: KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(ResourceFactory.newByteArrayResource(script.getBytes(StandardCharsets.UTF_8)), ResourceType.DRL); //script is the rule script InternalKnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase(); KieSession ksession = base.newKieSession(); AgendaFilter filter = RuleConstant.DroolsRuleNameFilter.getFilter(ruleTypeCode);//get a filter kSession.insert(fact); kSession.fireAllRules(filter); kSession.dispose();
  1. Query the drl script from the ruleset table based on the rule type
  2. Add steps to KnowledgeBuilder to build a knowledge base
  3. Get the knowledge base InternalKnowledgeBase (corresponding to Kbase in Kmodule in the new version)
  4. Create a KieSession session link through InternalKnowledgeBase
  5. Create an AgendaFilter to formulate and execute a certain rule or rules
  6. Call the insert method to insert the fact object fact into the working memory
  7. Call the fireAllRules method to execute the rules
  8. Finally call dispose to close the connection

Four. Summary

This article mainly introduces the rule engine Drools from a case in the collection system, and then introduces the concept and usage of Drools and the principle of pattern matching Rete algorithm in detail. Finally, combined with the collection system, I explained how Drools is used in the collection system.

Through the introduction of the rules engine, developers no longer need to participate in the development and maintenance of rules, which greatly saves development costs. Through the visual decision table of the self-developed collection system, business personnel can flexibly configure maintenance rules in the system without writing complicated rule scripts every time, which solves the pain points of business personnel. The system is essentially a rule script for execution. Here we optimize the generation of the script. First, enter the rules through the visual page and store them as structured data, and then integrate and assemble them with the rule definition, and finally automatically generated by the system. Rule script.

There are still some problems with the rule engine in the current collection system, such as:

  1. The collection system is suitable for relatively simple rule logic by dynamically generating scripts. If you want to implement more complex rules, you need to write a lot of complicated code, and the maintenance cost is relatively high.
  2. Although the collection system uses the drools7.x version, the method of use still uses the 5.x programmatic builder method (Knowledge API)
  3. Currently, the collection system can only edit rules but cannot add rules on the fixed page of rules. Rules can only be added by initializing the database table.

In the future, we will continue to upgrade and optimize with the iteration of the version, thank you for reading.

Reference documents:

  1. Official documentation: Drools Documentation
  2. api documentation: KIE::Public API 6.5.0.Final API
Share vivo Internet technology dry goods and salon activities, recommend the latest industry trends and popular conferences.
 
Go to https://www.cnblogs.com/vivotech/p/16921060.html

Posted by shane18 on Thu, 01 Dec 2022 14:17:39 +1030