Drools basic introduction, introductory cases, basic grammar

catalogue

Classic requirements and scenarios

demand

Traditional practice -if

Traditional practice strategy

Question?

Rule engine

concept

origin

Principle -- rule engine based on rete algorithm

Rule engine application scenarios

Drools introduction

Consumption bonus points case

Step 1: create a project and import jar s

Create drools auto configuration class

Order entity class

Rule engine file

Client

Summary of drools development

drools composition

Description of relevant concepts

KIE introduction

Rule file development

Rule file composition

Regular grammatical structure

notes

Pattern pattern matching

Comparison operator

Execute specified rules

keyword

Classic requirements and scenarios

demand

Free points for shopping mall system consumption

No extra points for less than 100 yuan
100 yuan -500 yuan plus 100 points
500 yuan -1000 yuan plus 500 points
1000 points for more than 1000 yuan

Traditional practice -if

if (order.getAmout() <= 100){
    order.setScore(0);
    addScore(order);
}else if(order.getAmout() > 100 && order.getAmout() <= 500){
    order.setScore(100);
    addScore(order);
}else if(order.getAmout() > 500 && order.getAmout() <= 1000){
    order.setScore(500);
    addScore(order);
}else{
    order.setScore(1000);
    addScore(order);
}

Traditional practice strategy

interface Strategy {
    addScore(int num1,int num2);
}

class Strategy1 {
    addScore(int num1);
}
......................
interface StrategyN {
    addScore(int num1);
}

class Environment {
    private Strategy strategy;

    public Environment(Strategy strategy) {
        this.strategy = strategy;
    }

    public int addScore(int num1) {
        return strategy.addScore(num1);
    }
}

Question?

Thinking about the above solutions:

If the demand changes, the integral hierarchy increases, and the integral proportion is adjusted?

database

Problems encountered and bottlenecks:

First, we should simplify the if else structure and separate business logic from data!

Second, the separated business logic must be easy to write. At least writing these business logic alone is faster than writing code!

Third, the separated business logic must be easier to read than the original code!

Fourth, the separated business logic must be easier to maintain than the original. At least change these logic, and the application does not need to restart!

Rule engine

concept

The rules engine is powered by Inference engine Developed from, it is a component embedded in applications, which separates business decisions from application code, and uses predefined semantic modules to write business decisions. Accept data input, explain business rules, and make business decisions according to business rules

It should be noted that the rule engine is not a specific technical framework, but refers to a kind of system, namely business rule management system. At present, specific rule engine products on the market include drools, VisualRules, iLog, etc

In the IT business system of many enterprises, there are often a large number of business rule configurations, and these business rules will change with the change of enterprise managers' decisions. In order to meet such needs, our IT business system should be able to be updated quickly and at low cost. To meet such needs, the general practice is to take out the configuration of business rules separately to maintain low coupling with the business system. At present, programs that realize such functions have been developed into rule engines.

origin

Principle -- rule engine based on rete algorithm

principle

In the field of AI, production system is a very important theory. Production reasoning is divided into forward reasoning and reverse reasoning. The general form of its rules is: IF condition THEN operation. rete algorithm is an efficient pattern matching algorithm to realize forward reasoning in production system. By forming a rete network for pattern matching, it uses the characteristics of time redundancy and structural similarity of rule-based system to improve the efficiency of system pattern matching

Forward chaining and backward chaining

(1) Forward reasoning, also known as deductive method, is driven by facts, starting from an initial fact and constantly drawing conclusions from the application of rules. First, select a rule in the candidate queue as the enabling rule for reasoning, and record its conclusion as the evidence for the next reasoning. This process is repeated until no more rules are available or the required solution is obtained.

(2) Reverse reasoning, also known as induction, is goal driven. First, put forward a hypothesis, and then look for evidence to support the hypothesis. If the required evidence can be found, it shows that the original hypothesis is correct. If the required evidence can not be found anyway, it shows that the original hypothesis is not tenable, and a new hypothesis needs to be made at this time.

rete algorithm

Rete algorithm was originally described by Dr. Charles L.Forgy of Carnegie Mellon University in a paper published in 1974. This algorithm provides an efficient implementation of expert system. Since the Rete algorithm was proposed, it has been used in some large rule systems, such as ILog, Jess, JBoss Rules and so on, which are rule engines based on Rete algorithm.

Rete translates as "net" in Latin, that is, network. Rete matching algorithm is an efficient method to compare a large number of pattern sets and a large number of object sets. All objects and rules matching each pattern are found through network filtering.

Its core idea is to dynamically construct the matching tree according to the content of the separated matching items, so as to significantly reduce the amount of computation. Rete algorithm can be divided into two parts: rule compilation and rule execution. When Rete algorithm asserts facts, it includes three stages: match, select and execute, which is called match select Act cycle.

Rule engine application scenarios

Drools introduction

Drools has an open source business rule engine that is easy to access enterprise policies, easy to adjust and easy to manage. It conforms to industry standards, and is fast and efficient. Business analysts or reviewers can use it to easily view business rules to verify whether the encoded rules implement the required business rules. Its predecessor is an open source project of Codehaus called drools, which was later incorporated into JBoss, renamed JBoss Rules, and became the rule engine of JBoss application server.

Drools is divided into two main parts: Compilation and runtime. Compilation is to parse the rule description file according to ANTLR 3 syntax, check the correctness of the syntax, and then generate an intermediate structure "descr", which uses AST to describe rules. At present, drools supports four rule description files: drl file, xls file, brl file and dsl file. Among them, the commonly used description files are drl file and XLS file, while XLS file is easier to maintain, more intuitive and more understood by business personnel. At runtime, AST is transferred to PackageBuilder, and PackageBuilder generates RuleBase, which contains one or more Package objects.

Consumption bonus points case

Step 1: create a project and import jar s

Due to the current java development, springboot is commonly used. This course takes springboot as the basic framework for demonstration

jar dependencies. Note that spring related dependencies are excluded

<!-- Rule engine -->
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <version>${drools.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-tx</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Create drools auto configuration class

drools is used in the same way in spring or springboot, which is to create some bean s

package com.mashibing.drools.config;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;


/**
 * <p> Rule engine auto configuration class</p>
 * @author Sun Zhiqiang
 * @date 2019/9/10 11:20
 */
@Configuration
public class DroolsAutoConfiguration {

    private static final String RULES_PATH = "rules/";

    private KieServices getKieServices() {
        return KieServices.Factory.get();
    }

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();

        kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());

        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();

        KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());

        return kieContainer;
    }

    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }

}

Order entity class

@Data
@Accessors(chain = true)
public class Order {

    /**
     * Order original price amount
     */
    private int price;

    /**
     *Next person
     */
    private User user;

    /**
     *integral
     */
    private int score;

    /**
     * Order date 
     */
    private Date bookingDate;
} 

Rule engine file

package rules

import com.mashibing.drools.entity.Order

rule "zero"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout <= 100)
    then
        $s.setScore(0);
        update($s);
end

rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 100 && amout <= 500)
    then
        $s.setScore(100);
        update($s);
end

rule "add500"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 500 && amout <= 1000)
    then
        $s.setScore(500);
        update($s);
end

rule "add1000"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 1000)
    then
        $s.setScore(1000);
        update($s);
end

Client

/**
 * demand
 * The rules for calculating the amount of additional points are as follows: order original price amount
 * 100 Below, no extra points
 * 100-500 Add 100 points
 * 500-1000 Add 500 points
 * 1000 Add 1000 points above
 */
public class DroolsOrderTests extends DroolsApplicationTests {
    @Resource
    private KieContainer kieContainer;

    @Test
    public void Test() throws Exception {
        List<Order> orderList = getInitData();
        for (Order order : orderList) {
            if (order.getAmout() <= 100) {
                order.setScore(0);
                addScore(order);
            } else if (order.getAmout() > 100 && order.getAmout() <= 500) {
                order.setScore(100);
                addScore(order);
            } else if (order.getAmout() > 500 && order.getAmout() <= 1000) {
                order.setScore(500);
                addScore(order);
            } else {
                order.setScore(1000);
                addScore(order);
            }
        }
    }

    @Test
    public void droolsOrderTest() throws Exception {
        KieSession kieSession = kieContainer.newKieSession();
        List<Order> orderList = getInitData();
        for (Order order: orderList) {
            // 1-rule engine processing logic
            kieSession.insert(order);
            kieSession.fireAllRules();
            // 2 - after executing the rules, execute the relevant logic
            addScore(order);
        }
        kieSession.dispose();
    }



    private static void addScore(Order o){
        System.out.println("user" + o.getUser().getName() + "Enjoy extra points: " + o.getScore());
    }

    private static List<Order> getInitData() throws Exception {
        List<Order> orderList = new ArrayList<>();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        {
            Order order = new Order();
            order.setAmout(80);
            order.setBookingDate(df.parse("2015-07-01"));
            User user = new User();
            user.setLevel(1);
            user.setName("Name1");
            order.setUser(user);
            order.setScore(111);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setAmout(200);
            order.setBookingDate(df.parse("2015-07-02"));
            User user = new User();
            user.setLevel(2);
            user.setName("Name2");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setAmout(800);
            order.setBookingDate(df.parse("2015-07-03"));
            User user = new User();
            user.setLevel(3);
            user.setName("Name3");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setAmout(1500);
            order.setBookingDate(df.parse("2015-07-04"));
            User user = new User();
            user.setLevel(4);
            user.setName("Name4");
            order.setUser(user);
            orderList.add(order);
        }
        return orderList;
    }
}

Summary of drools development

drools composition

drools rule engine consists of the following parts:

Working Memory

Rules (rule base)

Facts

Production memory

Working memory:

Agenda

As shown in the following figure:

Description of relevant concepts

Working Memory: Working Memory. The drools rule engine will get data from Working Memory and match the pattern with the rules defined in the rule file, so the application we developed only needs to insert our data into Working Memory. For example, in this case, we call kieSession.insert(order) to insert the order object into Working Memory.

Fact: in fact, it means that in the application of drools rules, the object * * after inserting a * * ordinary JavaBean into Working Memory * * is a fact object. For example, the Order object in this case belongs to a fact object. Fact object is a bridge or channel for data interaction between our application and rule engine.

Rules: rule base. The rules defined in the rule file will be loaded into the rule base.

Pattern Matcher: a matcher, which matches all rules in the Rule Base with the Fact object in Working Memory. The rules that match successfully will be activated and put into the Agenda.

Agenda: agenda, which is used to store the rules activated after pattern matching through the matcher.

KIE introduction

In the above analysis of point exchange, some class names beginning with "kie" are simply used. The full name of kie is Knowledge Is Everything, that is, the abbreviation of "Knowledge Is Everything", which is the general name of a series of Jboss projects. Description on the official website: this name permeates the GitHub account and Maven POMs. With the expansion of the scope and the launch of new projects, kie (the abbreviation of Knowledge Is Everything) was selected as the new group name. The name of kie is also used in the sharing aspect of the system; Such as unified construction, deployment and use.

 

Rule file development

Rule file composition

When using Drools, a very important job is to write rule files. Usually, the suffix of rule files is.drl.

drl is the abbreviation of Drools Rule Language. Write specific rules in the rules file.

The contents of a complete set of rules are as follows:

 

The rule files supported by Drools are not only in drl form, but also in Excel file type.

Regular grammatical structure

The rule body is an important part of the content of the rule file. It is the part for judging business rules and processing business results.

The syntax structure of rule body is as follows:

rule "ruleName"
    attributes
    when
        LHS 
    then
        RHS
end

Rule: keyword, indicating the beginning of the rule. The parameter is the unique name of the rule.

attributes: rule attribute, which is an optional parameter between rule and when.

when: keyword, followed by the condition part of the rule.

LHS(Left Hand Side): it is the general name of the condition part of the rule. It consists of zero or more conditional elements. If LHS is empty, it will be treated as a condition element that is always true. (left hand side)

then: keyword, followed by the result part of the rule.

RHS(Right Hand Side): it is the common name of the consequence or action part of the rule. (right hand side)

End: keyword, indicating the end of a rule.

notes

Annotations used in drl form rule files are consistent with those used in Java classes, which are divided into single line annotations and multi line annotations.

Single line comments are marked with "/ /", and multi line comments start with "/ *" and end with "* /". The following example:

//The comment of rule rule1, which is a single line comment
rule "rule1"
    when
    then
        System.out.println("rule1 trigger");
end

/*
Comments for rule rule2,
This is a multiline comment
*/
rule "rule2"
    when
    then
        System.out.println("rule2 trigger");
end

Pattern pattern matching

As we have known previously, the matcher in Drools can match all rules in Rule Base with the Fact object in Working Memory, so we need to define rules and match patterns in the LHS part of the rule body. The LHS part consists of one or more conditions, which are also called patterns.

The syntax structure of pattern is: binding variable name: Object(Field constraint)

The bound variable name can be omitted. Generally, it is recommended to start with $. If the binding variable name is defined, you can use this binding variable name in the RHS part of the rule body to operate the corresponding Fact object. The Field constraint part is 0 or more expressions that need to return true or false.

For example, in our introductory case:

rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price > 100 && price <= 500)
    then
        $order.setScore(100);
        update($s);
end

From the above example, we can know that the matching conditions are:

1. There must be a Fact object of type Order in the working memory ----- type constraint

2. The price attribute value of the Fact object must be greater than 100 ------ attribute constraint

3. The price attribute value of the Fact object must be less than or equal to 500 ------ attribute constraint

The above conditions must meet the current rules at the same time before they can be activated.

Binding variables can be used on both objects and object properties. For example, the above example can be changed to:

rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order($price:price > 100 && amopriceut <= 500)
    then
        System.out.println("$price=" + $price);
        $s.setScore(100);
        update($s);
end

The LHS part can also define multiple patterns. Multiple patterns can be connected with and or without writing. The default connection is and.

rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price > 100 && price <= 500) and
        $user : User(level>3)
    then
        System.out.println($order.getUser());
        $order.setScore(100);
        update($order);
end

Comparison operator

The comparison operators provided by Drools are as follows:

grammar

contains | not contains syntax structure

Object(Field[Collection/Array] contains value)

Object(Field[Collection/Array] not contains value)

memberOf | not memberOf syntax structure

Object(field memberOf value[Collection/Array])

Object(field not memberOf value[Collection/Array])

matches | not matches syntax structure

Object(field matches "regular expression")

Object(field not matches "regular expression")

contain is the front including the back, and memberOf is the back including the front.

Operation steps

Step 1: create an entity class to test the comparison operator

package com.mashibing.drools.entity;

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.List;

/**
 * @author sunzhiqiang23
 * @date 2021-06-16 21:11
 */

@Data
@Accessors(chain = true)
public class ComparisonEntity {

    /**
     *Name set
     */
    private String names;

    /**
     * String collection
     */
    private List<String> list;

}

Step 2: create the rule file comparison.drl under /resources/rules

package rules
import com.mashibing.drools.entity.ComparisonEntity

/*
 Used to test the comparison operators provided by Drools
*/

//Test comparison operator contains
rule "rule_comparison_contains"
    when
        ComparisonEntity(names contains "Zhang San")
        ComparisonEntity(list contains names)
    then
        System.out.println("rule rule_comparison_contains trigger");
end

//Test comparison operator not contains
rule "rule_comparison_notContains"
    when
        ComparisonEntity(names not contains "Zhang San")
        ComparisonEntity(list not contains names)
    then
        System.out.println("rule rule_comparison_notContains trigger");
end

//Test comparison operator memberOf
rule "rule_comparison_memberOf"
    when
        ComparisonEntity(names memberOf list)
    then
        System.out.println("rule rule_comparison_memberOf trigger");
end

//Test comparison operator not memberOf
rule "rule_comparison_notMemberOf"
    when
        ComparisonEntity(names not memberOf list)
    then
        System.out.println("rule rule_comparison_notMemberOf trigger");
end

//Test comparison operator matches
rule "rule_comparison_matches"
    when
        ComparisonEntity(names matches "Zhang.*")
    then
        System.out.println("rule rule_comparison_matches trigger");
end

//Test comparison operator not matches
rule "rule_comparison_notMatches"
    when
        ComparisonEntity(names not matches "Zhang.*")
    then
        System.out.println("rule rule_comparison_notMatches trigger");
end

Step 3: write unit tests

package com.mashibing.drools.client;

import com.mashibing.drools.DroolsApplicationTests;
import com.mashibing.drools.entity.ComparisonEntity;
import org.junit.jupiter.api.Test;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author sunzhiqiang23
 * @date 2021-06-17 23:46
 */
public class ComparisonTest extends DroolsApplicationTests {

    @Resource
    public KieBase kieBase;

    @Test
    public void testComparison(){
        KieSession kieSession = kieBase.newKieSession();
        ComparisonEntity comparisonEntity = new ComparisonEntity();
        comparisonEntity.setNames("Zhang San");
        List<String> list = new ArrayList<>();
        list.add("Zhang San");
        list.add("Li Si");
        comparisonEntity.setList(list);

        kieSession.insert(comparisonEntity);

        kieSession.fireAllRules();
        kieSession.dispose();
    }
}

Execute specified rules

From the previous case, we can see that when we call the rule code, the rules that meet the conditions will be executed. So if we only want to implement one of these rules, how to implement it?

Drools provides us with a way to implement specified rules through rule filters. There is no need to modify the rule file, just modify the Java code, as follows:

//Only the specified rules are executed through the rule filter
kieSession.fireAllRules(new kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_filter_1"));

keyword

Drools' keywords are divided into hard keywords and soft keywords.

Hard keywords are clearly not used when we define the package name or rule name in the rule file, otherwise the program will report an error. Although soft keywords can be used, they are not recommended.

Hard keywords include: true false null

Soft keywords include: lock on active date effective date expires no loop auto focus activation group agenda group ruleflow group entry point duration package import dialog sales enabled attributes rule extend when then template query declare function global Eval not in or and exists forall aggregate collect from action reverse result end over init
 

For example:
rule true  //may not
rule "true" sure

Tags: SQL Java servlet

Posted by Dookster on Thu, 04 Aug 2022 22:13:04 +0530