Developing decision services in Red Hat Decision Manager
Abstract
Preface
As a developer of business decisions, you can use Red Hat Decision Manager to develop decision services using Decision Model and Notation (DMN) models, Drools Rule Language (DRL) rules, guided decision tables, and other rule-authoring assets.
Making open source more inclusive
Red Hat is committed to replacing problematic language in our code, documentation, and web properties. We are beginning with these four terms: master, slave, blacklist, and whitelist. Because of the enormity of this endeavor, these changes will be implemented gradually over several upcoming releases. For more details, see our CTO Chris Wright’s message.
Part I. Designing a decision service using DMN models
As a business analyst or business rules developer, you can use Decision Model and Notation (DMN) to model a decision service graphically. The decision requirements of a DMN decision model are determined by a decision requirements graph (DRG) that is depicted in one or more decision requirements diagrams (DRDs). A DRD can represent part or all of the overall DRG for the DMN model. DRDs trace business decisions from start to finish, with each decision node using logic defined in DMN boxed expressions such as decision tables.
Red Hat Decision Manager provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3, and design support for DMN 1.2 models at conformance level 3. You can design your DMN models directly in Business Central or with the Red Hat Decision Manager DMN modeler in VS Code, or import existing DMN models into your Red Hat Decision Manager projects for deployment and execution. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import into Business Central, open in the DMN designer, and save are converted to DMN 1.2 models.
For more information about DMN, see the Object Management Group (OMG) Decision Model and Notation specification.
For a step-by-step tutorial with an example DMN decision service, see Getting started with decision services.
Chapter 1. Decision-authoring assets in Red Hat Decision Manager
Red Hat Decision Manager supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.
The following table highlights the main decision-authoring assets supported in Red Hat Decision Manager projects to help you decide or confirm the best method for defining decisions in your decision service.
Asset | Highlights | Authoring tools | Documentation |
---|---|---|---|
Decision Model and Notation (DMN) models |
| Business Central or other DMN-compliant editor | |
Guided decision tables |
| Business Central | |
Spreadsheet decision tables |
| Spreadsheet editor | Designing a decision service using spreadsheet decision tables |
Guided rules |
| Business Central | |
Guided rule templates |
| Business Central | |
DRL rules |
| Business Central or integrated development environment (IDE) | |
Predictive Model Markup Language (PMML) models |
| PMML or XML editor |
When you define business decisions, you can also consider using Red Hat build of Kogito for your cloud-native decision services. For more information about getting started with Red Hat build of Kogito microservices, see Getting started with Red Hat build of Kogito in Red Hat Decision Manager.
Chapter 2. Red Hat Decision Manager BPMN and DMN modelers
Red Hat Decision Manager provides the following extensions or applications that you can use to design Business Process Model and Notation (BPMN) process models and Decision Model and Notation (DMN) decision models using graphical modelers.
Business Central: Enables you to view and design BPMN models, DMN models, and test scenario files in a related embedded designer.
To use Business Central, you can set up a development environment containing a Business Central to design business rules and processes, and a KIE Server to execute and test the created business rules and processes.
Red Hat Decision Manager VS Code extension: Enables you to view and design BPMN models, DMN models, and test scenario files in Visual Studio Code (VS Code). The VS Code extension requires VS Code 1.46.0 or later.
To install the Red Hat Decision Manager VS Code extension, select the Extensions menu option in VS Code and search for and install the Red Hat Business Automation Bundle extension.
-
Standalone BPMN and DMN editors: Enable you to view and design BPMN and DMN models embedded in your web applications. To download the necessary files, you can either use the NPM artifacts from the NPM registry or download the JavaScript files directly for the DMN standalone editor library at
https://<YOUR_PAGE>/dmn/index.js
and for the BPMN standalone editor library athttps://<YOUR_PAGE>/bpmn/index.js
.
2.1. Installing the Red Hat Decision Manager VS Code extension bundle
Red Hat Decision Manager provides a Red Hat Business Automation Bundle VS Code extension that enables you to design Decision Model and Notation (DMN) decision models, Business Process Model and Notation (BPMN) 2.0 business processes, and test scenarios directly in VS Code. VS Code is the preferred integrated development environment (IDE) for developing new business applications. Red Hat Decision Manager also provides individual DMN Editor and BPMN Editor VS Code extensions for DMN or BPMN support only, if needed.
The editors in the VS Code are partially compatible with the editors in the Business Central, and several Business Central features are not supported in the VS Code.
Prerequisites
- The latest stable version of VS Code is installed.
Procedure
In your VS Code IDE, select the Extensions menu option and search for Red Hat Business Automation Bundle for DMN, BPMN, and test scenario file support.
For DMN or BPMN file support only, you can also search for the individual DMN Editor or BPMN Editor extensions.
- When the Red Hat Business Automation Bundle extension appears in VS Code, select it and click Install.
- For optimal VS Code editor behavior, after the extension installation is complete, reload or close and re-launch your instance of VS Code.
After you install the VS Code extension bundle, any .dmn
, .bpmn
, or .bpmn2
files that you open or create in VS Code are automatically displayed as graphical models. Additionally, any .scesim
files that you open or create are automatically displayed as tabular test scenario models for testing the functionality of your business decisions.
If the DMN, BPMN, or test scenario modelers open only the XML source of a DMN, BPMN, or test scenario file and displays an error message, review the reported errors and the model file to ensure that all elements are correctly defined.
For new DMN or BPMN models, you can also enter dmn.new
or bpmn.new
in a web browser to design your DMN or BPMN model in the online modeler. When you finish creating your model, you can click Download in the online modeler page to import your DMN or BPMN file into your Red Hat Decision Manager project in VS Code.
2.2. Configuring the Red Hat Decision Manager standalone editors
Red Hat Decision Manager provides standalone editors that are distributed in a self-contained library providing an all-in-one JavaScript file for each editor. The JavaScript file uses a comprehensive API to set and control the editor.
You can install the standalone editors using the following methods:
- Download each JavaScript file manually
- Use the NPM package
Procedure
Install the standalone editors using one of the following methods:
Download each JavaScript file manually: For this method, follow these steps:
- Download the JavaScript files.
- Add the downloaded Javascript files to your hosted application.
Add the following
<script>
tag to your HTML page:Script tag for your HTML page for the DMN editor
<script src="https://<YOUR_PAGE>/dmn/index.js"></script>
Script tag for your HTML page for the BPMN editor
<script src="https://<YOUR_PAGE>/bpmn/index.js"></script>
Use the NPM package: For this method, follow these steps:
Add the NPM package to your
package.json
file:Adding the NPM package
npm install @kie-tools/kie-editors-standalone
Import each editor library to your
TypeScript
file:Importing each editor
import * as DmnEditor from "@kie-tools/kie-editors-standalone/dist/dmn" import * as BpmnEditor from "@kie-tools/kie-editors-standalone/dist/bpmn"
After you install the standalone editors, open the required editor by using the provided editor API, as shown in the following example for opening a DMN editor. The API is the same for each editor.
Opening the DMN standalone editor
const editor = DmnEditor.open({ container: document.getElementById("dmn-editor-container"), initialContent: Promise.resolve(""), readOnly: false, origin: "", resources: new Map([ [ "MyIncludedModel.dmn", { contentType: "text", content: Promise.resolve("") } ] ]) });
Use the following parameters with the editor API:
Table 2.1. Example parameters Parameter Description container
HTML element in which the editor is appended.
initialContent
Promise to a DMN model content. This parameter can be empty, as shown in the following examples:
-
Promise.resolve("")
-
Promise.resolve("<DIAGRAM_CONTENT_DIRECTLY_HERE>")
-
fetch("MyDmnModel.dmn").then(content ⇒ content.text())
readOnly
(Optional)Enables you to allow changes in the editor. Set to
false
(default) to allow content editing andtrue
for read-only mode in editor.origin
(Optional)Origin of the repository. The default value is
window.location.origin
.resources
(Optional)Map of resources for the editor. For example, this parameter is used to provide included models for the DMN editor or work item definitions for the BPMN editor. Each entry in the map contains a resource name and an object that consists of
content-type
(text
orbinary
) andcontent
(similar to theinitialContent
parameter).The returned object contains the methods that are required to manipulate the editor.
Table 2.2. Returned object methods Method Description getContent(): Promise<string>
Returns a promise containing the editor content.
setContent(path: string, content: string): void
Sets the content of the editor.
getPreview(): Promise<string>
Returns a promise containing an SVG string of the current diagram.
subscribeToContentChanges(callback: (isDirty: boolean) ⇒ void): (isDirty: boolean) ⇒ void
Sets a callback to be called when the content changes in the editor and returns the same callback to be used for unsubscription.
unsubscribeToContentChanges(callback: (isDirty: boolean) ⇒ void): void
Unsubscribes the passed callback when the content changes in the editor.
markAsSaved(): void
Resets the editor state that indicates that the content in the editor is saved. Also, it activates the subscribed callbacks related to content change.
undo(): void
Undoes the last change in the editor. Also, it activates the subscribed callbacks related to content change.
redo(): void
Redoes the last undone change in the editor. Also, it activates the subscribed callbacks related to content change.
close(): void
Closes the editor.
getElementPosition(selector: string): Promise<Rect>
Provides an alternative to extend the standard query selector when an element lives inside a canvas or a video component. The
selector
parameter must follow the<PROVIDER>:::<SELECT>
format, such asCanvas:::MySquare
orVideo:::PresenterHand
. This method returns aRect
representing the element position.envelopeApi: MessageBusClientApi<KogitoEditorEnvelopeApi>
This is an advanced editor API. For more information about advanced editor API, see MessageBusClientApi and KogitoEditorEnvelopeApi.
-
Chapter 3. Creating and executing DMN and BPMN models using Maven
You can use Maven archetypes to develop DMN and BPMN models in VS Code using the Red Hat Decision Manager VS Code extension instead of Business Central. You can then integrate your archetypes with your Red Hat Decision Manager decision and process services in Business Central as needed. This method of developing DMN and BPMN models is helpful for building new business applications using the Red Hat Decision Manager VS Code extension.
Procedure
- In a command terminal, navigate to a local folder where you want to store the new Red Hat Decision Manager project.
Enter the following command to use a Maven archtype to generate a project within a defined folder:
Generating a project using Maven archetype
mvn archetype:generate \ -DarchetypeGroupId=org.kie \ -DarchetypeArtifactId=kie-kjar-archetype \ -DarchetypeVersion=7.67.0.Final-redhat-00024
This command generates a Maven project with required dependencies and generates required directories and files to build your business application. You can use the Git version control system (recommended) when developing a project.
If you want to generate multiple projects in the same directory, specify the
artifactId
andgroupId
of the generated business application by adding-DgroupId=<groupid> -DartifactId=<artifactId>
to the previous command.- In your VS Code IDE, click File, select Open Folder, and navigate to the folder that is generated using the previous command.
Before creating the first asset, set a package for your business application, for example,
org.kie.businessapp
, and create respective directories in the following paths:-
PROJECT_HOME/src/main/java
-
PROJECT_HOME/src/main/resources
-
PROJECT_HOME/src/test/resources
For example, you can create
PROJECT_HOME/src/main/java/org/kie/businessapp
fororg.kie.businessapp
package.-
Use VS Code to create assets for your business application. You can create the assets supported by Red Hat Decision Manager VS Code extension using the following ways:
-
To create a business process, create a new file with
.bpmn
or.bpmn2
inPROJECT_HOME/src/main/resources/org/kie/businessapp
directory, such asProcess.bpmn
. -
To create a DMN model, create a new file with
.dmn
inPROJECT_HOME/src/main/resources/org/kie/businessapp
directory, such asAgeDecision.dmn
. -
To create a test scenario simulation model, create a new file with
.scesim
inPROJECT_HOME/src/test/resources/org/kie/businessapp
directory, such asTestAgeScenario.scesim
.
-
To create a business process, create a new file with
After you create the assets in your Maven archetype, navigate to the root directory (contains
pom.xml
) of the project in the command line and run the following command to build the knowledge JAR (KJAR) of your project:mvn clean install
If the build fails, address any problems described in the command line error messages and try again to validate the project until the build is successful. However, if the build is successful, you can find the artifact of your business application in
PROJECT_HOME/target
directory.NoteUse
mvn clean install
command often to validate your project after each major change during development.
You can deploy the generated knowledge JAR (KJAR) of your business application on a running KIE Server using the REST API. For more information about using REST API, see Interacting with Red Hat Decision Manager using KIE APIs.
Chapter 4. Decision Model and Notation (DMN)
Decision Model and Notation (DMN) is a standard established by the Object Management Group (OMG) for describing and modeling operational decisions. DMN defines an XML schema that enables DMN models to be shared between DMN-compliant platforms and across organizations so that business analysts and business rules developers can collaborate in designing and implementing DMN decision services. The DMN standard is similar to and can be used together with the Business Process Model and Notation (BPMN) standard for designing and modeling business processes.
For more information about the background and applications of DMN, see the OMG Decision Model and Notation specification.
4.1. DMN conformance levels
The DMN specification defines three incremental levels of conformance in a software implementation. A product that claims compliance at one level must also be compliant with any preceding levels. For example, a conformance level 3 implementation must also include the supported components in conformance levels 1 and 2. For the formal definitions of each conformance level, see the OMG Decision Model and Notation specification.
The following list summarizes the three DMN conformance levels:
- Conformance level 1
- A DMN conformance level 1 implementation supports decision requirement diagrams (DRDs), decision logic, and decision tables, but decision models are not executable. Any language can be used to define the expressions, including natural, unstructured languages.
- Conformance level 2
- A DMN conformance level 2 implementation includes the requirements in conformance level 1, and supports Simplified Friendly Enough Expression Language (S-FEEL) expressions and fully executable decision models.
- Conformance level 3
- A DMN conformance level 3 implementation includes the requirements in conformance levels 1 and 2, and supports Friendly Enough Expression Language (FEEL) expressions, the full set of boxed expressions, and fully executable decision models.
Red Hat Decision Manager provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3, and design support for DMN 1.2 models at conformance level 3. You can design your DMN models directly in Business Central or with the Red Hat Decision Manager DMN modeler in VS Code, or import existing DMN models into your Red Hat Decision Manager projects for deployment and execution. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import into Business Central, open in the DMN designer, and save are converted to DMN 1.2 models.
4.2. DMN decision requirements diagram (DRD) components
A decision requirements diagram (DRD) is a visual representation of your DMN model. A DRD can represent part or all of the overall decision requirements graph (DRG) for the DMN model. DRDs trace business decisions using decision nodes, business knowledge models, sources of business knowledge, input data, and decision services.
The following table summarizes the components in a DRD:
Component | Description | Notation | |
---|---|---|---|
Elements | Decision | Node where one or more input elements determine an output based on defined decision logic. | |
Business knowledge model | Reusable function with one or more decision elements. Decisions that have the same logic but depend on different sub-input data or sub-decisions use business knowledge models to determine which procedure to follow. | ||
Knowledge source | External authorities, documents, committees, or policies that regulate a decision or business knowledge model. Knowledge sources are references to real-world factors rather than executable business rules. | ||
Input data | Information used in a decision node or a business knowledge model. Input data usually includes business-level concepts or objects relevant to the business, such as loan applicant data used in a lending strategy. | ||
Decision service | Top-level decision containing a set of reusable decisions published as a service for invocation. A decision service can be invoked from an external application or a BPMN business process. | ||
Requirement connectors | Information requirement | Connection from an input data node or decision node to another decision node that requires the information. | |
Knowledge requirement | Connection from a business knowledge model to a decision node or to another business knowledge model that invokes the decision logic. | ||
Authority requirement | Connection from an input data node or a decision node to a dependent knowledge source or from a knowledge source to a decision node, business knowledge model, or another knowledge source. | ||
Artifacts | Text annotation | Explanatory note associated with an input data node, decision node, business knowledge model, or knowledge source. | |
Association | Connection from an input data node, decision node, business knowledge model, or knowledge source to a text annotation. |
The following table summarizes the permitted connectors between DRD elements:
Starts from | Connects to | Connection type | Example |
---|---|---|---|
Decision | Decision | Information requirement | |
Business knowledge model | Decision | Knowledge requirement | |
Business knowledge model | |||
Decision service | Decision | Knowledge requirement | |
Business knowledge model | |||
Input data | Decision | Information requirement | |
Knowledge source | Authority requirement | ||
Knowledge source | Decision | Authority requirement | |
Business knowledge model | |||
Knowledge source | |||
Decision | Text annotation | Association | |
Business knowledge model | |||
Knowledge source | |||
Input data |
The following example DRD illustrates some of these DMN components in practice:
Figure 4.1. Example DRD: Loan prequalification
The following example DRD illustrates DMN components that are part of a reusable decision service:
Figure 4.2. Example DRD: Phone call handling as a decision service
In a DMN decision service node, the decision nodes in the bottom segment incorporate input data from outside of the decision service to arrive at a final decision in the top segment of the decision service node. The resulting top-level decisions from the decision service are then implemented in any subsequent decisions or business knowledge requirements of the DMN model. You can reuse DMN decision services in other DMN models to apply the same decision logic with different input data and different outgoing connections.
4.3. Rule expressions in FEEL
Friendly Enough Expression Language (FEEL) is an expression language defined by the Object Management Group (OMG) DMN specification. FEEL expressions define the logic of a decision in a DMN model. FEEL is designed to facilitate both decision modeling and execution by assigning semantics to the decision model constructs. FEEL expressions in decision requirements diagrams (DRDs) occupy table cells in boxed expressions for decision nodes and business knowledge models.
For more information about FEEL in DMN, see the OMG Decision Model and Notation specification.
4.3.1. Data types in FEEL
Friendly Enough Expression Language (FEEL) supports the following data types:
- Numbers
- Strings
- Boolean values
- Dates
- Time
- Date and time
- Days and time duration
- Years and months duration
- Functions
- Contexts
- Ranges (or intervals)
- Lists
The DMN specification currently does not provide an explicit way of declaring a variable as a function
, context
, range
, or list
, but Red Hat Decision Manager extends the DMN built-in types to support variables of these types.
The following list describes each data type:
- Numbers
Numbers in FEEL are based on the IEEE 754-2008 Decimal 128 format, with 34 digits of precision. Internally, numbers are represented in Java as
BigDecimals
withMathContext DECIMAL128
. FEEL supports only one number data type, so the same type is used to represent both integers and floating point numbers.FEEL numbers use a dot (
.
) as a decimal separator. FEEL does not support-INF
,+INF
, orNaN
. FEEL usesnull
to represent invalid numbers.Red Hat Decision Manager extends the DMN specification and supports additional number notations:
-
Scientific: You can use scientific notation with the suffix
e<exp>
orE<exp>
. For example,1.2e3
is the same as writing the expression1.2*10**3
, but is a literal instead of an expression. -
Hexadecimal: You can use hexadecimal numbers with the prefix
0x
. For example,0xff
is the same as the decimal number255
. Both uppercase and lowercase letters are supported. For example,0XFF
is the same as0xff
. -
Type suffixes: You can use the type suffixes
f
,F
,d
,D
,l
, andL
. These suffixes are ignored.
-
Scientific: You can use scientific notation with the suffix
- Strings
Strings in FEEL are any sequence of characters delimited by double quotation marks.
Example
"John Doe"
- Boolean values
-
FEEL uses three-valued boolean logic, so a boolean logic expression may have values
true
,false
, ornull
. - Dates
Date literals are not supported in FEEL, but you can use the built-in
date()
function to construct date values. Date strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is"YYYY-MM-DD"
whereYYYY
is the year with four digits,MM
is the number of the month with two digits, andDD
is the number of the day.Example:
date( "2017-06-23" )
Date objects have time equal to
"00:00:00"
, which is midnight. The dates are considered to be local, without a timezone.- Time
Time literals are not supported in FEEL, but you can use the built-in
time()
function to construct time values. Time strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is"hh:mm:ss[.uuu][(+-)hh:mm]"
wherehh
is the hour of the day (from00
to23
),mm
is the minutes in the hour, andss
is the number of seconds in the minute. Optionally, the string may define the number of milliseconds (uuu
) within the second and contain a positive (+
) or negative (-
) offset from UTC time to define its timezone. Instead of using an offset, you can use the letterz
to represent the UTC time, which is the same as an offset of-00:00
. If no offset is defined, the time is considered to be local.Examples:
time( "04:25:12" ) time( "14:10:00+02:00" ) time( "22:35:40.345-05:00" ) time( "15:00:30z" )
Time values that define an offset or a timezone cannot be compared to local times that do not define an offset or a timezone.
- Date and time
Date and time literals are not supported in FEEL, but you can use the built-in
date and time()
function to construct date and time values. Date and time strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is"<date>T<time>"
, where<date>
and<time>
follow the prescribed XML schema formatting, conjoined byT
.Examples:
date and time( "2017-10-22T23:59:00" ) date and time( "2017-06-13T14:10:00+02:00" ) date and time( "2017-02-05T22:35:40.345-05:00" ) date and time( "2017-06-13T15:00:30z" )
Date and time values that define an offset or a timezone cannot be compared to local date and time values that do not define an offset or a timezone.
ImportantIf your implementation of the DMN specification does not support spaces in the XML schema, use the keyword
dateTime
as a synonym ofdate and time
.- Days and time duration
Days and time duration literals are not supported in FEEL, but you can use the built-in
duration()
function to construct days and time duration values. Days and time duration strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document, but are restricted to only days, hours, minutes and seconds. Months and years are not supported.Examples:
duration( "P1DT23H12M30S" ) duration( "P23D" ) duration( "PT12H" ) duration( "PT35M" )
ImportantIf your implementation of the DMN specification does not support spaces in the XML schema, use the keyword
dayTimeDuration
as a synonym ofdays and time duration
.- Years and months duration
Years and months duration literals are not supported in FEEL, but you can use the built-in
duration()
function to construct days and time duration values. Years and months duration strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document, but are restricted to only years and months. Days, hours, minutes, or seconds are not supported.Examples:
duration( "P3Y5M" ) duration( "P2Y" ) duration( "P10M" ) duration( "P25M" )
ImportantIf your implementation of the DMN specification does not support spaces in the XML schema, use the keyword
yearMonthDuration
as a synonym ofyears and months duration
.- Functions
FEEL has
function
literals (or anonymous functions) that you can use to create functions. The DMN specification currently does not provide an explicit way of declaring a variable as afunction
, but Red Hat Decision Manager extends the DMN built-in types to support variables of functions.Example:
function(a, b) a + b
In this example, the FEEL expression creates a function that adds the parameters
a
andb
and returns the result.- Contexts
FEEL has
context
literals that you can use to create contexts. Acontext
in FEEL is a list of key and value pairs, similar to maps in languages like Java. The DMN specification currently does not provide an explicit way of declaring a variable as acontext
, but Red Hat Decision Manager extends the DMN built-in types to support variables of contexts.Example:
{ x : 5, y : 3 }
In this example, the expression creates a context with two entries,
x
andy
, representing a coordinate in a chart.In DMN 1.2, another way to create contexts is to create an item definition that contains the list of keys as attributes, and then declare the variable as having that item definition type.
The Red Hat Decision Manager DMN API supports DMN
ItemDefinition
structural types in aDMNContext
represented in two ways:-
User-defined Java type: Must be a valid JavaBeans object defining properties and getters for each of the components in the DMN
ItemDefinition
. If necessary, you can also use the@FEELProperty
annotation for those getters representing a component name which would result in an invalid Java identifier. -
java.util.Map
interface: The map needs to define the appropriate entries, with the keys corresponding to the component name in the DMNItemDefinition
.
-
User-defined Java type: Must be a valid JavaBeans object defining properties and getters for each of the components in the DMN
- Ranges (or intervals)
FEEL has
range
literals that you can use to create ranges or intervals. Arange
in FEEL is a value that defines a lower and an upper bound, where either can be open or closed. The DMN specification currently does not provide an explicit way of declaring a variable as arange
, but Red Hat Decision Manager extends the DMN built-in types to support variables of ranges.The syntax of a range is defined in the following formats:
range := interval_start endpoint '..' endpoint interval_end interval_start := open_start | closed_start open_start := '(' | ']' closed_start := '[' interval_end := open_end | closed_end open_end := ')' | '[' closed_end := ']' endpoint := expression
The expression for the endpoint must return a comparable value, and the lower bound endpoint must be lower than the upper bound endpoint.
For example, the following literal expression defines an interval between
1
and10
, including the boundaries (a closed interval on both endpoints):[ 1 .. 10 ]
The following literal expression defines an interval between 1 hour and 12 hours, including the lower boundary (a closed interval), but excluding the upper boundary (an open interval):
[ duration("PT1H") .. duration("PT12H") )
You can use ranges in decision tables to test for ranges of values, or use ranges in simple literal expressions. For example, the following literal expression returns
true
if the value of a variablex
is between0
and100
:x in [ 1 .. 100 ]
- Lists
FEEL has
list
literals that you can use to create lists of items. Alist
in FEEL is represented by a comma-separated list of values enclosed in square brackets. The DMN specification currently does not provide an explicit way of declaring a variable as alist
, but Red Hat Decision Manager extends the DMN built-in types to support variables of lists.Example:
[ 2, 3, 4, 5 ]
All lists in FEEL contain elements of the same type and are immutable. Elements in a list can be accessed by index, where the first element is
1
. Negative indexes can access elements starting from the end of the list so that-1
is the last element.For example, the following expression returns the second element of a list
x
:x[2]
The following expression returns the second-to-last element of a list
x
:x[-2]
Elements in a list can also be counted by the function
count
, which uses the list of elements as the parameter.For example, the following expression returns
4
:count([ 2, 3, 4, 5 ])
4.3.2. Built-in functions in FEEL
To promote interoperability with other platforms and systems, Friendly Enough Expression Language (FEEL) includes a library of built-in functions. The built-in FEEL functions are implemented in the Drools Decision Model and Notation (DMN) engine so that you can use the functions in your DMN decision services.
The following sections describe each built-in FEEL function, listed in the format NAME( PARAMETERS )
. For more information about FEEL functions in DMN, see the OMG Decision Model and Notation specification.
4.3.2.1. Conversion functions
The following functions support conversion between values of different types. Some of these functions use specific string formats, such as the following examples:
-
date string
: Follows the format defined in the XML Schema Part 2: Datatypes document, such as2020-06-01
time string
: Follows one of the following formats:-
Format defined in the XML Schema Part 2: Datatypes document, such as
23:59:00z
-
Format for a local time defined by ISO 8601 followed by
@
and an IANA Timezone, such as00:01:00@Etc/UTC
-
Format defined in the XML Schema Part 2: Datatypes document, such as
-
date time string
: Follows the format of adate string
followed byT
and atime string
, such as2012-12-25T11:00:00Z
-
duration string
: Follows the format ofdays and time duration
andyears and months duration
defined in the XQuery 1.0 and XPath 2.0 Data Model, such asP1Y2M
- date( from ) - using date
Converts
from
to adate
value.Table 4.3. Parameters Parameter Type Format from
string
date string
Example
date( "2012-12-25" ) - date( "2012-12-24" ) = duration( "P1D" )
- date( from ) - using date and time
Converts
from
to adate
value and sets time components to null.Table 4.4. Parameters Parameter Type from
date and time
Example
date(date and time( "2012-12-25T11:00:00Z" )) = date( "2012-12-25" )
- date( year, month, day )
Produces a
date
from the specified year, month, and day values.Table 4.5. Parameters Parameter Type year
number
month
number
day
number
Example
date( 2012, 12, 25 ) = date( "2012-12-25" )
- date and time( date, time )
Produces a
date and time
from the specified date and ignores any time components and the specified time.Table 4.6. Parameters Parameter Type date
date
ordate and time
time
time
Example
date and time ( "2012-12-24T23:59:00" ) = date and time(date( "2012-12-24" ), time( "23:59:00" ))
- date and time( from )
Produces a
date and time
from the specified string.Table 4.7. Parameters Parameter Type Format from
string
date time string
Example
date and time( "2012-12-24T23:59:00" ) + duration( "PT1M" ) = date and time( "2012-12-25T00:00:00" )
- time( from )
Produces a
time
from the specified string.Table 4.8. Parameters Parameter Type Format from
string
time string
Example
time( "23:59:00z" ) + duration( "PT2M" ) = time( "00:01:00@Etc/UTC" )
- time( from )
Produces a
time
from the specified parameter and ignores any date components.Table 4.9. Parameters Parameter Type from
time
ordate and time
Example
time(date and time( "2012-12-25T11:00:00Z" )) = time( "11:00:00Z" )
- time( hour, minute, second, offset? )
Produces a
time
from the specified hour, minute, and second component values.Table 4.10. Parameters Parameter Type hour
number
minute
number
second
number
offset
(Optional)days and time duration
or nullExample
time( "23:59:00z" ) = time(23, 59, 0, duration( "PT0H" ))
- number( from, grouping separator, decimal separator )
Converts
from
to anumber
using the specified separators.Table 4.11. Parameters Parameter Type from
string
representing a valid numbergrouping separator
Space ( ), comma (
,
), period (.
), or nulldecimal separator
Same types as
grouping separator
, but the values cannot matchExample
number( "1 000,0", " ", "," ) = number( "1,000.0", ",", "." )
- string( from )
Provides a string representation of the specified parameter.
Table 4.12. Parameters Parameter Type from
Non-null value
Examples
string( 1.1 ) = "1.1" string( null ) = null
- duration( from )
Converts
from
to adays and time duration
value oryears and months duration
value.Table 4.13. Parameters Parameter Type Format from
string
duration string
Examples
date and time( "2012-12-24T23:59:00" ) - date and time( "2012-12-22T03:45:00" ) = duration( "P2DT20H14M" ) duration( "P2Y2M" ) = duration( "P26M" )
- years and months duration( from, to )
Calculates the
years and months duration
between the two specified parameters.Table 4.14. Parameters Parameter Type from
date
ordate and time
to
date
ordate and time
Example
years and months duration( date( "2011-12-22" ), date( "2013-08-24" ) ) = duration( "P1Y8M" )
4.3.2.2. Boolean functions
The following functions support Boolean operations.
- not( negand )
Performs the logical negation of the
negand
operand.Table 4.15. Parameters Parameter Type negand
boolean
Examples
not( true ) = false not( null ) = null
4.3.2.3. String functions
The following functions support string operations.
In FEEL, Unicode characters are counted based on their code points.
- substring( string, start position, length? )
Returns the substring from the start position for the specified length. The first character is at position value
1
.Table 4.16. Parameters Parameter Type string
string
start position
number
length
(Optional)number
Examples
substring( "testing",3 ) = "sting" substring( "testing",3,3 ) = "sti" substring( "testing", -2, 1 ) = "n" substring( "\U01F40Eab", 2 ) = "ab"
NoteIn FEEL, the string literal
"\U01F40Eab"
is the🐎ab
string (horse symbol followed bya
andb
).
- string length( string )
Calculates the length of the specified string.
Table 4.17. Parameters Parameter Type string
string
Examples
string length( "tes" ) = 3 string length( "\U01F40Eab" ) = 3
- upper case( string )
Produces an uppercase version of the specified string.
Table 4.18. Parameters Parameter Type string
string
Example
upper case( "aBc4" ) = "ABC4"
- lower case( string )
Produces a lowercase version of the specified string.
Table 4.19. Parameters Parameter Type string
string
Example
lower case( "aBc4" ) = "abc4"
- substring before( string, match )
Calculates the substring before the match.
Table 4.20. Parameters Parameter Type string
string
match
string
Examples
substring before( "testing", "ing" ) = "test" substring before( "testing", "xyz" ) = ""
- substring after( string, match )
Calculates the substring after the match.
Table 4.21. Parameters Parameter Type string
string
match
string
Examples
substring after( "testing", "test" ) = "ing" substring after( "", "a" ) = ""
- replace( input, pattern, replacement, flags? )
Calculates the regular expression replacement.
Table 4.22. Parameters Parameter Type input
string
pattern
string
replacement
string
flags
(Optional)string
NoteThis function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Example
replace( "abcd", "(ab)|(a)", "[1=$1][2=$2]" ) = "[1=ab][2=]cd"
- contains( string, match )
Returns
true
if the string contains the match.Table 4.23. Parameters Parameter Type string
string
match
string
Example
contains( "testing", "to" ) = false
- starts with( string, match )
Returns
true
if the string starts with the matchTable 4.24. Parameters Parameter Type string
string
match
string
Example
starts with( "testing", "te" ) = true
- ends with( string, match )
Returns
true
if the string ends with the match.Table 4.25. Parameters Parameter Type string
string
match
string
Example
ends with( "testing", "g" ) = true
- matches( input, pattern, flags? )
Returns
true
if the input matches the regular expression.Table 4.26. Parameters Parameter Type input
string
pattern
string
flags
(Optional)string
NoteThis function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Example
matches( "teeesting", "^te*sting" ) = true
- split( string, delimiter )
Returns a list of the original string and splits it at the delimiter regular expression pattern.
Table 4.27. Parameters Parameter Type string
string
delimiter
string
for a regular expression patternNoteThis function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Examples
split( "John Doe", "\\s" ) = ["John", "Doe"] split( "a;b;c;;", ";" ) = ["a","b","c","",""]
4.3.2.4. List functions
The following functions support list operations.
In FEEL, the index of the first element in a list is 1
. The index of the last element in a list can be identified as -1
.
- list contains( list, element )
Returns
true
if the list contains the element.Table 4.28. Parameters Parameter Type list
list
element
Any type, including null
Example
list contains( [1,2,3], 2 ) = true
- count( list )
Counts the elements in the list.
Table 4.29. Parameters Parameter Type list
list
Examples
count( [1,2,3] ) = 3 count( [] ) = 0 count( [1,[2,3]] ) = 2
- min( list )
Returns the minimum comparable element in the list.
Table 4.30. Parameters Parameter Type list
list
Alternative signature
min( e1, e2, ..., eN )
Examples
min( [1,2,3] ) = 1 min( 1 ) = 1 min( [1] ) = 1
- max( list )
Returns the maximum comparable element in the list.
Table 4.31. Parameters Parameter Type list
list
Alternative signature
max( e1, e2, ..., eN )
Examples
max( 1,2,3 ) = 3 max( [] ) = null
- sum( list )
Returns the sum of the numbers in the list.
Table 4.32. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
sum( n1, n2, ..., nN )
Examples
sum( [1,2,3] ) = 6 sum( 1,2,3 ) = 6 sum( 1 ) = 1 sum( [] ) = null
- mean( list )
Calculates the average (arithmetic mean) of the elements in the list.
Table 4.33. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
mean( n1, n2, ..., nN )
Examples
mean( [1,2,3] ) = 2 mean( 1,2,3 ) = 2 mean( 1 ) = 1 mean( [] ) = null
- all( list )
Returns
true
if all elements in the list are true.Table 4.34. Parameters Parameter Type list
list
ofboolean
elementsAlternative signature
all( b1, b2, ..., bN )
Examples
all( [false,null,true] ) = false all( true ) = true all( [true] ) = true all( [] ) = true all( 0 ) = null
- any( list )
Returns
true
if any element in the list is true.Table 4.35. Parameters Parameter Type list
list
ofboolean
elementsAlternative signature
any( b1, b2, ..., bN )
Examples
any( [false,null,true] ) = true any( false ) = false any( [] ) = false any( 0 ) = null
- sublist( list, start position, length? )
Returns the sublist from the start position, limited to the length elements.
Table 4.36. Parameters Parameter Type list
list
start position
number
length
(Optional)number
Example
sublist( [4,5,6], 1, 2 ) = [4,5]
- append( list, item )
Creates a list that is appended to the item or items.
Table 4.37. Parameters Parameter Type list
list
item
Any type
Example
append( [1], 2, 3 ) = [1,2,3]
- concatenate( list )
Creates a list that is the result of the concatenated lists.
Table 4.38. Parameters Parameter Type list
list
Example
concatenate( [1,2],[3] ) = [1,2,3]
- insert before( list, position, newItem )
Creates a list with the
newItem
inserted at the specified position.Table 4.39. Parameters Parameter Type list
list
position
number
newItem
Any type
Example
insert before( [1,3],1,2 ) = [2,1,3]
- remove( list, position )
Creates a list with the removed element excluded from the specified position.
Table 4.40. Parameters Parameter Type list
list
position
number
Example
remove( [1,2,3], 2 ) = [1,3]
- reverse( list )
Returns a reversed list.
Table 4.41. Parameters Parameter Type list
list
Example
reverse( [1,2,3] ) = [3,2,1]
- index of( list, match )
Returns indexes matching the element.
Parameters
-
list
of typelist
-
match
of any type
Table 4.42. Parameters Parameter Type list
list
match
Any type
Example
index of( [1,2,3,2],2 ) = [2,4]
-
- union( list )
Returns a list of all the elements from multiple lists and excludes duplicates.
Table 4.43. Parameters Parameter Type list
list
Example
union( [1,2],[2,3] ) = [1,2,3]
- distinct values( list )
Returns a list of elements from a single list and excludes duplicates.
Table 4.44. Parameters Parameter Type list
list
Example
distinct values( [1,2,3,2,1] ) = [1,2,3]
- flatten( list )
Returns a flattened list.
Table 4.45. Parameters Parameter Type list
list
Example
flatten( [[1,2],[[3]], 4] ) = [1,2,3,4]
- product( list )
Returns the product of the numbers in the list.
Table 4.46. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
product( n1, n2, ..., nN )
Examples
product( [2, 3, 4] ) = 24 product( 2, 3, 4 ) = 24
- median( list )
Returns the median of the numbers in the list. If the number of elements is odd, the result is the middle element. If the number of elements is even, the result is the average of the two middle elements.
Table 4.47. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
median( n1, n2, ..., nN )
Examples
median( 8, 2, 5, 3, 4 ) = 4 median( [6, 1, 2, 3] ) = 2.5 median( [ ] ) = null
- stddev( list )
Returns the standard deviation of the numbers in the list.
Table 4.48. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
stddev( n1, n2, ..., nN )
Examples
stddev( 2, 4, 7, 5 ) = 2.081665999466132735282297706979931 stddev( [47] ) = null stddev( 47 ) = null stddev( [ ] ) = null
- mode( list )
Returns the mode of the numbers in the list. If multiple elements are returned, the numbers are sorted in ascending order.
Table 4.49. Parameters Parameter Type list
list
ofnumber
elementsAlternative signature
mode( n1, n2, ..., nN )
Examples
mode( 6, 3, 9, 6, 6 ) = [6] mode( [6, 1, 9, 6, 1] ) = [1, 6] mode( [ ] ) = [ ]
4.3.2.4.1. Loop statements
Loop statements can transform lists or verify if some elements satisfy a specific condition:
- for in (list)
Iterates the elements of the list.
Table 4.50. Parameters Parameter Type list
list
ofAny
elementsExamples
for i in [1, 2, 3] return i * i = [1, 4, 9] for i in [1,2,3], j in [1,2,3] return i*j = [1, 2, 3, 2, 4, 6, 3, 6, 9]
- some in (list) satisfies (condition)
Returns to single boolean value (true or false), if any element in the list satisfies the condition.
Table 4.51. Parameters Parameter Type list
list
ofAny
elementscondition
boolean expression evaluated to true or false
Examples
some i in [1, 2, 3] satisfies i > 3 = true some i in [1, 2, 3] satisfies i > 4 = false
- every in (list) satisfies (condition)
Returns to single boolean value (true or false), if every element in the list satisfies the condition.
Table 4.52. Parameters Parameter Type list
list
ofAny
elementscondition
boolean expression evaluated to true or false
Examples
every i in [1, 2, 3] satisfies i > 1 = false every i in [1, 2, 3] satisfies i > 0 = true
4.3.2.5. Numeric functions
The following functions support number operations.
- decimal( n, scale )
Returns a number with the specified scale.
Table 4.53. Parameters Parameter Type n
number
scale
number
in the range[−6111..6176]
NoteThis function is implemented to be consistent with the
FEEL:number
definition for rounding decimal numbers to the nearest even decimal number.Examples
decimal( 1/3, 2 ) = .33 decimal( 1.5, 0 ) = 2 decimal( 2.5, 0 ) = 2 decimal( 1.035, 2 ) = 1.04 decimal( 1.045, 2 ) = 1.04 decimal( 1.055, 2 ) = 1.06 decimal( 1.065, 2 ) = 1.06
- floor( n )
Returns the greatest integer that is less than or equal to the specified number.
Table 4.54. Parameters Parameter Type n
number
Examples
floor( 1.5 ) = 1 floor( -1.5 ) = -2
- ceiling( n )
Returns the smallest integer that is greater than or equal to the specified number.
Table 4.55. Parameters Parameter Type n
number
Examples
ceiling( 1.5 ) = 2 ceiling( -1.5 ) = -1
- abs( n )
Returns the absolute value.
Table 4.56. Parameters Parameter Type n
number
,days and time duration
, oryears and months duration
Examples
abs( 10 ) = 10 abs( -10 ) = 10 abs( @"PT5H" ) = @"PT5H" abs( @"-PT5H" ) = @"PT5H"
- modulo( dividend, divisor )
Returns the remainder of the division of the dividend by the divisor. If either the dividend or divisor is negative, the result is of the same sign as the divisor.
NoteThis function is also expressed as
modulo(dividend, divisor) = dividend - divisor*floor(dividen d/divisor)
.Table 4.57. Parameters Parameter Type dividend
number
divisor
number
Examples
modulo( 12, 5 ) = 2 modulo( -12,5 )= 3 modulo( 12,-5 )= -3 modulo( -12,-5 )= -2 modulo( 10.1, 4.5 )= 1.1 modulo( -10.1, 4.5 )= 3.4 modulo( 10.1, -4.5 )= -3.4 modulo( -10.1, -4.5 )= -1.1
- sqrt( number )
Returns the square root of the specified number.
Table 4.58. Parameters Parameter Type n
number
Example
sqrt( 16 ) = 4
- log( number )
Returns the logarithm of the specified number.
Table 4.59. Parameters Parameter Type n
number
Example
decimal( log( 10 ), 2 ) = 2.30
- exp( number )
Returns Euler’s number
e
raised to the power of the specified number.Table 4.60. Parameters Parameter Type n
number
Example
decimal( exp( 5 ), 2 ) = 148.41
- odd( number )
Returns
true
if the specified number is odd.Table 4.61. Parameters Parameter Type n
number
Examples
odd( 5 ) = true odd( 2 ) = false
- even( number )
Returns
true
if the specified number is even.Table 4.62. Parameters Parameter Type n
number
Examples
even( 5 ) = false even ( 2 ) = true
4.3.2.6. Date and time functions
The following functions support date and time operations.
- is( value1, value2 )
Returns
true
if both values are the same element in the FEEL semantic domain.Table 4.63. Parameters Parameter Type value1
Any type
value2
Any type
Examples
is( date( "2012-12-25" ), time( "23:00:50" ) ) = false is( date( "2012-12-25" ), date( "2012-12-25" ) ) = true is( time( "23:00:50z" ), time( "23:00:50" ) ) = false
4.3.2.7. Range functions
The following functions support temporal ordering operations to establish relationships between single scalar values and ranges of such values. These functions are similar to the components in the Health Level Seven (HL7) International Clinical Quality Language (CQL) 1.4 syntax.
- before( )
Returns
true
when an elementA
is before an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
before( point1 point2 )
-
before( point range )
-
before( range point )
-
before( range1,range2 )
Requirements for evaluating to
true
-
point1 < point2
-
point < range.start or ( point = range.start and not(range.start included) )
-
range.end < point or ( range.end = point and not(range.end included) )
-
range1.end < range2.start or (( not(range1.end included) or not(range2.start included) ) and range1.end = range2.start )
Examples
before( 1, 10 ) = true before( 10, 1 ) = false before( 1, [1..10] ) = false before( 1, (1..10] ) = true before( 1, [5..10] ) = true before( [1..10], 10 ) = false before( [1..10), 10 ) = true before( [1..10], 15 ) = true before( [1..10], [15..20] ) = true before( [1..10], [10..20] ) = false before( [1..10), [10..20] ) = true before( [1..10], (10..20] ) = true
-
- after( )
Returns
true
when an elementA
is after an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
after( point1 point2 )
-
after( point range )
-
after( range, point )
-
after( range1 range2 )
Requirements for evaluating to
true
-
point1 > point2
-
point > range.end or ( point = range.end and not(range.end included) )
-
range.start > point or ( range.start = point and not(range.start included) )
-
range1.start > range2.end or (( not(range1.start included) or not(range2.end included) ) and range1.start = range2.end )
Examples
after( 10, 5 ) = true after( 5, 10 ) = false after( 12, [1..10] ) = true after( 10, [1..10) ) = true after( 10, [1..10] ) = false after( [11..20], 12 ) = false after( [11..20], 10 ) = true after( (11..20], 11 ) = true after( [11..20], 11 ) = false after( [11..20], [1..10] ) = true after( [1..10], [11..20] ) = false after( [11..20], [1..11) ) = true after( (11..20], [1..11] ) = true
-
- meets( )
Returns
true
when an elementA
meets an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
meets( range1, range2 )
Requirements for evaluating to
true
-
range1.end included and range2.start included and range1.end = range2.start
Examples
meets( [1..5], [5..10] ) = true meets( [1..5), [5..10] ) = false meets( [1..5], (5..10] ) = false meets( [1..5], [6..10] ) = false
-
- met by( )
Returns
true
when an elementA
is met by an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
met by( range1, range2 )
Requirements for evaluating to
true
-
range1.start included and range2.end included and range1.start = range2.end
Examples
met by( [5..10], [1..5] ) = true met by( [5..10], [1..5) ) = false met by( (5..10], [1..5] ) = false met by( [6..10], [1..5] ) = false
-
- overlaps( )
Returns
true
when an elementA
overlaps an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
overlaps( range1, range2 )
Requirements for evaluating to
true
-
( range1.end > range2.start or (range1.end = range2.start and (range1.end included or range2.end included)) ) and ( range1.start < range2.end or (range1.start = range2.end and range1.start included and range2.end included) )
Examples
overlaps( [1..5], [3..8] ) = true overlaps( [3..8], [1..5] ) = true overlaps( [1..8], [3..5] ) = true overlaps( [3..5], [1..8] ) = true overlaps( [1..5], [6..8] ) = false overlaps( [6..8], [1..5] ) = false overlaps( [1..5], [5..8] ) = true overlaps( [1..5], (5..8] ) = false overlaps( [1..5), [5..8] ) = false overlaps( [1..5), (5..8] ) = false overlaps( [5..8], [1..5] ) = true overlaps( (5..8], [1..5] ) = false overlaps( [5..8], [1..5) ) = false overlaps( (5..8], [1..5) ) = false
-
- overlaps before( )
Returns
true
when an elementA
overlaps before an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
overlaps before( range1 range2 )
Requirements for evaluating to
true
-
( range1.start < range2.start or (range1.start = range2.start and range1.start included and range2.start included) ) and ( range1.end > range2.start or (range1.end = range2.start and range1.end included and range2.start included) ) and ( range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included )) )
Examples
overlaps before( [1..5], [3..8] ) = true overlaps before( [1..5], [6..8] ) = false overlaps before( [1..5], [5..8] ) = true overlaps before( [1..5], (5..8] ) = false overlaps before( [1..5), [5..8] ) = false overlaps before( [1..5), (1..5] ) = true overlaps before( [1..5], (1..5] ) = true overlaps before( [1..5), [1..5] ) = false overlaps before( [1..5], [1..5] ) = false
-
- overlaps after( )
Returns
true
when an elementA
overlaps after an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
overlaps after( range1 range2 )
Requirements for evaluating to
true
-
( range2.start < range1.start or (range2.start = range1.start and range2.start included and not( range1.start included)) ) and ( range2.end > range1.start or (range2.end = range1.start and range2.end included and range1.start included) ) and ( range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)) )
Examples
overlaps after( [3..8], [1..5] )= true overlaps after( [6..8], [1..5] )= false overlaps after( [5..8], [1..5] )= true overlaps after( (5..8], [1..5] )= false overlaps after( [5..8], [1..5) )= false overlaps after( (1..5], [1..5) )= true overlaps after( (1..5], [1..5] )= true overlaps after( [1..5], [1..5) )= false overlaps after( [1..5], [1..5] )= false overlaps after( (1..5), [1..5] )= false overlaps after( (1..5], [1..6] )= false overlaps after( (1..5], (1..5] )= false overlaps after( (1..5], [2..5] )= false
-
- finishes( )
Returns
true
when an elementA
finishes an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
finishes( point, range )
-
finishes( range1, range2 )
Requirements for evaluating to
true
-
range.end included and range.end = point
-
range1.end included = range2.end included and range1.end = range2.end and ( range1.start > range2.start or (range1.start = range2.start and (not(range1.start included) or range2.start included)) )
Examples
finishes( 10, [1..10] ) = true finishes( 10, [1..10) ) = false finishes( [5..10], [1..10] ) = true finishes( [5..10), [1..10] ) = false finishes( [5..10), [1..10) ) = true finishes( [1..10], [1..10] ) = true finishes( (1..10], [1..10] ) = true
-
- finished by( )
Returns
true
when an elementA
is finished by an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
finished by( range, point )
-
finished by( range1 range2 )
Requirements for evaluating to
true
-
range.end included and range.end = point
-
range1.end included = range2.end included and range1.end = range2.end and ( range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included))) )
Examples
finished by( [1..10], 10 ) = true finished by( [1..10), 10 ) = false finished by( [1..10], [5..10] ) = true finished by( [1..10], [5..10) ) = false finished by( [1..10), [5..10) ) = true finished by( [1..10], [1..10] ) = true finished by( [1..10], (1..10] ) = true
-
- includes( )
Returns
true
when an elementA
includes an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
includes( range, point )
-
includes( range1, range2 )
Requirements for evaluating to
true
-
(range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)
-
( range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included))) ) and ( range1.end > range2.end or (range1.end = range2.end and (range1.end included or not(range2.end included))) )
Examples
includes( [1..10], 5 ) = true includes( [1..10], 12 ) = false includes( [1..10], 1 ) = true includes( [1..10], 10 ) = true includes( (1..10], 1 ) = false includes( [1..10), 10 ) = false includes( [1..10], [4..6] ) = true includes( [1..10], [1..5] ) = true includes( (1..10], (1..5] ) = true includes( [1..10], (1..10) ) = true includes( [1..10), [5..10) ) = true includes( [1..10], [1..10) ) = true includes( [1..10], (1..10] ) = true includes( [1..10], [1..10] ) = true
-
- during( )
Returns
true
when an elementA
is during an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
during( point, range )
-
during( range1 range2 )
Requirements for evaluating to
true
-
(range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)
-
( range2.start < range1.start or (range2.start = range1.start and (range2.start included or not(range1.start included))) ) and ( range2.end > range1.end or (range2.end = range1.end and (range2.end included or not(range1.end included))) )
Examples
during( 5, [1..10] ) = true during( 12, [1..10] ) = false during( 1, [1..10] ) = true during( 10, [1..10] ) = true during( 1, (1..10] ) = false during( 10, [1..10) ) = false during( [4..6], [1..10] ) = true during( [1..5], [1..10] ) = true during( (1..5], (1..10] ) = true during( (1..10), [1..10] ) = true during( [5..10), [1..10) ) = true during( [1..10), [1..10] ) = true during( (1..10], [1..10] ) = true during( [1..10], [1..10] ) = true
-
- starts( )
Returns
true
when an elementA
starts an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
starts( point, range )
-
starts( range1, range2 )
Requirements for evaluating to
true
-
range.start = point and range.start included
-
range1.start = range2.start and range1.start included = range2.start included and ( range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included)) )
Examples
starts( 1, [1..10] ) = true starts( 1, (1..10] ) = false starts( 2, [1..10] ) = false starts( [1..5], [1..10] ) = true starts( (1..5], (1..10] ) = true starts( (1..5], [1..10] ) = false starts( [1..5], (1..10] ) = false starts( [1..10], [1..10] ) = true starts( [1..10), [1..10] ) = true starts( (1..10), (1..10) ) = true
-
- started by( )
Returns
true
when an elementA
is started by an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
started by( range, point )
-
started by( range1, range2 )
Requirements for evaluating to
true
-
range.start = point and range.start included
-
range1.start = range2.start and range1.start included = range2.start included and ( range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)) )
Examples
started by( [1..10], 1 ) = true started by( (1..10], 1 ) = false started by( [1..10], 2 ) = false started by( [1..10], [1..5] ) = true started by( (1..10], (1..5] ) = true started by( [1..10], (1..5] ) = false started by( (1..10], [1..5] ) = false started by( [1..10], [1..10] ) = true started by( [1..10], [1..10) ) = true started by( (1..10), (1..10) ) = true
-
- coincides( )
Returns
true
when an elementA
coincides with an elementB
and when the relevant requirements for evaluating totrue
are also met.Signatures
-
coincides( point1, point2 )
-
coincides( range1, range2 )
Requirements for evaluating to
true
-
point1 = point2
-
range1.start = range2.start and range1.start included = range2.start included and range1.end = range2.end and range1.end included = range2.end included
Examples
coincides( 5, 5 ) = true coincides( 3, 4 ) = false coincides( [1..5], [1..5] ) = true coincides( (1..5), [1..5] ) = false coincides( [1..5], [2..6] ) = false
-
4.3.2.8. Temporal functions
The following functions support general temporal operations.
- day of year( date )
Returns the Gregorian number of the day of the year.
Table 4.64. Parameters Parameter Type date
date
ordate and time
Example
day of year( date(2019, 9, 17) ) = 260
- day of week( date )
Returns the Gregorian day of the week:
"Monday"
,"Tuesday"
,"Wednesday"
,"Thursday"
,"Friday"
,"Saturday"
, or"Sunday"
.Table 4.65. Parameters Parameter Type date
date
ordate and time
Example
day of week( date(2019, 9, 17) ) = "Tuesday"
- month of year( date )
Returns the Gregorian month of the year:
"January"
,"February"
,"March"
,"April"
,"May"
,"June"
,"July"
,"August"
,"September"
,"October"
,"November"
, or"December"
.Table 4.66. Parameters Parameter Type date
date
ordate and time
Example
month of year( date(2019, 9, 17) ) = "September"
- month of year( date )
Returns the Gregorian week of the year as defined by ISO 8601.
Table 4.67. Parameters Parameter Type date
date
ordate and time
Examples
week of year( date(2019, 9, 17) ) = 38 week of year( date(2003, 12, 29) ) = 1 week of year( date(2004, 1, 4) ) = 1 week of year( date(2005, 1, 1) ) = 53 week of year( date(2005, 1, 3) ) = 1 week of year( date(2005, 1, 9) ) = 1
4.3.2.9. Sort functions
The following functions support sorting operations.
- sort( list, precedes )
Returns a list of the same elements but ordered according to the sorting function.
Table 4.68. Parameters Parameter Type list
list
precedes
function
Example
sort( list: [3,1,4,5,2], precedes: function(x,y) x < y ) = [1,2,3,4,5]
4.3.2.10. Context functions
The following functions support context operations.
- get value( m, key )
Returns the value from the context for the specified entry key.
Table 4.69. Parameters Parameter Type m
context
key
string
Examples
get value( {key1 : "value1"}, "key1" ) = "value1" get value( {key1 : "value1"}, "unexistent-key" ) = null
- get entries( m )
Returns a list of key-value pairs for the specified context.
Table 4.70. Parameters Parameter Type m
context
Example
get entries( {key1 : "value1", key2 : "value2"} ) = [ { key : "key1", value : "value1" }, {key : "key2", value : "value2"} ]
4.3.3. Variable and function names in FEEL
Unlike many traditional expression languages, Friendly Enough Expression Language (FEEL) supports spaces and a few special characters as part of variable and function names. A FEEL name must start with a letter
, ?
, or _
element. The unicode letter characters are also allowed. Variable names cannot start with a language keyword, such as and
, true
, or every
. The remaining characters in a variable name can be any of the starting characters, as well as digits
, white spaces, and special characters such as +
, -
, /
, *
, '
, and .
.
For example, the following names are all valid FEEL names:
- Age
- Birth Date
- Flight 234 pre-check procedure
Several limitations apply to variable and function names in FEEL:
- Ambiguity
-
The use of spaces, keywords, and other special characters as part of names can make FEEL ambiguous. The ambiguities are resolved in the context of the expression, matching names from left to right. The parser resolves the variable name as the longest name matched in scope. You can use
( )
to disambiguate names if necessary. - Spaces in names
The DMN specification limits the use of spaces in FEEL names. According to the DMN specification, names can contain multiple spaces but not two consecutive spaces.
In order to make the language easier to use and avoid common errors due to spaces, Red Hat Decision Manager removes the limitation on the use of consecutive spaces. Red Hat Decision Manager supports variable names with any number of consecutive spaces, but normalizes them into a single space. For example, the variable references
First Name
with one space andFirst Name
with two spaces are both acceptable in Red Hat Decision Manager.Red Hat Decision Manager also normalizes the use of other white spaces, like the non-breakable white space that is common in web pages, tabs, and line breaks. From a Red Hat Decision Manager FEEL engine perspective, all of these characters are normalized into a single white space before processing.
- The keyword
in
-
The keyword
in
is the only keyword in the language that cannot be used as part of a variable name. Although the specifications allow the use of keywords in the middle of variable names, the use ofin
in variable names conflicts with the grammar definition offor
,every
andsome
expression constructs.
4.4. DMN decision logic in boxed expressions
Boxed expressions in DMN are tables that you use to define the underlying logic of decision nodes and business knowledge models in a decision requirements diagram (DRD). Some boxed expressions can contain other boxed expressions, but the top-level boxed expression corresponds to the decision logic of a single DRD artifact. While DRDs represent the flow of a DMN decision model, boxed expressions define the actual decision logic of individual nodes. DRDs and boxed expressions together form a complete and functional DMN decision model.
The following are the types of DMN boxed expressions:
- Decision tables
- Literal expressions
- Contexts
- Relations
- Functions
- Invocations
- Lists
Red Hat Decision Manager does not provide boxed list expressions in Business Central, but supports a FEEL list
data type that you can use in boxed literal expressions. For more information about the list
data type and other FEEL data types in Red Hat Decision Manager, see Section 4.3.1, “Data types in FEEL”.
All Friendly Enough Expression Language (FEEL) expressions that you use in your boxed expressions must conform to the FEEL syntax requirements in the OMG Decision Model and Notation specification.
4.4.1. DMN decision tables
A decision table in DMN is a visual representation of one or more business rules in a tabular format. You use decision tables to define rules for a decision node that applies those rules at a given point in the decision model. Each rule consists of a single row in the table, and includes columns that define the conditions (input) and outcome (output) for that particular row. The definition of each row is precise enough to derive the outcome using the values of the conditions. Input and output values can be FEEL expressions or defined data type values.
For example, the following decision table determines credit score ratings based on a defined range of a loan applicant’s credit score:
Figure 4.3. Decision table for credit score rating
The following decision table determines the next step in a lending strategy for applicants depending on applicant loan eligibility and the bureau call type:
Figure 4.4. Decision table for lending strategy
The following decision table determines applicant qualification for a loan as the concluding decision node in a loan prequalification decision model:
Figure 4.5. Decision table for loan prequalification
Decision tables are a popular way of modeling rules and decision logic, and are used in many methodologies (such as DMN) and implementation frameworks (such as Drools).
Red Hat Decision Manager supports both DMN decision tables and Drools-native decision tables, but they are different types of assets with different syntax requirements and are not interchangeable. For more information about Drools-native decision tables in Red Hat Decision Manager, see Designing a decision service using spreadsheet decision tables.
4.4.1.1. Hit policies in DMN decision tables
Hit policies determine how to reach an outcome when multiple rules in a decision table match the provided input values. For example, if one rule in a decision table applies a sales discount to military personnel and another rule applies a discount to students, then when a customer is both a student and in the military, the decision table hit policy must indicate whether to apply one discount or the other (Unique, First) or both discounts (Collect Sum). You specify the single character of the hit policy (U, F, C+) in the upper-left corner of the decision table.
The following decision table hit policies are supported in DMN:
- Unique (U): Permits only one rule to match. Any overlap raises an error.
- Any (A): Permits multiple rules to match, but they must all have the same output. If multiple matching rules do not have the same output, an error is raised.
- Priority (P): Permits multiple rules to match, with different outputs. The output that comes first in the output values list is selected.
- First (F): Uses the first match in rule order.
Collect (C+, C>, C<, C#): Aggregates output from multiple rules based on an aggregation function.
- Collect ( C ): Aggregates values in an arbitrary list.
- Collect Sum (C+): Outputs the sum of all collected values. Values must be numeric.
- Collect Min (C<): Outputs the minimum value among the matches. The resulting values must be comparable, such as numbers, dates, or text (lexicographic order).
- Collect Max (C>): Outputs the maximum value among the matches. The resulting values must be comparable, such as numbers, dates or text (lexicographic order).
- Collect Count (C#): Outputs the number of matching rules.
4.4.2. Boxed literal expressions
A boxed literal expression in DMN is a literal FEEL expression as text in a table cell, typically with a labeled column and an assigned data type. You use boxed literal expressions to define simple or complex node logic or decision data directly in FEEL for a particular node in a decision. Literal FEEL expressions must conform to FEEL syntax requirements in the OMG Decision Model and Notation specification.
For example, the following boxed literal expression defines the minimum acceptable PITI calculation (principal, interest, taxes, and insurance) in a lending decision, where acceptable rate
is a variable defined in the DMN model:
Figure 4.6. Boxed literal expression for minimum PITI value
The following boxed literal expression sorts a list of possible dating candidates (soul mates) in an online dating application based on their score on criteria such as age, location, and interests:
Figure 4.7. Boxed literal expression for matching online dating candidates
4.4.3. Boxed context expressions
A boxed context expression in DMN is a set of variable names and values with a result value. Each name-value pair is a context entry. You use context expressions to represent data definitions in decision logic and set a value for a desired decision element within the DMN decision model. A value in a boxed context expression can be a data type value or FEEL expression, or can contain a nested sub-expression of any type, such as a decision table, a literal expression, or another context expression.
For example, the following boxed context expression defines the factors for sorting delayed passengers in a flight-rebooking decision model, based on defined data types (tPassengerTable
, tFlightNumberList
):
Figure 4.8. Boxed context expression for flight passenger waiting list
The following boxed context expression defines the factors that determine whether a loan applicant can meet minimum mortgage payments based on principal, interest, taxes, and insurance (PITI), represented as a front-end ratio calculation with a sub-context expression:
Figure 4.9. Boxed context expression for front-end client PITI ratio
4.4.4. Boxed relation expressions
A boxed relation expression in DMN is a traditional data table with information about given entities, listed as rows. You use boxed relation tables to define decision data for relevant entities in a decision at a particular node. Boxed relation expressions are similar to context expressions in that they set variable names and values, but relation expressions contain no result value and list all variable values based on a single defined variable in each column.
For example, the following boxed relation expression provides information about employees in an employee rostering decision:
Figure 4.10. Boxed relation expression with employee information
4.4.5. Boxed function expressions
A boxed function expression in DMN is a parameterized boxed expression containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type. By default, all business knowledge models are defined as boxed function expressions. You use boxed function expressions to call functions on your decision logic and to define all business knowledge models.
For example, the following boxed function expression determines airline flight capacity in a flight-rebooking decision model:
Figure 4.11. Boxed function expression for flight capacity
The following boxed function expression contains a basic Java function as a context expression for determining absolute value in a decision model calculation:
Figure 4.12. Boxed function expression for absolute value
The following boxed function expression determines a monthly mortgage installment as a business knowledge model in a lending decision, with the function value defined as a nested context expression:
Figure 4.13. Boxed function expression for installment calculation in business knowledge model
The following boxed function expression uses a PMML model included in the DMN file to define the minimum acceptable PITI calculation (principal, interest, taxes, and insurance) in a lending decision:
Figure 4.14. Boxed function expression with an included PMML model in business knowledge model
4.4.6. Boxed invocation expressions
A boxed invocation expression in DMN is a boxed expression that invokes a business knowledge model. A boxed invocation expression contains the name of the business knowledge model to be invoked and a list of parameter bindings. Each binding is represented by two boxed expressions on a row: The box on the left contains the name of a parameter and the box on the right contains the binding expression whose value is assigned to the parameter to evaluate the invoked business knowledge model. You use boxed invocations to invoke at a particular decision node a business knowledge model defined in the decision model.
For example, the following boxed invocation expression invokes a Reassign Next Passenger
business knowledge model as the concluding decision node in a flight-rebooking decision model:
Figure 4.15. Boxed invocation expression to reassign flight passengers
The following boxed invocation expression invokes an InstallmentCalculation
business knowledge model to calculate a monthly installment amount for a loan before proceeding to affordability decisions:
Figure 4.16. Boxed invocation expression for required monthly installment
4.4.7. Boxed list expressions
A boxed list expression in DMN represents a FEEL list of items. You use boxed lists to define lists of relevant items for a particular node in a decision. You can also use literal FEEL expressions for list items in cells to create more complex lists.
For example, the following boxed list expression identifies approved credit score agencies in a loan application decision service:
Figure 4.17. Boxed list expression for approved credit score agencies
The following boxed list expression also identifies approved credit score agencies but uses FEEL logic to define the agency status (Inc., LLC, SA, GA) based on a DMN input node:
Figure 4.18. Boxed list expression using FEEL logic for approved credit score agency status
4.5. DMN model example
The following is a real-world DMN model example that demonstrates how you can use decision modeling to reach a decision based on input data, circumstances, and company guidelines. In this scenario, a flight from San Diego to New York is canceled, requiring the affected airline to find alternate arrangements for its inconvenienced passengers.
First, the airline collects the information necessary to determine how best to get the travelers to their destinations:
- Input data
- List of flights
- List of passengers
- Decisions
- Prioritize the passengers who will get seats on a new flight
- Determine which flights those passengers will be offered
- Business knowledge models
- The company process for determining passenger priority
- Any flights that have space available
- Company rules for determining how best to reassign inconvenienced passengers
The airline then uses the DMN standard to model its decision process in the following decision requirements diagram (DRD) for determining the best rebooking solution:
Figure 4.19. DRD for flight rebooking
Similar to flowcharts, DRDs use shapes to represent the different elements in a process. Ovals contain the two necessary input data, rectangles contain the decision points in the model, and rectangles with clipped corners (business knowledge models) contain reusable logic that can be repeatedly invoked.
The DRD draws logic for each element from boxed expressions that provide variable definitions using FEEL expressions or data type values.
Some boxed expressions are basic, such as the following decision for establishing a prioritized waiting list:
Figure 4.20. Boxed context expression example for prioritized wait list
Some boxed expressions are more complex with greater detail and calculation, such as the following business knowledge model for reassigning the next delayed passenger:
Figure 4.21. Boxed function expression for passenger reassignment
The following is the DMN source file for this decision model:
<dmn:definitions xmlns="https://www.drools.org/kie-dmn/Flight-rebooking" xmlns:dmn="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" id="_0019_flight_rebooking" name="0019-flight-rebooking" namespace="https://www.drools.org/kie-dmn/Flight-rebooking"> <dmn:itemDefinition id="_tFlight" name="tFlight"> <dmn:itemComponent id="_tFlight_Flight" name="Flight Number"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_From" name="From"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_To" name="To"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_Dep" name="Departure"> <dmn:typeRef>feel:dateTime</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_Arr" name="Arrival"> <dmn:typeRef>feel:dateTime</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_Capacity" name="Capacity"> <dmn:typeRef>feel:number</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tFlight_Status" name="Status"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> </dmn:itemDefinition> <dmn:itemDefinition id="_tFlightTable" isCollection="true" name="tFlightTable"> <dmn:typeRef>tFlight</dmn:typeRef> </dmn:itemDefinition> <dmn:itemDefinition id="_tPassenger" name="tPassenger"> <dmn:itemComponent id="_tPassenger_Name" name="Name"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tPassenger_Status" name="Status"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tPassenger_Miles" name="Miles"> <dmn:typeRef>feel:number</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_tPassenger_Flight" name="Flight Number"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemComponent> </dmn:itemDefinition> <dmn:itemDefinition id="_tPassengerTable" isCollection="true" name="tPassengerTable"> <dmn:typeRef>tPassenger</dmn:typeRef> </dmn:itemDefinition> <dmn:itemDefinition id="_tFlightNumberList" isCollection="true" name="tFlightNumberList"> <dmn:typeRef>feel:string</dmn:typeRef> </dmn:itemDefinition> <dmn:inputData id="i_Flight_List" name="Flight List"> <dmn:variable name="Flight List" typeRef="tFlightTable"/> </dmn:inputData> <dmn:inputData id="i_Passenger_List" name="Passenger List"> <dmn:variable name="Passenger List" typeRef="tPassengerTable"/> </dmn:inputData> <dmn:decision name="Prioritized Waiting List" id="d_PrioritizedWaitingList"> <dmn:variable name="Prioritized Waiting List" typeRef="tPassengerTable"/> <dmn:informationRequirement> <dmn:requiredInput href="#i_Passenger_List"/> </dmn:informationRequirement> <dmn:informationRequirement> <dmn:requiredInput href="#i_Flight_List"/> </dmn:informationRequirement> <dmn:knowledgeRequirement> <dmn:requiredKnowledge href="#b_PassengerPriority"/> </dmn:knowledgeRequirement> <dmn:context> <dmn:contextEntry> <dmn:variable name="Cancelled Flights" typeRef="tFlightNumberList"/> <dmn:literalExpression> <dmn:text>Flight List[ Status = "cancelled" ].Flight Number</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Waiting List" typeRef="tPassengerTable"/> <dmn:literalExpression> <dmn:text>Passenger List[ list contains( Cancelled Flights, Flight Number ) ]</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:literalExpression> <dmn:text>sort( Waiting List, passenger priority )</dmn:text> </dmn:literalExpression> </dmn:contextEntry> </dmn:context> </dmn:decision> <dmn:decision name="Rebooked Passengers" id="d_RebookedPassengers"> <dmn:variable name="Rebooked Passengers" typeRef="tPassengerTable"/> <dmn:informationRequirement> <dmn:requiredDecision href="#d_PrioritizedWaitingList"/> </dmn:informationRequirement> <dmn:informationRequirement> <dmn:requiredInput href="#i_Flight_List"/> </dmn:informationRequirement> <dmn:knowledgeRequirement> <dmn:requiredKnowledge href="#b_ReassignNextPassenger"/> </dmn:knowledgeRequirement> <dmn:invocation> <dmn:literalExpression> <dmn:text>reassign next passenger</dmn:text> </dmn:literalExpression> <dmn:binding> <dmn:parameter name="Waiting List"/> <dmn:literalExpression> <dmn:text>Prioritized Waiting List</dmn:text> </dmn:literalExpression> </dmn:binding> <dmn:binding> <dmn:parameter name="Reassigned Passengers List"/> <dmn:literalExpression> <dmn:text>[]</dmn:text> </dmn:literalExpression> </dmn:binding> <dmn:binding> <dmn:parameter name="Flights"/> <dmn:literalExpression> <dmn:text>Flight List</dmn:text> </dmn:literalExpression> </dmn:binding> </dmn:invocation> </dmn:decision> <dmn:businessKnowledgeModel id="b_PassengerPriority" name="passenger priority"> <dmn:encapsulatedLogic> <dmn:formalParameter name="Passenger1" typeRef="tPassenger"/> <dmn:formalParameter name="Passenger2" typeRef="tPassenger"/> <dmn:decisionTable hitPolicy="UNIQUE"> <dmn:input id="b_Passenger_Priority_dt_i_P1_Status" label="Passenger1.Status"> <dmn:inputExpression typeRef="feel:string"> <dmn:text>Passenger1.Status</dmn:text> </dmn:inputExpression> <dmn:inputValues> <dmn:text>"gold", "silver", "bronze"</dmn:text> </dmn:inputValues> </dmn:input> <dmn:input id="b_Passenger_Priority_dt_i_P2_Status" label="Passenger2.Status"> <dmn:inputExpression typeRef="feel:string"> <dmn:text>Passenger2.Status</dmn:text> </dmn:inputExpression> <dmn:inputValues> <dmn:text>"gold", "silver", "bronze"</dmn:text> </dmn:inputValues> </dmn:input> <dmn:input id="b_Passenger_Priority_dt_i_P1_Miles" label="Passenger1.Miles"> <dmn:inputExpression typeRef="feel:string"> <dmn:text>Passenger1.Miles</dmn:text> </dmn:inputExpression> </dmn:input> <dmn:output id="b_Status_Priority_dt_o" label="Passenger1 has priority"> <dmn:outputValues> <dmn:text>true, false</dmn:text> </dmn:outputValues> <dmn:defaultOutputEntry> <dmn:text>false</dmn:text> </dmn:defaultOutputEntry> </dmn:output> <dmn:rule id="b_Passenger_Priority_dt_r1"> <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i1"> <dmn:text>"gold"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i2"> <dmn:text>"gold"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i3"> <dmn:text>>= Passenger2.Miles</dmn:text> </dmn:inputEntry> <dmn:outputEntry id="b_Passenger_Priority_dt_r1_o1"> <dmn:text>true</dmn:text> </dmn:outputEntry> </dmn:rule> <dmn:rule id="b_Passenger_Priority_dt_r2"> <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i1"> <dmn:text>"gold"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i2"> <dmn:text>"silver","bronze"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i3"> <dmn:text>-</dmn:text> </dmn:inputEntry> <dmn:outputEntry id="b_Passenger_Priority_dt_r2_o1"> <dmn:text>true</dmn:text> </dmn:outputEntry> </dmn:rule> <dmn:rule id="b_Passenger_Priority_dt_r3"> <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i1"> <dmn:text>"silver"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i2"> <dmn:text>"silver"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i3"> <dmn:text>>= Passenger2.Miles</dmn:text> </dmn:inputEntry> <dmn:outputEntry id="b_Passenger_Priority_dt_r3_o1"> <dmn:text>true</dmn:text> </dmn:outputEntry> </dmn:rule> <dmn:rule id="b_Passenger_Priority_dt_r4"> <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i1"> <dmn:text>"silver"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i2"> <dmn:text>"bronze"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i3"> <dmn:text>-</dmn:text> </dmn:inputEntry> <dmn:outputEntry id="b_Passenger_Priority_dt_r4_o1"> <dmn:text>true</dmn:text> </dmn:outputEntry> </dmn:rule> <dmn:rule id="b_Passenger_Priority_dt_r5"> <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i1"> <dmn:text>"bronze"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i2"> <dmn:text>"bronze"</dmn:text> </dmn:inputEntry> <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i3"> <dmn:text>>= Passenger2.Miles</dmn:text> </dmn:inputEntry> <dmn:outputEntry id="b_Passenger_Priority_dt_r5_o1"> <dmn:text>true</dmn:text> </dmn:outputEntry> </dmn:rule> </dmn:decisionTable> </dmn:encapsulatedLogic> <dmn:variable name="passenger priority" typeRef="feel:boolean"/> </dmn:businessKnowledgeModel> <dmn:businessKnowledgeModel id="b_ReassignNextPassenger" name="reassign next passenger"> <dmn:encapsulatedLogic> <dmn:formalParameter name="Waiting List" typeRef="tPassengerTable"/> <dmn:formalParameter name="Reassigned Passengers List" typeRef="tPassengerTable"/> <dmn:formalParameter name="Flights" typeRef="tFlightTable"/> <dmn:context> <dmn:contextEntry> <dmn:variable name="Next Passenger" typeRef="tPassenger"/> <dmn:literalExpression> <dmn:text>Waiting List[1]</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Original Flight" typeRef="tFlight"/> <dmn:literalExpression> <dmn:text>Flights[ Flight Number = Next Passenger.Flight Number ][1]</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Best Alternate Flight" typeRef="tFlight"/> <dmn:literalExpression> <dmn:text>Flights[ From = Original Flight.From and To = Original Flight.To and Departure > Original Flight.Departure and Status = "scheduled" and has capacity( item, Reassigned Passengers List ) ][1]</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Reassigned Passenger" typeRef="tPassenger"/> <dmn:context> <dmn:contextEntry> <dmn:variable name="Name" typeRef="feel:string"/> <dmn:literalExpression> <dmn:text>Next Passenger.Name</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Status" typeRef="feel:string"/> <dmn:literalExpression> <dmn:text>Next Passenger.Status</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Miles" typeRef="feel:number"/> <dmn:literalExpression> <dmn:text>Next Passenger.Miles</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Flight Number" typeRef="feel:string"/> <dmn:literalExpression> <dmn:text>Best Alternate Flight.Flight Number</dmn:text> </dmn:literalExpression> </dmn:contextEntry> </dmn:context> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Remaining Waiting List" typeRef="tPassengerTable"/> <dmn:literalExpression> <dmn:text>remove( Waiting List, 1 )</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:variable name="Updated Reassigned Passengers List" typeRef="tPassengerTable"/> <dmn:literalExpression> <dmn:text>append( Reassigned Passengers List, Reassigned Passenger )</dmn:text> </dmn:literalExpression> </dmn:contextEntry> <dmn:contextEntry> <dmn:literalExpression> <dmn:text>if count( Remaining Waiting List ) > 0 then reassign next passenger( Remaining Waiting List, Updated Reassigned Passengers List, Flights ) else Updated Reassigned Passengers List</dmn:text> </dmn:literalExpression> </dmn:contextEntry> </dmn:context> </dmn:encapsulatedLogic> <dmn:variable name="reassign next passenger" typeRef="tPassengerTable"/> <dmn:knowledgeRequirement> <dmn:requiredKnowledge href="#b_HasCapacity"/> </dmn:knowledgeRequirement> </dmn:businessKnowledgeModel> <dmn:businessKnowledgeModel id="b_HasCapacity" name="has capacity"> <dmn:encapsulatedLogic> <dmn:formalParameter name="flight" typeRef="tFlight"/> <dmn:formalParameter name="rebooked list" typeRef="tPassengerTable"/> <dmn:literalExpression> <dmn:text>flight.Capacity > count( rebooked list[ Flight Number = flight.Flight Number ] )</dmn:text> </dmn:literalExpression> </dmn:encapsulatedLogic> <dmn:variable name="has capacity" typeRef="feel:boolean"/> </dmn:businessKnowledgeModel> </dmn:definitions>
Chapter 5. DMN support in Red Hat Decision Manager
Red Hat Decision Manager provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3, and design support for DMN 1.2 models at conformance level 3. You can integrate DMN models with your Red Hat Decision Manager decision services in several ways:
- Design your DMN models directly in Business Central using the DMN designer.
- Import DMN files into your project in Business Central (Menu → Design → Projects → Import Asset). Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import into Business Central, open in the DMN designer, and save are converted to DMN 1.2 models.
- Package DMN files as part of your project knowledge JAR (KJAR) file without Business Central.
The following table summarizes the design and runtime support for each DMN version in Red Hat Decision Manager:
DMN version | DMN engine support | DMN modeler support | |
Execution | Open | Save | |
DMN 1.1 |
|
|
|
DMN 1.2 |
|
|
|
DMN 1.3 |
|
|
|
DMN 1.4 |
|
|
|
In addition to all DMN conformance level 3 requirements, Red Hat Decision Manager also includes enhancements and fixes to FEEL and DMN model components to optimize the experience of implementing DMN decision services with Red Hat Decision Manager. From a platform perspective, DMN models are like any other business asset in Red Hat Decision Manager, such as DRL files or spreadsheet decision tables, that you can include in your Red Hat Decision Manager project and deploy to KIE Server in order to start your DMN decision services.
For more information about including external DMN files with your Red Hat Decision Manager project packaging and deployment method, see Packaging and deploying an Red Hat Decision Manager project.
You can design a new DMN decision service using a Red Hat build of Kogito microservice as an alternative for the cloud-native capabilities of DMN decision services. You can migrate your DMN service to a Red Hat build of Kogito microservice. For more information about migrating to Red Hat build of Kogito microservices, see Migrating to Red Hat build of Kogito microservices.
5.1. Configurable DMN properties in Red Hat Decision Manager
Red Hat Decision Manager provides the following DMN properties that you can configure when you execute your DMN models on KIE Server or on your client application. You can configure some of these properties using the kmodule.xml
file in your Red Hat Decision Manager project when you deploy your project on KIE Server.
- org.kie.dmn.strictConformance
When enabled, this property disables by default any extensions or profiles provided beyond the DMN standard, such as some helper functions or enhanced features of DMN 1.2 backported into DMN 1.1. You can use this property to configure the decision engine to support only pure DMN features, such as when running the DMN Technology Compatibility Kit (TCK).
Default value:
false
-Dorg.kie.dmn.strictConformance=true
- org.kie.dmn.runtime.typecheck
When enabled, this property enables verification of actual values conforming to their declared types in the DMN model, as input or output of DRD elements. You can use this property to verify whether data supplied to the DMN model or produced by the DMN model is compliant with what is specified in the model.
Default value:
false
-Dorg.kie.dmn.runtime.typecheck=true
- org.kie.dmn.decisionservice.coercesingleton
By default, this property makes the result of a decision service defining a single output decision be the single value of the output decision value. When disabled, this property makes the result of a decision service defining a single output decision be a
context
with the single entry for that decision. You can use this property to adjust your decision service outputs according to your project requirements.Default value:
true
-Dorg.kie.dmn.decisionservice.coercesingleton=false
- org.kie.dmn.profiles.$PROFILE_NAME
When valorized with a Java fully qualified name, this property loads a DMN profile onto the decision engine at start time. You can use this property to implement a predefined DMN profile with supported features different from or beyond the DMN standard. For example, if you are creating DMN models using the Signavio DMN modeller, use this property to implement features from the Signavio DMN profile into your DMN decision service.
-Dorg.kie.dmn.profiles.signavio=org.kie.dmn.signavio.KieDMNSignavioProfile
- org.kie.dmn.runtime.listeners.$LISTENER_NAME
When valorized with a Java fully qualified name, this property loads and registers a DMN Runtime Listener onto the decision engine at start time. You can use this property to register a DMN listener in order to be notified of several events during DMN model evaluations.
To configure this property when deploying your project on KIE Server, modify this property in the
kmodule.xml
file of your project. This approach is helpful when the listener is specific to your project and when the configuration must be applied in KIE Server only to your deployed project.<kmodule xmlns="http://www.drools.org/xsd/kmodule"> <configuration> <property key="org.kie.dmn.runtime.listeners.mylistener" value="org.acme.MyDMNListener"/> </configuration> </kmodule>
To configure this property globally for your Red Hat Decision Manager environment, modify this property using a command terminal or any other global application configuration mechanism. This approach is helpful when the decision engine is embedded as part of your Java application.
-Dorg.kie.dmn.runtime.listeners.mylistener=org.acme.MyDMNListener
- org.kie.dmn.compiler.execmodel
When enabled, this property enables DMN decision table logic to be compiled into executable rule models during run time. You can use this property to evaluate DMN decision table logic more efficiently. This property is helpful when the executable model compilation was not originally performed during project compile time. Enabling this property may result in added compile time during the first evaluation by the decision engine, but subsequent compilations are more efficient.
Default value:
false
-Dorg.kie.dmn.compiler.execmodel=true
5.2. Configurable DMN validation in Red Hat Decision Manager
By default, the kie-maven-plugin
component in the pom.xml
file of your Red Hat Decision Manager project uses the following <validateDMN>
configurations to perform pre-compilation validation of DMN model assets and to perform DMN decision table static analysis:
-
VALIDATE_SCHEMA
: DMN model files are verified against the DMN specification XSD schema to ensure that the files are valid XML and compliant with the specification. -
VALIDATE_MODEL
: The pre-compilation analysis is performed for the DMN model to ensure that the basic semantic is aligned with the DMN specification. -
ANALYZE_DECISION_TABLE
: DMN decision tables are statically analyzed for gaps or overlaps and to ensure that the semantic of the decision table follows best practices.
You can modify the default DMN validation and DMN decision table analysis behavior to perform only a specified validation during the project build, or you can disable this default behavior completely, as shown in the following examples:
Default configuration for DMN validation and decision table analysis
<plugin> <groupId>org.kie</groupId> <artifactId>kie-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <validateDMN>VALIDATE_SCHEMA,VALIDATE_MODEL,ANALYZE_DECISION_TABLE</validateDMN> </configuration> </plugin>
Configuration to perform only the DMN decision table static analysis
<plugin> <groupId>org.kie</groupId> <artifactId>kie-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <validateDMN>ANALYZE_DECISION_TABLE</validateDMN> </configuration> </plugin>
Configuration to perform only the XSD schema validation
<plugin> <groupId>org.kie</groupId> <artifactId>kie-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <validateDMN>VALIDATE_SCHEMA</validateDMN> </configuration> </plugin>
Configuration to perform only the DMN model validation
<plugin> <groupId>org.kie</groupId> <artifactId>kie-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <validateDMN>VALIDATE_MODEL</validateDMN> </configuration> </plugin>
Configuration to disable all DMN validation
<plugin> <groupId>org.kie</groupId> <artifactId>kie-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <validateDMN>disable</validateDMN> </configuration> </plugin>
If you enter an unrecognized <validateDMN>
configuration flag, all pre-compilation validation is disabled and the Maven plugin emits related log messages.
Chapter 6. Creating and editing DMN models in Business Central
You can use the DMN designer in Business Central to design DMN decision requirements diagrams (DRDs) and define decision logic for a complete and functional DMN decision model. Red Hat Decision Manager provides design support for DMN 1.2 models at conformance level 3, and includes enhancements and fixes to FEEL and DMN model components to optimize the experience of implementing DMN decision services with Red Hat Decision Manager. Red Hat Decision Manager also provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3, but any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import into Business Central, open in the DMN designer, and save are converted to DMN 1.2 models.
Procedure
- In Business Central, go to Menu → Design → Projects and click the project name.
Create or import a DMN file in your Business Central project.
To create a DMN file, click Add Asset → DMN, enter an informative DMN model name, select the appropriate Package, and click Ok.
To import an existing DMN file, click Import Asset, enter the DMN model name, select the appropriate Package, select the DMN file to upload, and click Ok.
The new DMN file is now listed in the DMN panel of the Project Explorer, and the DMN decision requirements diagram (DRD) canvas appears.
NoteIf you imported a DMN file that does not contain layout information, the imported decision requirements diagram (DRD) is formatted automatically in the DMN designer. Click Save in the DMN designer to save the DRD layout.
If an imported DRD is not automatically formatted, you can select the Perform automatic layout icon in the upper-right toolbar in the DMN designer to format the DRD.
Begin adding components to your new or imported DMN decision requirements diagram (DRD) by clicking and dragging one of the DMN nodes from the left toolbar:
Figure 6.1. Adding DRD components
The following DRD components are available:
- Decision: Use this node for a DMN decision, where one or more input elements determine an output based on defined decision logic.
- Business knowledge model: Use this node for reusable functions with one or more decision elements. Decisions that have the same logic but depend on different sub-input data or sub-decisions use business knowledge models to determine which procedure to follow.
- Knowledge source: Use this node for external authorities, documents, committees, or policies that regulate a decision or business knowledge model. Knowledge sources are references to real-world factors rather than executable business rules.
- Input data: Use this node for information used in a decision node or a business knowledge model. Input data usually includes business-level concepts or objects relevant to the business, such as loan applicant data used in a lending strategy.
- Text annotation: Use this node for explanatory notes associated with an input data node, decision node, business knowledge model, or knowledge source.
- Decision service: Use this node to enclose a set of reusable decisions implemented as a decision service for invocation. A decision service can be used in other DMN models and can be invoked from an external application or a BPMN business process.
- In the DMN designer canvas, double-click the new DRD node to enter an informative node name.
If the node is a decision or business knowledge model, select the node to display the node options and click the Edit icon to open the DMN boxed expression designer to define the decision logic for the node:
Figure 6.2. Opening a new decision node boxed expression
Figure 6.3. Opening a new business knowledge model boxed expression
By default, all business knowledge models are defined as boxed function expressions containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type.
For decision nodes, you click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.
Figure 6.4. Selecting the logic type for a decision node
For business knowledge models, you click the top-left function cell to select the function type, or right-click the function value cell, select Clear, and select a boxed expression of another type.
Figure 6.5. Selecting the function or other logic type for a business knowledge model
In the selected boxed expression designer for either a decision node (any expression type) or business knowledge model (function expression), click the applicable table cells to define the table name, variable data types, variable names and values, function parameters and bindings, or FEEL expressions to include in the decision logic.
You can right-click cells for additional actions where applicable, such as inserting or removing table rows and columns or clearing table contents.
The following is an example decision table for a decision node that determines credit score ratings based on a defined range of a loan applicant’s credit score:
Figure 6.6. Decision node decision table for credit score rating
The following is an example boxed function expression for a business knowledge model that calculates mortgage payments based on principal, interest, taxes, and insurance (PITI) as a literal expression:
Figure 6.7. Business knowledge model function for PITI calculation
- After you define the decision logic for the selected node, click Back to "<MODEL_NAME>" to return to the DRD view.
For the selected DRD node, use the available connection options to create and connect to the next node in the DRD, or click and drag a new node onto the DRD canvas from the left toolbar.
The node type determines which connection options are supported. For example, an Input data node can connect to a decision node, knowledge source, or text annotation using the applicable connection type, whereas a Knowledge source node can connect to any DRD element. A Decision node can connect only to another decision or a text annotation.
The following connection types are available, depending on the node type:
- Information requirement: Use this connection from an input data node or decision node to another decision node that requires the information.
- Knowledge requirement: Use this connection from a business knowledge model to a decision node or to another business knowledge model that invokes the decision logic.
- Authority requirement: Use this connection from an input data node or a decision node to a dependent knowledge source or from a knowledge source to a decision node, business knowledge model, or another knowledge source.
- Association: Use this connection from an input data node, decision node, business knowledge model, or knowledge source to a text annotation.
Figure 6.8. Connecting credit score input to the credit score rating decision
Continue adding and defining the remaining DRD components of your decision model. Periodically click Save in the DMN designer to save your work.
NoteAs you periodically save a DRD, the DMN designer performs a static validation of the DMN model and might produce error messages until the model is defined completely. After you finish defining the DMN model completely, if any errors remain, troubleshoot the specified problems accordingly.
After you add and define all components of the DRD, click Save to save and validate the completed DRD.
To adjust the DRD layout, you can select the Perform automatic layout icon in the upper-right toolbar of the DMN designer.
The following is an example DRD for a loan prequalification decision model:
Figure 6.9. Completed DRD for loan prequalification
The following is an example DRD for a phone call handling decision model using a reusable decision service:
Figure 6.10. Completed DRD for phone call handling with a decision service
In a DMN decision service node, the decision nodes in the bottom segment incorporate input data from outside of the decision service to arrive at a final decision in the top segment of the decision service node. The resulting top-level decisions from the decision service are then implemented in any subsequent decisions or business knowledge requirements of the DMN model. You can reuse DMN decision services in other DMN models to apply the same decision logic with different input data and different outgoing connections.
6.1. Defining DMN decision logic in boxed expressions in Business Central
Boxed expressions in DMN are tables that you use to define the underlying logic of decision nodes and business knowledge models in a decision requirements diagram (DRD). Some boxed expressions can contain other boxed expressions, but the top-level boxed expression corresponds to the decision logic of a single DRD artifact. While DRDs represent the flow of a DMN decision model, boxed expressions define the actual decision logic of individual nodes. DRDs and boxed expressions together form a complete and functional DMN decision model.
You can use the DMN designer in Business Central to define decision logic for your DRD components using built-in boxed expressions.
Prerequisites
- A DMN file is created or imported in Business Central.
Procedure
- In Business Central, go to Menu → Design → Projects, click the project name, and select the DMN file you want to modify.
In the DMN designer canvas, select a decision node or business knowledge model node that you want to define and click the Edit icon to open the DMN boxed expression designer:
Figure 6.11. Opening a new decision node boxed expression
Figure 6.12. Opening a new business knowledge model boxed expression
By default, all business knowledge models are defined as boxed function expressions containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type.
For decision nodes, you click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.
Figure 6.13. Selecting the logic type for a decision node
For business knowledge model nodes, you click the top-left function cell to select the function type, or right-click the function value cell, select Clear, and select a boxed expression of another type.
Figure 6.14. Selecting the function or other logic type for a business knowledge model
For this example, use a decision node and select Decision Table as the boxed expression type.
A decision table in DMN is a visual representation of one or more rules in a tabular format. Each rule consists of a single row in the table, and includes columns that define the conditions (input) and outcome (output) for that particular row.
-
Click the input column header to define the name and data type for the input condition. For example, name the input column Credit Score.FICO with a
number
data type. This column specifies numeric credit score values or ranges of loan applicants. Click the output column header to define the name and data type for the output values. For example, name the output column Credit Score Rating and next to the Data Type option, click Manage to go to the Data Types page where you can create a custom data type with score ratings as constraints.
Figure 6.15. Managing data types for a column header value
On the Data Types page, click New Data Type to add a new data type or click Import Data Object to import an existing data object from your project that you want to use as a DMN data type.
If you import a data object from your project as a DMN data type and then that object is updated, you must re-import the data object as a DMN data type to apply the changes in your DMN model.
For this example, click New Data Type and create a Credit_Score_Rating data type as a
string
:Figure 6.16. Adding a new data type
Click Add Constraints, select Enumeration from the drop-down options, and add the following constraints:
-
"Excellent"
-
"Good"
-
"Fair"
-
"Poor"
-
"Bad"
Figure 6.17. Adding constraints to the new data type
To change the order of data type constraints, you can click the left end of the constraint row and drag the row as needed:
Figure 6.18. Dragging constraints to change constraint order
For information about constraint types and syntax requirements for the specified data type, see the Decision Model and Notation specification.
-
- Click OK to save the constraints and click the check mark to the right of the data type to save the data type.
- Return to the Credit Score Rating decision table, click the Credit Score Rating column header, and set the data type to this new custom data type.
Use the Credit Score.FICO input column to define credit score values or ranges of values, and use the Credit Score Rating column to specify one of the corresponding ratings you defined in the Credit_Score_Rating data type.
Right-click any value cell to insert or delete rows (rules) or columns (clauses).
Figure 6.19. Decision node decision table for credit score rating
After you define all rules, click the top-left corner of the decision table to define the rule Hit Policy and Builtin Aggregator (for COLLECT hit policy only).
The hit policy determines how to reach an outcome when multiple rules in a decision table match the provided input values. The built-in aggregator determines how to aggregate rule values when you use the COLLECT hit policy.
Figure 6.20. Defining the decision table hit policy
The following example is a more complex decision table that determines applicant qualification for a loan as the concluding decision node in the same loan prequalification decision model:
Figure 6.21. Decision table for loan prequalification
For boxed expression types other than decision tables, you follow these guidelines similarly to navigate the boxed expression tables and define variables and parameters for decision logic, but according to the requirements of the boxed expression type. Some boxed expressions, such as boxed literal expressions, can be single-column tables, while other boxed expressions, such as function, context, and invocation expressions, can be multi-column tables with nested boxed expressions of other types.
For example, the following boxed context expression defines the parameters that determine whether a loan applicant can meet minimum mortgage payments based on principal, interest, taxes, and insurance (PITI), represented as a front-end ratio calculation with a sub-context expression:
Figure 6.22. Boxed context expression for front-end client PITI ratio
The following boxed function expression determines a monthly mortgage installment as a business knowledge model in a lending decision, with the function value defined as a nested context expression:
Figure 6.23. Boxed function expression for installment calculation in business knowledge model
For more information and examples of each boxed expression type, see Section 4.4, “DMN decision logic in boxed expressions”.
6.2. Creating custom data types for DMN boxed expressions in Business Central
In DMN boxed expressions in Business Central, data types determine the structure of the data that you use within an associated table, column, or field in the boxed expression. You can use default DMN data types (such as String, Number, Boolean) or you can create custom data types to specify additional fields and constraints that you want to implement for the boxed expression values.
Custom data types that you create for a boxed expression can be simple or structured:
-
Simple data types have only a name and a type assignment. Example:
Age (number)
. -
Structured data types contain multiple fields associated with a parent data type. Example: A single type
Person
containing the fieldsName (string)
,Age (number)
,Email (string)
.
Prerequisites
- A DMN file is created or imported in Business Central.
Procedure
- In Business Central, go to Menu → Design → Projects, click the project name, and select the DMN file you want to modify.
- In the DMN designer canvas, select a decision node or business knowledge model for which you want to define the data types and click the Edit icon to open the DMN boxed expression designer.
If the boxed expression is for a decision node that is not yet defined, click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.
Figure 6.24. Selecting the logic type for a decision node
Click the cell for the table header, column header, or parameter field (depending on the boxed expression type) for which you want to define the data type and click Manage to go to the Data Types page where you can create a custom data type.
Figure 6.25. Managing data types for a column header value
You can also set and manage custom data types for a specified decision node or business knowledge model node by selecting the Properties icon in the upper-right corner of the DMN designer:
Figure 6.26. Managing data types in decision requirements diagram (DRD) properties
The data type that you define for a specified cell in a boxed expression determines the structure of the data that you use within that associated table, column, or field in the boxed expression.
In this example, an output column Credit Score Rating for a DMN decision table defines a set of custom credit score ratings based on an applicant’s credit score.
On the Data Types page, click New Data Type to add a new data type or click Import Data Object to import an existing data object from your project that you want to use as a DMN data type.
If you import a data object from your project as a DMN data type and then that object is updated, you must re-import the data object as a DMN data type to apply the changes in your DMN model.
For this example, click New Data Type and create a Credit_Score_Rating data type as a
string
:Figure 6.27. Adding a new data type
If the data type requires a list of items, enable the List setting.
Click Add Constraints, select Enumeration from the drop-down options, and add the following constraints:
-
"Excellent"
-
"Good"
-
"Fair"
-
"Poor"
-
"Bad"
Figure 6.28. Adding constraints to the new data type
To change the order of data type constraints, you can click the left end of the constraint row and drag the row as needed:
Figure 6.29. Dragging constraints to change constraint order
For information about constraint types and syntax requirements for the specified data type, see the Decision Model and Notation specification.
-
- Click OK to save the constraints and click the check mark to the right of the data type to save the data type.
Return to the Credit Score Rating decision table, click the Credit Score Rating column header, set the data type to this new custom data type, and define the rule values for that column with the rating constraints that you specified.
Figure 6.30. Decision table for credit score rating
In the DMN decision model for this scenario, the Credit Score Rating decision flows into the following Loan Prequalification decision that also requires custom data types:
Figure 6.31. Decision table for loan prequalification
Continuing with this example, return to the Data Types window, click New Data Type, and create a Loan_Qualification data type as a
Structure
with no constraints.When you save the new structured data type, the first sub-field appears so that you can begin defining nested data fields in this parent data type. You can use these sub-fields in association with the parent structured data type in boxed expressions, such as nested column headers in decision tables or nested table parameters in context or function expressions.
For additional sub-fields, select the addition icon next to the Loan_Qualification data type:
Figure 6.32. Adding a new structured data type with nested fields
For this example, under the structured Loan_Qualification data type, add a Qualification field with
"Qualified"
and"Not Qualified"
enumeration constraints, and a Reason field with no constraints. Add also a simple Back_End_Ratio and a Front_End_Ratio data type, both with"Sufficient"
and"Insufficient"
enumeration constraints.Click the check mark to the right of each data type that you create to save your changes.
Figure 6.33. Adding nested data types with constraints
To change the order or nesting of data types, you can click the left end of the data type row and drag the row as needed:
Figure 6.34. Dragging data types to change data type order or nesting
Return to the decision table and, for each column, click the column header cell, set the data type to the new corresponding custom data type, and define the rule values as needed for the column with the constraints that you specified, if applicable.
Figure 6.35. Decision table for loan prequalification
For boxed expression types other than decision tables, you follow these guidelines similarly to navigate the boxed expression tables and define custom data types as needed.
For example, the following boxed function expression uses custom tCandidate
and tProfile
structured data types to associate data for online dating compatibility:
Figure 6.36. Boxed function expression for online dating compatibility
Figure 6.37. Custom data type definitions for online dating compatibility
Figure 6.38. Parameter definitions with custom data types for online dating compatibility
6.3. Included models in DMN files in Business Central
In the DMN designer in Business Central, you can use the Included Models tab to include other DMN models and Predictive Model Markup Language (PMML) models from your project in a specified DMN file. When you include a DMN model within another DMN file, you can use all of the nodes and logic from both models in the same decision requirements diagram (DRD). When you include a PMML model within a DMN file, you can invoke that PMML model as a boxed function expression for a DMN decision node or business knowledge model node.
You cannot include DMN or PMML models from other projects in Business Central.
6.3.1. Including other DMN models within a DMN file in Business Central
In Business Central, you can include other DMN models from your project in a specified DMN file. When you include a DMN model within another DMN file, you can use all of the nodes and logic from both models in the same decision requirements diagram (DRD), but you cannot edit the nodes from the included model. To edit nodes from included models, you must update the source file for the included model directly. If you update the source file for an included DMN model, open the DMN file where the DMN model is included (or close an re-open) to verify the changes.
You cannot include DMN models from other projects in Business Central.
Prerequisites
-
The DMN models are created or imported (as
.dmn
files) in the same project in Business Central as the DMN file in which you want to include the models.
Procedure
- In Business Central, go to Menu → Design → Projects, click the project name, and select the DMN file you want to modify.
- In the DMN designer, click the Included Models tab.
Click Include Model, select a DMN model from your project in the Models list, enter a unique name for the included model, and click Include:
Figure 6.39. Including a DMN model
The DMN model is added to this DMN file, and all DRD nodes from the included model are listed under Decision Components in the Decision Navigator view:
Figure 6.40. DMN file with decision components from the included DMN model
All data types from the included model are also listed in read-only mode in the Data Types tab for the DMN file:
Figure 6.41. DMN file with data types from the included DMN model
In the Model tab of the DMN designer, click and drag the included DRD components onto the canvas to begin implementing them in your DRD:
Figure 6.42. Adding DRD components from the included DMN model
To edit DRD nodes or data types from included models, you must update the source file for the included model directly. If you update the source file for an included DMN model, open the DMN file where the DMN model is included (or close an re-open) to verify the changes.
To edit the included model name or to remove the included model from the DMN file, use the Included Models tab in the DMN designer.
ImportantWhen you remove an included model, any nodes from that included model that are currently used in the DRD are also removed.
6.3.2. Including PMML models within a DMN file in Business Central
In Business Central, you can include Predictive Model Markup Language (PMML) models from your project in a specified DMN file. When you include a PMML model within a DMN file, you can invoke that PMML model as a boxed function expression for a DMN decision node or business knowledge model node. If you update the source file for an included PMML model, you must remove and re-include the PMML model in the DMN file to apply the source changes.
You cannot include PMML models from other projects in Business Central.
Prerequisites
-
The PMML models are imported (as
.pmml
files) in the same project in Business Central as the DMN file in which you want to include the models.
Procedure
In your DMN project, add the following dependencies to the project
pom.xml
file to enable PMML evaluation:<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml</artifactId> <version>${rhpam.version}</version> <scope>provided</scope> </dependency> <!-- Alternative dependencies for JPMML Evaluator, override `kie-pmml` dependency --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-dmn-jpmml</artifactId> <version>${rhpam.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jpmml</groupId> <artifactId>pmml-evaluator</artifactId> <version>1.5.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jpmml</groupId> <artifactId>pmml-evaluator-extension</artifactId> <version>1.5.1</version> <scope>provided</scope> </dependency>
To access the project
pom.xml
file in Business Central, you can select any existing asset in the project and then in the Project Explorer menu on the left side of the screen, click the Customize View gear icon and select Repository View → pom.xml.If you want to use the full PMML specification implementation with the Java Evaluator API for PMML (JPMML), use the alternative set of JPMML dependencies in your DMN project. If the JPMML dependencies and the standard
kie-pmml
dependency are both present, thekie-pmml
dependency is disabled. For information about JPMML licensing terms, see Openscoring.io.ImportantThe legacy
kie-pmml
dependency is deprecated with Red Hat Decision Manager 7.10.0 and will be replaced bykie-pmml-trusty
dependency in a future Red Hat Decision Manager release.NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency todependencyManagement
section of your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
If you added the JPMML dependencies in your DMN project to use the JPMML Evaluator, download the following JAR files and add them to the
~/kie-server.war/WEB-INF/lib
and~/business-central.war/WEB-INF/lib
directories in your Red Hat Decision Manager distribution:-
kie-dmn-jpmml
JAR file in the Red Hat Process Automation Manager 7.13.5 Maven Repository distribution (rhpam-7.13.5-maven-repository/maven-repository/org/kie/kie-dmn-jpmml/7.67.0.Final-redhat-00024/kie-dmn-jpmml-7.67.0.Final-redhat-00024.jar
) from the Red Hat Customer Portal - JPMML Evaluator 1.5.1 JAR file from the online Maven repository
- JPMML Evaluator Extensions 1.5.1 JAR file from the online Maven repository
These artifacts are required to enable JPMML evaluation in KIE Server and Business Central.
ImportantRed Hat supports integration with the Java Evaluator API for PMML (JPMML) for PMML execution in Red Hat Decision Manager. However, Red Hat does not support the JPMML libraries directly. If you include JPMML libraries in your Red Hat Decision Manager distribution, see the Openscoring.io licensing terms for JPMML.
-
- In Business Central, go to Menu → Design → Projects, click the project name, and select the DMN file you want to modify.
- In the DMN designer, click the Included Models tab.
Click Include Model, select a PMML model from your project in the Models list, enter a unique name for the included model, and click Include:
Figure 6.43. Including a PMML model
The PMML model is added to this DMN file:
Figure 6.44. DMN file with included PMML model
In the Model tab of the DMN designer, select or create the decision node or business knowledge model node in which you want to invoke the PMML model and click the Edit icon to open the DMN boxed expression designer:
Figure 6.45. Opening a new decision node boxed expression
Figure 6.46. Opening a new business knowledge model boxed expression
- Set the expression type to Function (default for business knowledge model nodes), click the top-left function cell, and select PMML.
In the document and model rows in the table, double-click the undefined cells to specify the included PMML document and the relevant PMML model within that document:
Figure 6.47. Adding a PMML model in a DMN business knowledge model
Figure 6.48. Example PMML definition in a DMN business knowledge model
If you update the source file for an included PMML model, you must remove and re-include the PMML model in the DMN file to apply the source changes.
To edit the included model name or to remove the included model from the DMN file, use the Included Models tab in the DMN designer.
6.4. Creating DMN models with multiple diagrams in Business Central
For complex DMN models, you can use the DMN designer in Business Central to design multiple DMN decision requirements diagrams (DRDs) that represent parts of the overall decision requirements graph (DRG) for the DMN decision model. In simple cases, you can use a single DRD to represent all of the overall DRG for the decision model, but in complex cases, a single DRD can become large and difficult to follow. Therefore, to better organize DMN decision models with many decision requirements, you can divide the model into smaller nested DRDs that constitute the larger central DRD representation of the overall DRG.
Prerequisites
- You understand how to design DRDs in Business Central. For information about creating DRDs, see Chapter 6, Creating and editing DMN models in Business Central.
Procedure
- In Business Central, navigate to your DMN project and create or import a DMN file in the project.
- Open the new or imported DMN file to view the DRD in the DMN designer, and begin designing or modifying the DRD using the DMN nodes in the left toolbar.
For any DMN nodes that you want to define in a separate nested DRD, select the node, click the DRD Actions icon, and select from the available options.
Figure 6.49. DRD actions icon for subdividing a DRD
The following options are available:
- Create: Use this option to create a nested DRD where you can separately define the DMN components and diagram for the selected node.
- Add to: If you already created a nested DRD, use this option to add the selected node to an existing DRD.
- Remove: If the node that you selected is already within a nested DRD, use this option to remove the node from that nested DRD.
After you create a nested DRD within your DMN decision model, the new DRD opens in a separate DRD canvas and the available DRD and components are listed in the Decision Navigator left menu. You can use the Decision Navigator menu to rename or remove a nested DRD.
Figure 6.50. Rename new nested DRD in the Decision Navigator menu
- In the separate canvas for the new nested DRD, design the flow and logic for all required components in this portion of the DMN model, as usual.
Continue adding and defining any other nested DRDs for your decision model and save the completed DMN file.
For example, the following DRD for a loan prequalification decision model contains all DMN components for the model without any nested DRDs. This example relies on the single DRD for all components and logic, resulting in a large and complex diagram.
Figure 6.51. Single DRD for loan prequalification
Alternatively, by following the steps in this procedure, you can divide this example DRD into multiple nested DRDs to better organize the decision requirements, as shown in the following example:
Figure 6.52. Multiple nested DRDs for loan prequalification
Figure 6.53. Overview of front end ratio DRD
Figure 6.54. DRD for front end ratio
Figure 6.55. Overview of credit score rating DRD
Figure 6.56. DRD for credit score rating
Figure 6.57. Overview of back end ratio DRD
Figure 6.58. DRD for back end ratio
6.5. DMN model documentation in Business Central
In the DMN designer in Business Central, you can use the Documentation tab to generate a report of your DMN model that you can print or download as an HTML file for offline use. The DMN model report contains all decision requirements diagrams (DRDs), data types, and boxed expressions in your DMN model. You can use this report to share your DMN model details or as part of your internal reporting workflow.
Figure 6.59. Example DMN model report
Chapter 7. DMN model execution
You can create or import DMN files in your Red Hat Decision Manager project using Business Central or package the DMN files as part of your project knowledge JAR (KJAR) file without Business Central. After you implement your DMN files in your Red Hat Decision Manager project, you can execute the DMN decision service by deploying the KIE container that contains it to KIE Server for remote access or by manipulating the KIE container directly as a dependency of the calling application. Other options for creating and deploying DMN knowledge packages are also available, and most are similar for all types of knowledge assets, such as DRL files or process definitions.
For information about including external DMN assets with your project packaging and deployment method, see Packaging and deploying an Red Hat Decision Manager project.
7.1. Embedding a DMN call directly in a Java application
A KIE container is local when the knowledge assets are either embedded directly into the calling program or are physically pulled in using Maven dependencies for the KJAR. You typically embed knowledge assets directly into a project if there is a tight relationship between the version of the code and the version of the DMN definition. Any changes to the decision take effect after you have intentionally updated and redeployed the application. A benefit of this approach is that proper operation does not rely on any external dependencies to the run time, which can be a limitation of locked-down environments.
Using Maven dependencies enables further flexibility because the specific version of the decision can dynamically change, (for example, by using a system property), and it can be periodically scanned for updates and automatically updated. This introduces an external dependency on the deploy time of the service, but executes the decision locally, reducing reliance on an external service being available during run time.
Prerequisites
You have built the DMN project as a KJAR artifact and deployed it to a Maven repository, or you have included your DMN assets as part of your project classpath:
mvn clean install
For more information about project packaging and deployment and executable models, see Packaging and deploying an Red Hat Decision Manager project.
Procedure
In your client application, add the following dependencies to the relevant classpath of your Java project:
<!-- Required for the DMN runtime API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-dmn-core</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
The
<version>
is the Maven artifact version for Red Hat Decision Manager currently used in your project (for example, 7.67.0.Final-redhat-00024).NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency to your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
Create a KIE container from
classpath
orReleaseId
:KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
Alternative option:
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
Obtain
DMNRuntime
from the KIE container and a reference to the DMN model to be evaluated, by using the modelnamespace
andmodelName
:DMNRuntime dmnRuntime = KieRuntimeFactory.of(kieContainer.getKieBase()).get(DMNRuntime.class); String namespace = "http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a"; String modelName = "dmn-movieticket-ageclassification"; DMNModel dmnModel = dmnRuntime.getModel(namespace, modelName);
Execute the decision services for the desired model:
DMNContext dmnContext = dmnRuntime.newContext(); 1 for (Integer age : Arrays.asList(1,12,13,64,65,66)) { dmnContext.set("Age", age); 2 DMNResult dmnResult = dmnRuntime.evaluateAll(dmnModel, dmnContext); 3 for (DMNDecisionResult dr : dmnResult.getDecisionResults()) { 4 log.info("Age: " + age + ", " + "Decision: '" + dr.getDecisionName() + "', " + "Result: " + dr.getResult()); } }
- 1
- Instantiate a new DMN Context to be the input for the model evaluation. Note that this example is looping through the Age Classification decision multiple times.
- 2
- Assign input variables for the input DMN context.
- 3
- Evaluate all DMN decisions defined in the DMN model.
- 4
- Each evaluation may result in one or more results, creating the loop.
This example prints the following output:
Age 1 Decision 'AgeClassification' : Child Age 12 Decision 'AgeClassification' : Child Age 13 Decision 'AgeClassification' : Adult Age 64 Decision 'AgeClassification' : Adult Age 65 Decision 'AgeClassification' : Senior Age 66 Decision 'AgeClassification' : Senior
If the DMN model was not previously compiled as an executable model for more efficient execution, you can enable the following property when you execute your DMN models:
-Dorg.kie.dmn.compiler.execmodel=true
7.2. Executing a DMN service using the KIE Server Java client API
The KIE Server Java client API provides a lightweight approach to invoking a remote DMN service either through the REST or JMS interfaces of KIE Server. This approach reduces the number of runtime dependencies necessary to interact with a KIE base. Decoupling the calling code from the decision definition also increases flexibility by enabling them to iterate independently at the appropriate pace.
For more information about the KIE Server Java client API, see Interacting with Red Hat Decision Manager using KIE APIs.
Prerequisites
-
KIE Server is installed and configured, including a known user name and credentials for a user with the
kie-server
role. For installation options, see Planning a Red Hat Decision Manager installation. You have built the DMN project as a KJAR artifact and deployed it to KIE Server:
mvn clean install
For more information about project packaging and deployment and executable models, see Packaging and deploying an Red Hat Decision Manager project.
- You have the ID of the KIE container containing the DMN model. If more than one model is present, you must also know the model namespace and model name of the relevant model.
Procedure
In your client application, add the following dependency to the relevant classpath of your Java project:
<!-- Required for the KIE Server Java client API --> <dependency> <groupId>org.kie.server</groupId> <artifactId>kie-server-client</artifactId> <version>${rhpam.version}</version> </dependency>
The
<version>
is the Maven artifact version for Red Hat Decision Manager currently used in your project (for example, 7.67.0.Final-redhat-00024).NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency to your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
Instantiate a
KieServicesClient
instance with the appropriate connection information.Example:
KieServicesConfiguration conf = KieServicesFactory.newRestConfiguration(URL, USER, PASSWORD); 1 conf.setMarshallingFormat(MarshallingFormat.JSON); 2 KieServicesClient kieServicesClient = KieServicesFactory.newKieServicesClient(conf);
- 1
- The connection information:
-
Example URL:
http://localhost:8080/kie-server/services/rest/server
-
The credentials should reference a user with the
kie-server
role.
-
Example URL:
- 2
- The Marshalling format is an instance of
org.kie.server.api.marshalling.MarshallingFormat
. It controls whether the messages will be JSON or XML. Options for Marshalling format are JSON, JAXB, or XSTREAM.
Obtain a
DMNServicesClient
from the KIE server Java client connected to the related KIE Server by invoking the methodgetServicesClient()
on the KIE server Java client instance:DMNServicesClient dmnClient = kieServicesClient.getServicesClient(DMNServicesClient.class );
The
dmnClient
can now execute decision services on KIE Server.Execute the decision services for the desired model.
Example:
for (Integer age : Arrays.asList(1,12,13,64,65,66)) { DMNContext dmnContext = dmnClient.newContext(); 1 dmnContext.set("Age", age); 2 ServiceResponse<DMNResult> serverResp = 3 dmnClient.evaluateAll($kieContainerId, $modelNamespace, $modelName, dmnContext); DMNResult dmnResult = serverResp.getResult(); 4 for (DMNDecisionResult dr : dmnResult.getDecisionResults()) { log.info("Age: " + age + ", " + "Decision: '" + dr.getDecisionName() + "', " + "Result: " + dr.getResult()); } }
- 1
- Instantiate a new DMN Context to be the input for the model evaluation. Note that this example is looping through the Age Classification decision multiple times.
- 2
- Assign input variables for the input DMN Context.
- 3
- Evaluate all the DMN Decisions defined in the DMN model:
-
$kieContainerId
is the ID of the container where the KJAR containing the DMN model is deployed -
$modelNamespace
is the namespace for the model. -
$modelName
is the name for the model.
-
- 4
- The DMN Result object is available from the server response.
At this point, the
dmnResult
contains all the decision results from the evaluated DMN model.You can also execute only a specific DMN decision in the model by using alternative methods of the
DMNServicesClient
.NoteIf the KIE container only contains one DMN model, you can omit
$modelNamespace
and$modelName
because the KIE Server API selects it by default.
7.3. Executing a DMN service using the KIE Server REST API
Directly interacting with the REST endpoints of KIE Server provides the most separation between the calling code and the decision logic definition. The calling code is completely free of direct dependencies, and you can implement it in an entirely different development platform such as Node.js
or .NET
. The examples in this section demonstrate Nix-style curl commands but provide relevant information to adapt to any REST client.
When you use a REST endpoint of KIE Server, the best practice is to define a domain object POJO Java class, annotated with standard KIE Server marshalling annotations. For example, the following code is using a domain object Person
class that is annotated properly:
Example POJO Java class
@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) public class Person implements java.io.Serializable { static final long serialVersionUID = 1L; private java.lang.String id; private java.lang.String name; @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(org.kie.internal.jaxb.LocalDateXmlAdapter.class) private java.time.LocalDate dojoining; public Person() { } public java.lang.String getId() { return this.id; } public void setId(java.lang.String id) { this.id = id; } public java.lang.String getName() { return this.name; } public void setName(java.lang.String name) { this.name = name; } public java.time.LocalDate getDojoining() { return this.dojoining; } public void setDojoining(java.time.LocalDate dojoining) { this.dojoining = dojoining; } public Person(java.lang.String id, java.lang.String name, java.time.LocalDate dojoining) { this.id = id; this.name = name; this.dojoining = dojoining; } }
For more information about the KIE Server REST API, see Interacting with Red Hat Decision Manager using KIE APIs.
Prerequisites
-
KIE Server is installed and configured, including a known user name and credentials for a user with the
kie-server
role. For installation options, see Planning a Red Hat Decision Manager installation. You have built the DMN project as a KJAR artifact and deployed it to KIE Server:
mvn clean install
For more information about project packaging and deployment and executable models, see Packaging and deploying an Red Hat Decision Manager project.
- You have the ID of the KIE container containing the DMN model. If more than one model is present, you must also know the model namespace and model name of the relevant model.
Procedure
Determine the base URL for accessing the KIE Server REST API endpoints. This requires knowing the following values (with the default local deployment values as an example):
-
Host (
localhost
) -
Port (
8080
) -
Root context (
kie-server
) -
Base REST path (
services/rest/
)
Example base URL in local deployment:
http://localhost:8080/kie-server/services/rest/
-
Host (
Determine user authentication requirements.
When users are defined directly in the KIE Server configuration, HTTP Basic authentication is used and requires the user name and password. Successful requests require that the user have the
kie-server
role.The following example demonstrates how to add credentials to a curl request:
curl -u username:password <request>
If KIE Server is configured with Red Hat Single Sign-On, the request must include a bearer token:
curl -H "Authorization: bearer $TOKEN" <request>
Specify the format of the request and response. The REST API endpoints work with both JSON and XML formats and are set using request headers:
JSON
curl -H "accept: application/json" -H "content-type: application/json"
XML
curl -H "accept: application/xml" -H "content-type: application/xml"
Optional: Query the container for a list of deployed decision models:
[GET]
server/containers/{containerId}/dmn
Example curl request:
curl -u krisv:krisv -H "accept: application/xml" -X GET "http://localhost:8080/kie-server/services/rest/server/containers/MovieDMNContainer/dmn"
Sample XML output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <response type="SUCCESS" msg="OK models successfully retrieved from container 'MovieDMNContainer'"> <dmn-model-info-list> <model> <model-namespace>http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a</model-namespace> <model-name>dmn-movieticket-ageclassification</model-name> <model-id>_99</model-id> <decisions> <dmn-decision-info> <decision-id>_3</decision-id> <decision-name>AgeClassification</decision-name> </dmn-decision-info> </decisions> </model> </dmn-model-info-list> </response>
Sample JSON output:
{ "type" : "SUCCESS", "msg" : "OK models successfully retrieved from container 'MovieDMNContainer'", "result" : { "dmn-model-info-list" : { "models" : [ { "model-namespace" : "http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a", "model-name" : "dmn-movieticket-ageclassification", "model-id" : "_99", "decisions" : [ { "decision-id" : "_3", "decision-name" : "AgeClassification" } ] } ] } } }
Execute the model:
[POST]
server/containers/{containerId}/dmn
Example curl request:
curl -u krisv:krisv -H "accept: application/json" -H "content-type: application/json" -X POST "http://localhost:8080/kie-server/services/rest/server/containers/MovieDMNContainer/dmn" -d "{ \"model-namespace\" : \"http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a\", \"model-name\" : \"dmn-movieticket-ageclassification\", \"decision-name\" : [ ], \"decision-id\" : [ ], \"dmn-context\" : {\"Age\" : 66}}"
Example JSON request:
{ "model-namespace" : "http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a", "model-name" : "dmn-movieticket-ageclassification", "decision-name" : [ ], "decision-id" : [ ], "dmn-context" : {"Age" : 66} }
Example XML request (JAXB format):
<?xml version="1.0" encoding="UTF-8"?> <dmn-evaluation-context> <model-namespace>http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a</model-namespace> <model-name>dmn-movieticket-ageclassification</model-name> <dmn-context xsi:type="jaxbListWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <type>MAP</type> <element xsi:type="jaxbStringObjectPair" key="Age"> <value xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema">66</value> </element> </dmn-context> </dmn-evaluation-context>
NoteRegardless of the request format, the request requires the following elements:
- Model namespace
- Model name
- Context object containing input values
Example JSON response:
{ "type" : "SUCCESS", "msg" : "OK from container 'MovieDMNContainer'", "result" : { "dmn-evaluation-result" : { "messages" : [ ], "model-namespace" : "http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a", "model-name" : "dmn-movieticket-ageclassification", "decision-name" : [ ], "dmn-context" : { "Age" : 66, "AgeClassification" : "Senior" }, "decision-results" : { "_3" : { "messages" : [ ], "decision-id" : "_3", "decision-name" : "AgeClassification", "result" : "Senior", "status" : "SUCCEEDED" } } } } }
Example XML (JAXB format) response:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <response type="SUCCESS" msg="OK from container 'MovieDMNContainer'"> <dmn-evaluation-result> <model-namespace>http://www.redhat.com/_c7328033-c355-43cd-b616-0aceef80e52a</model-namespace> <model-name>dmn-movieticket-ageclassification</model-name> <dmn-context xsi:type="jaxbListWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <type>MAP</type> <element xsi:type="jaxbStringObjectPair" key="Age"> <value xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema">66</value> </element> <element xsi:type="jaxbStringObjectPair" key="AgeClassification"> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">Senior</value> </element> </dmn-context> <messages/> <decisionResults> <entry> <key>_3</key> <value> <decision-id>_3</decision-id> <decision-name>AgeClassification</decision-name> <result xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Senior</result> <messages/> <status>SUCCEEDED</status> </value> </entry> </decisionResults> </dmn-evaluation-result> </response>
7.4. REST endpoints for specific DMN models
Red Hat Decision Manager provides model-specific DMN KIE Server endpoints that you can use to interact with your specific DMN model without using the Business Central user interface.
For each DMN model in a container in Red Hat Decision Manager, the following KIE Server REST endpoints are automatically generated based on the content of the DMN model:
-
POST /server/containers/{containerId}/dmn/models/{modelname}
: A business-domain endpoint for evaluating a specified DMN model in a container -
POST /server/containers/{containerId}/dmn/models/{modelname}/{decisionServiceName}
: A business-domain endpoint for evaluating a specified decision service component in a specific DMN model available in a container -
POST /server/containers/{containerId}/dmn/models/{modelname}/dmnresult
: An endpoint for evaluating a specified DMN model containing customized body payload and returning aDMNResult
response, including business-domain context, helper messages, and helper decision pointers -
POST /server/containers/{containerId}/dmn/models/{modelname}/{decisionServiceName}/dmnresult
: An endpoint for evaluating a specified decision service component in a specific DMN model and returning aDMNResult
response, including the business-domain context, helper messages, and help decision pointers for the decision service -
GET /server/containers/{containerId}/dmn/models/{modelname}
: An endpoint for returning standard DMN XML without decision logic and containing the inputs and decisions of the specified DMN model -
GET /server/containers/{containerId}/dmn/openapi.json (|.yaml)
: An endpoint for retrieving Swagger or OAS for the DMN models in a specified container
You can use these endpoints to interact with a DMN model or a specific decision service within a model. As you decide between using business-domain and dmnresult
variants of these REST endpoints, review the following considerations:
-
REST business-domain endpoints: Use this endpoint type if a client application is only concerned with a positive evaluation outcome, is not interested in parsing
Info
orWarn
messages, and only needs an HTTP 5xx response for any errors. This type of endpoint is also helpful for single-page application-like clients, due to singleton coercion of decision service results that resemble the DMN modeling behavior. -
REST
dmnresult
endpoints: Use this endpoint type if a client needs to parseInfo
,Warn
, orError
messages in all cases.
For each endpoint, use a REST client or curl utility to send requests with the following components:
-
Base URL:
http://HOST:PORT/kie-server/services/rest/
Path parameters:
-
{containerId}
: The string identifier of the container, such asmykjar-project
-
{modelName}
: The string identifier of the DMN model, such asTraffic Violation
-
{decisionServiceName}
: The string identifier of the decision service component in the DMN DRG, such asTrafficViolationDecisionService
-
dmnresult
: The string identifier that enables the endpoint to return a fullDMNResult
response with more detailedInfo
,Warn
, andError
messaging
-
HTTP headers: For
POST
requests only:-
accept
:application/json
-
content-type
:application/json
-
-
HTTP methods:
GET
orPOST
The examples in the following endpoints are based on a mykjar-project
container that contains a Traffic Violation
DMN model, containing a TrafficViolationDecisionService
decision service component.
For all of these endpoints, if a DMN evaluation Error
message occurs, a DMNResult
response is returned along with an HTTP 5xx error. If a DMN Info
or Warn
message occurs, the relevant response is returned along with the business-domain REST body, in the X-Kogito-decision-messages
extended HTTP header, to be used for client-side business logic. When there is a requirement of more refined client-side business logic, the client can use the dmnresult
variant of the endpoints.
- Retrieve Swagger or OAS for DMN models in a specified container
GET /server/containers/{containerId}/dmn/openapi.json (|.yaml)
Example REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/openapi.json (|.yaml)
- Return the DMN XML without decision logic
GET /server/containers/{containerId}/dmn/models/{modelname}
Example REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation
Example curl request
curl -u wbadmin:wbadmin -X GET "http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic%20Violation" -H "accept: application/xml"
Example response (XML)
<?xml version='1.0' encoding='UTF-8'?> <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://kiegroup.org/dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" id="_1C792953-80DB-4B32-99EB-25FBE32BAF9E" name="Traffic Violation" expressionLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://kiegroup.org/dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF"> <dmn:extensionElements/> <dmn:itemDefinition id="_63824D3F-9173-446D-A940-6A7F0FA056BB" name="tDriver" isCollection="false"> <dmn:itemComponent id="_9DAB5DAA-3B44-4F6D-87F2-95125FB2FEE4" name="Name" isCollection="false"> <dmn:typeRef>string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_856BA8FA-EF7B-4DF9-A1EE-E28263CE9955" name="Age" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_FDC2CE03-D465-47C2-A311-98944E8CC23F" name="State" isCollection="false"> <dmn:typeRef>string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_D6FD34C4-00DC-4C79-B1BF-BBCF6FC9B6D7" name="City" isCollection="false"> <dmn:typeRef>string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_7110FE7E-1A38-4C39-B0EB-AEEF06BA37F4" name="Points" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> </dmn:itemDefinition> <dmn:itemDefinition id="_40731093-0642-4588-9183-1660FC55053B" name="tViolation" isCollection="false"> <dmn:itemComponent id="_39E88D9F-AE53-47AD-B3DE-8AB38D4F50B3" name="Code" isCollection="false"> <dmn:typeRef>string</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_1648EA0A-2463-4B54-A12A-D743A3E3EE7B" name="Date" isCollection="false"> <dmn:typeRef>date</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_9F129EAA-4E71-4D99-B6D0-84EEC3AC43CC" name="Type" isCollection="false"> <dmn:typeRef>string</dmn:typeRef> <dmn:allowedValues kie:constraintType="enumeration" id="_626A8F9C-9DD1-44E0-9568-0F6F8F8BA228"> <dmn:text>"speed", "parking", "driving under the influence"</dmn:text> </dmn:allowedValues> </dmn:itemComponent> <dmn:itemComponent id="_DDD10D6E-BD38-4C79-9E2F-8155E3A4B438" name="Speed Limit" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_229F80E4-2892-494C-B70D-683ABF2345F6" name="Actual Speed" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> </dmn:itemDefinition> <dmn:itemDefinition id="_2D4F30EE-21A6-4A78-A524-A5C238D433AE" name="tFine" isCollection="false"> <dmn:itemComponent id="_B9F70BC7-1995-4F51-B949-1AB65538B405" name="Amount" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> <dmn:itemComponent id="_F49085D6-8F08-4463-9A1A-EF6B57635DBD" name="Points" isCollection="false"> <dmn:typeRef>number</dmn:typeRef> </dmn:itemComponent> </dmn:itemDefinition> <dmn:inputData id="_1929CBD5-40E0-442D-B909-49CEDE0101DC" name="Violation"> <dmn:variable id="_C16CF9B1-5FAB-48A0-95E0-5FCD661E0406" name="Violation" typeRef="tViolation"/> </dmn:inputData> <dmn:decision id="_4055D956-1C47-479C-B3F4-BAEB61F1C929" name="Fine"> <dmn:variable id="_8C1EAC83-F251-4D94-8A9E-B03ACF6849CD" name="Fine" typeRef="tFine"/> <dmn:informationRequirement id="_800A3BBB-90A3-4D9D-BA5E-A311DED0134F"> <dmn:requiredInput href="#_1929CBD5-40E0-442D-B909-49CEDE0101DC"/> </dmn:informationRequirement> </dmn:decision> <dmn:inputData id="_1F9350D7-146D-46F1-85D8-15B5B68AF22A" name="Driver"> <dmn:variable id="_A80F16DF-0DB4-43A2-B041-32900B1A3F3D" name="Driver" typeRef="tDriver"/> </dmn:inputData> <dmn:decision id="_8A408366-D8E9-4626-ABF3-5F69AA01F880" name="Should the driver be suspended?"> <dmn:question>Should the driver be suspended due to points on his license?</dmn:question> <dmn:allowedAnswers>"Yes", "No"</dmn:allowedAnswers> <dmn:variable id="_40387B66-5D00-48C8-BB90-E83EE3332C72" name="Should the driver be suspended?" typeRef="string"/> <dmn:informationRequirement id="_982211B1-5246-49CD-BE85-3211F71253CF"> <dmn:requiredInput href="#_1F9350D7-146D-46F1-85D8-15B5B68AF22A"/> </dmn:informationRequirement> <dmn:informationRequirement id="_AEC4AA5F-50C3-4FED-A0C2-261F90290731"> <dmn:requiredDecision href="#_4055D956-1C47-479C-B3F4-BAEB61F1C929"/> </dmn:informationRequirement> </dmn:decision> <dmndi:DMNDI> <dmndi:DMNDiagram> <di:extension/> <dmndi:DMNShape id="dmnshape-_1929CBD5-40E0-442D-B909-49CEDE0101DC" dmnElementRef="_1929CBD5-40E0-442D-B909-49CEDE0101DC" isCollapsed="false"> <dmndi:DMNStyle> <dmndi:FillColor red="255" green="255" blue="255"/> <dmndi:StrokeColor red="0" green="0" blue="0"/> <dmndi:FontColor red="0" green="0" blue="0"/> </dmndi:DMNStyle> <dc:Bounds x="708" y="350" width="100" height="50"/> <dmndi:DMNLabel/> </dmndi:DMNShape> <dmndi:DMNShape id="dmnshape-_4055D956-1C47-479C-B3F4-BAEB61F1C929" dmnElementRef="_4055D956-1C47-479C-B3F4-BAEB61F1C929" isCollapsed="false"> <dmndi:DMNStyle> <dmndi:FillColor red="255" green="255" blue="255"/> <dmndi:StrokeColor red="0" green="0" blue="0"/> <dmndi:FontColor red="0" green="0" blue="0"/> </dmndi:DMNStyle> <dc:Bounds x="709" y="210" width="100" height="50"/> <dmndi:DMNLabel/> </dmndi:DMNShape> <dmndi:DMNShape id="dmnshape-_1F9350D7-146D-46F1-85D8-15B5B68AF22A" dmnElementRef="_1F9350D7-146D-46F1-85D8-15B5B68AF22A" isCollapsed="false"> <dmndi:DMNStyle> <dmndi:FillColor red="255" green="255" blue="255"/> <dmndi:StrokeColor red="0" green="0" blue="0"/> <dmndi:FontColor red="0" green="0" blue="0"/> </dmndi:DMNStyle> <dc:Bounds x="369" y="344" width="100" height="50"/> <dmndi:DMNLabel/> </dmndi:DMNShape> <dmndi:DMNShape id="dmnshape-_8A408366-D8E9-4626-ABF3-5F69AA01F880" dmnElementRef="_8A408366-D8E9-4626-ABF3-5F69AA01F880" isCollapsed="false"> <dmndi:DMNStyle> <dmndi:FillColor red="255" green="255" blue="255"/> <dmndi:StrokeColor red="0" green="0" blue="0"/> <dmndi:FontColor red="0" green="0" blue="0"/> </dmndi:DMNStyle> <dc:Bounds x="534" y="83" width="133" height="63"/> <dmndi:DMNLabel/> </dmndi:DMNShape> <dmndi:DMNEdge id="dmnedge-_800A3BBB-90A3-4D9D-BA5E-A311DED0134F" dmnElementRef="_800A3BBB-90A3-4D9D-BA5E-A311DED0134F"> <di:waypoint x="758" y="375"/> <di:waypoint x="759" y="235"/> </dmndi:DMNEdge> <dmndi:DMNEdge id="dmnedge-_982211B1-5246-49CD-BE85-3211F71253CF" dmnElementRef="_982211B1-5246-49CD-BE85-3211F71253CF"> <di:waypoint x="419" y="369"/> <di:waypoint x="600.5" y="114.5"/> </dmndi:DMNEdge> <dmndi:DMNEdge id="dmnedge-_AEC4AA5F-50C3-4FED-A0C2-261F90290731" dmnElementRef="_AEC4AA5F-50C3-4FED-A0C2-261F90290731"> <di:waypoint x="759" y="235"/> <di:waypoint x="600.5" y="114.5"/> </dmndi:DMNEdge> </dmndi:DMNDiagram> </dmndi:DMNDI>
- Evaluate a specified DMN model in a specified container
POST /server/containers/{containerId}/dmn/models/{modelname}
Example REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation
Example curl request
curl -u wbadmin:wbadmin-X POST "http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"Driver\":{\"Points\":15},\"Violation\":{\"Date\":\"2021-04-08\",\"Type\":\"speed\",\"Actual Speed\":135,\"Speed Limit\":100}}"
Example POST request body with input data
{ "Driver": { "Points": 15 }, "Violation": { "Date": "2021-04-08", "Type": "speed", "Actual Speed": 135, "Speed Limit": 100 } }
Example response (JSON)
{ "Violation": { "Type": "speed", "Speed Limit": 100, "Actual Speed": 135, "Code": null, "Date": "2021-04-08" }, "Driver": { "Points": 15, "State": null, "City": null, "Age": null, "Name": null }, "Fine": { "Points": 7, "Amount": 1000 }, "Should the driver be suspended?": "Yes" }
- Evaluate a specified decision service within a specified DMN model in a container
POST /server/containers/{containerId}/dmn/models/{modelname}/{decisionServiceName}
For this endpoint, the request body must contain all the requirements of the decision service. The response is the resulting DMN context of the decision service, including the decision values, the original input values, and all other parametric DRG components in serialized form. For example, a business knowledge model is available in string-serialized form in its signature.
If the decision service is composed of a single-output decision, the response is the resulting value of that specific decision. This behavior provides an equivalent value at the API level of a specification feature when invoking the decision service in the model itself. As a result, you can, for example, interact with a DMN decision service from single-page web applications.
Figure 7.1. Example
TrafficViolationDecisionService
decision service with single-output decisionFigure 7.2. Example
TrafficViolationDecisionService
decision service with multiple-output decisionExample REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/TrafficViolationDecisionService
Example POST request body with input data
{ "Driver": { "Points": 2 }, "Violation": { "Type": "speed", "Actual Speed": 120, "Speed Limit": 100 } }
Example curl request
curl -X POST http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/TrafficViolationDecisionService -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response for single-output decision (JSON)
"No"
Example response for multiple-output decision (JSON)
{ "Violation": { "Type": "speed", "Speed Limit": 100, "Actual Speed": 120 }, "Driver": { "Points": 2 }, "Fine": { "Points": 3, "Amount": 500 }, "Should the driver be suspended?": "No" }
- Evaluate a specified DMN model in a specified container and return a
DMNResult
response POST /server/containers/{containerId}/dmn/models/{modelname}/dmnresult
Example REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/dmnresult
Example POST request body with input data
{ "Driver": { "Points": 2 }, "Violation": { "Type": "speed", "Actual Speed": 120, "Speed Limit": 100 } }
Example curl request
curl -X POST http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/dmnresult -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response (JSON)
{ "namespace": "https://kiegroup.org/dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF", "modelName": "Traffic Violation", "dmnContext": { "Violation": { "Type": "speed", "Speed Limit": 100, "Actual Speed": 120, "Code": null, "Date": null }, "Driver": { "Points": 2, "State": null, "City": null, "Age": null, "Name": null }, "Fine": { "Points": 3, "Amount": 500 }, "Should the driver be suspended?": "No" }, "messages": [], "decisionResults": [ { "decisionId": "_4055D956-1C47-479C-B3F4-BAEB61F1C929", "decisionName": "Fine", "result": { "Points": 3, "Amount": 500 }, "messages": [], "evaluationStatus": "SUCCEEDED" }, { "decisionId": "_8A408366-D8E9-4626-ABF3-5F69AA01F880", "decisionName": "Should the driver be suspended?", "result": "No", "messages": [], "evaluationStatus": "SUCCEEDED" } ] }
- Evaluate a specified decision service within a DMN model in a specified container and return a
DMNResult
response POST /server/containers/{containerId}/dmn/models/{modelname}/{decisionServiceName}/dmnresult
Example REST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/TrafficViolationDecisionService/dmnresult
Example POST request body with input data
{ "Driver": { "Points": 2 }, "Violation": { "Type": "speed", "Actual Speed": 120, "Speed Limit": 100 } }
Example curl request
curl -X POST http://localhost:8080/kie-server/services/rest/server/containers/mykjar-project/dmn/models/Traffic Violation/TrafficViolationDecisionService/dmnresult -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response (JSON)
{ "namespace": "https://kiegroup.org/dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF", "modelName": "Traffic Violation", "dmnContext": { "Violation": { "Type": "speed", "Speed Limit": 100, "Actual Speed": 120, "Code": null, "Date": null }, "Driver": { "Points": 2, "State": null, "City": null, "Age": null, "Name": null }, "Should the driver be suspended?": "No" }, "messages": [], "decisionResults": [ { "decisionId": "_8A408366-D8E9-4626-ABF3-5F69AA01F880", "decisionName": "Should the driver be suspended?", "result": "No", "messages": [], "evaluationStatus": "SUCCEEDED" } ] }
Chapter 8. Additional resources
Part II. Designing a decision service using PMML models
As a business rules developer, you can use Predictive Model Markup Language (PMML) to define statistical or data-mining models that you can integrate with your decision services in Red Hat Decision Manager. Red Hat Decision Manager includes consumer conformance support of PMML 4.2.1 for Regression, Scorecard, Tree, and Mining models. Red Hat Decision Manager does not include a built-in PMML model editor, but you can use an XML or PMML-specific authoring tool to create PMML models and then integrate them with your Red Hat Decision Manager projects.
For more information about PMML, see the DMG PMML specification.
You can also design your decision service using Decision Model and Notation (DMN) models and include your PMML models as part of your DMN service. For information about DMN support in Red Hat Decision Manager 7.13, see the following resources:
- Getting started with decision services (step-by-step tutorial with a DMN decision service example)
- Designing a decision service using DMN models (overview of DMN support and capabilities in Red Hat Decision Manager)
Chapter 9. Decision-authoring assets in Red Hat Decision Manager
Red Hat Decision Manager supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.
The following table highlights the main decision-authoring assets supported in Red Hat Decision Manager projects to help you decide or confirm the best method for defining decisions in your decision service.
Asset | Highlights | Authoring tools | Documentation |
---|---|---|---|
Decision Model and Notation (DMN) models |
| Business Central or other DMN-compliant editor | |
Guided decision tables |
| Business Central | |
Spreadsheet decision tables |
| Spreadsheet editor | Designing a decision service using spreadsheet decision tables |
Guided rules |
| Business Central | |
Guided rule templates |
| Business Central | |
DRL rules |
| Business Central or integrated development environment (IDE) | |
Predictive Model Markup Language (PMML) models |
| PMML or XML editor |
When you define business decisions, you can also consider using Red Hat build of Kogito for your cloud-native decision services. For more information about getting started with Red Hat build of Kogito microservices, see Getting started with Red Hat build of Kogito in Red Hat Decision Manager.
Chapter 10. Predictive Model Markup Language (PMML)
Predictive Model Markup Language (PMML) is an XML-based standard established by the Data Mining Group (DMG) for defining statistical and data-mining models. PMML models can be shared between PMML-compliant platforms and across organizations so that business analysts and developers are unified in designing, analyzing, and implementing PMML-based assets and services.
For more information about the background and applications of PMML, see the DMG PMML specification.
10.1. PMML conformance levels
The PMML specification defines producer and consumer conformance levels in a software implementation to ensure that PMML models are created and integrated reliably. For the formal definitions of each conformance level, see the DMG PMML conformance page.
The following list summarizes the PMML conformance levels:
- Producer conformance
- A tool or application is producer conforming if it generates valid PMML documents for at least one type of model. Satisfying PMML producer conformance requirements ensures that a model definition document is syntactically correct and defines a model instance that is consistent with semantic criteria that are defined in model specifications.
- Consumer conformance
- An application is consumer conforming if it accepts valid PMML documents for at least one type of model. Satisfying consumer conformance requirements ensures that a PMML model created according to producer conformance can be integrated and used as defined. For example, if an application is consumer conforming for Regression model types, then valid PMML documents defining models of this type produced by different conforming producers would be interchangeable in the application.
Red Hat Decision Manager includes consumer conformance support for the following PMML model types:
- Regression models
- Scorecard models
- Tree models
-
Mining models (with sub-types
modelChain
,selectAll
, andselectFirst
) - Clustering models
For a list of all PMML model types, including those not supported in Red Hat Decision Manager, see the DMG PMML specification.
Chapter 11. PMML model examples
PMML defines an XML schema that enables PMML models to be used between different PMML-compliant platforms. The PMML specification enables multiple software platforms to work with the same file for authoring, testing, and production execution, assuming producer and consumer conformance are met.
The following are examples of PMML Regression, Scorecard, Tree, Mining, and Clustering models. These examples illustrate the supported models that you can integrate with your decision services in Red Hat Decision Manager.
For more PMML examples, see the DMG PMML Sample Files page.
Example PMML Regression model
<PMML version="4.2" xsi:schemaLocation="http://www.dmg.org/PMML-4_2 http://www.dmg.org/v4-2-1/pmml-4-2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dmg.org/PMML-4_2"> <Header copyright="JBoss"/> <DataDictionary numberOfFields="5"> <DataField dataType="double" name="fld1" optype="continuous"/> <DataField dataType="double" name="fld2" optype="continuous"/> <DataField dataType="string" name="fld3" optype="categorical"> <Value value="x"/> <Value value="y"/> </DataField> <DataField dataType="double" name="fld4" optype="continuous"/> <DataField dataType="double" name="fld5" optype="continuous"/> </DataDictionary> <RegressionModel algorithmName="linearRegression" functionName="regression" modelName="LinReg" normalizationMethod="logit" targetFieldName="fld4"> <MiningSchema> <MiningField name="fld1"/> <MiningField name="fld2"/> <MiningField name="fld3"/> <MiningField name="fld4" usageType="predicted"/> <MiningField name="fld5" usageType="target"/> </MiningSchema> <RegressionTable intercept="0.5"> <NumericPredictor coefficient="5" exponent="2" name="fld1"/> <NumericPredictor coefficient="2" exponent="1" name="fld2"/> <CategoricalPredictor coefficient="-3" name="fld3" value="x"/> <CategoricalPredictor coefficient="3" name="fld3" value="y"/> <PredictorTerm coefficient="0.4"> <FieldRef field="fld1"/> <FieldRef field="fld2"/> </PredictorTerm> </RegressionTable> </RegressionModel> </PMML>
Example PMML Scorecard model
<PMML version="4.2" xsi:schemaLocation="http://www.dmg.org/PMML-4_2 http://www.dmg.org/v4-2-1/pmml-4-2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dmg.org/PMML-4_2"> <Header copyright="JBoss"/> <DataDictionary numberOfFields="4"> <DataField name="param1" optype="continuous" dataType="double"/> <DataField name="param2" optype="continuous" dataType="double"/> <DataField name="overallScore" optype="continuous" dataType="double" /> <DataField name="finalscore" optype="continuous" dataType="double" /> </DataDictionary> <Scorecard modelName="ScorecardCompoundPredicate" useReasonCodes="true" isScorable="true" functionName="regression" baselineScore="15" initialScore="0.8" reasonCodeAlgorithm="pointsAbove"> <MiningSchema> <MiningField name="param1" usageType="active" invalidValueTreatment="asMissing"> </MiningField> <MiningField name="param2" usageType="active" invalidValueTreatment="asMissing"> </MiningField> <MiningField name="overallScore" usageType="target"/> <MiningField name="finalscore" usageType="predicted"/> </MiningSchema> <Characteristics> <Characteristic name="ch1" baselineScore="50" reasonCode="reasonCh1"> <Attribute partialScore="20"> <SimplePredicate field="param1" operator="lessThan" value="20"/> </Attribute> <Attribute partialScore="100"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="param1" operator="greaterOrEqual" value="20"/> <SimplePredicate field="param2" operator="lessOrEqual" value="25"/> </CompoundPredicate> </Attribute> <Attribute partialScore="200"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="param1" operator="greaterOrEqual" value="20"/> <SimplePredicate field="param2" operator="greaterThan" value="25"/> </CompoundPredicate> </Attribute> </Characteristic> <Characteristic name="ch2" reasonCode="reasonCh2"> <Attribute partialScore="10"> <CompoundPredicate booleanOperator="or"> <SimplePredicate field="param2" operator="lessOrEqual" value="-5"/> <SimplePredicate field="param2" operator="greaterOrEqual" value="50"/> </CompoundPredicate> </Attribute> <Attribute partialScore="20"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="param2" operator="greaterThan" value="-5"/> <SimplePredicate field="param2" operator="lessThan" value="50"/> </CompoundPredicate> </Attribute> </Characteristic> </Characteristics> </Scorecard> </PMML>
Example PMML Tree model
<PMML version="4.2" xsi:schemaLocation="http://www.dmg.org/PMML-4_2 http://www.dmg.org/v4-2-1/pmml-4-2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dmg.org/PMML-4_2"> <Header copyright="JBOSS"/> <DataDictionary numberOfFields="5"> <DataField dataType="double" name="fld1" optype="continuous"/> <DataField dataType="double" name="fld2" optype="continuous"/> <DataField dataType="string" name="fld3" optype="categorical"> <Value value="true"/> <Value value="false"/> </DataField> <DataField dataType="string" name="fld4" optype="categorical"> <Value value="optA"/> <Value value="optB"/> <Value value="optC"/> </DataField> <DataField dataType="string" name="fld5" optype="categorical"> <Value value="tgtX"/> <Value value="tgtY"/> <Value value="tgtZ"/> </DataField> </DataDictionary> <TreeModel functionName="classification" modelName="TreeTest"> <MiningSchema> <MiningField name="fld1"/> <MiningField name="fld2"/> <MiningField name="fld3"/> <MiningField name="fld4"/> <MiningField name="fld5" usageType="predicted"/> </MiningSchema> <Node score="tgtX"> <True/> <Node score="tgtX"> <SimplePredicate field="fld4" operator="equal" value="optA"/> <Node score="tgtX"> <CompoundPredicate booleanOperator="surrogate"> <SimplePredicate field="fld1" operator="lessThan" value="30.0"/> <SimplePredicate field="fld2" operator="greaterThan" value="20.0"/> </CompoundPredicate> <Node score="tgtX"> <SimplePredicate field="fld2" operator="lessThan" value="40.0"/> </Node> <Node score="tgtZ"> <SimplePredicate field="fld2" operator="greaterOrEqual" value="10.0"/> </Node> </Node> <Node score="tgtZ"> <CompoundPredicate booleanOperator="or"> <SimplePredicate field="fld1" operator="greaterOrEqual" value="60.0"/> <SimplePredicate field="fld1" operator="lessOrEqual" value="70.0"/> </CompoundPredicate> <Node score="tgtZ"> <SimpleSetPredicate booleanOperator="isNotIn" field="fld4"> <Array type="string">optA optB</Array> </SimpleSetPredicate> </Node> </Node> </Node> <Node score="tgtY"> <CompoundPredicate booleanOperator="or"> <SimplePredicate field="fld4" operator="equal" value="optA"/> <SimplePredicate field="fld4" operator="equal" value="optC"/> </CompoundPredicate> <Node score="tgtY"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="fld1" operator="greaterThan" value="10.0"/> <SimplePredicate field="fld1" operator="lessThan" value="50.0"/> <SimplePredicate field="fld4" operator="equal" value="optA"/> <SimplePredicate field="fld2" operator="lessThan" value="100.0"/> <SimplePredicate field="fld3" operator="equal" value="false"/> </CompoundPredicate> </Node> <Node score="tgtZ"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="fld4" operator="equal" value="optC"/> <SimplePredicate field="fld2" operator="lessThan" value="30.0"/> </CompoundPredicate> </Node> </Node> </Node> </TreeModel> </PMML>
Example PMML Mining model (modelChain)
<PMML version="4.2" xsi:schemaLocation="http://www.dmg.org/PMML-4_2 http://www.dmg.org/v4-2-1/pmml-4-2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dmg.org/PMML-4_2"> <Header> <Application name="Drools-PMML" version="7.0.0-SNAPSHOT" /> </Header> <DataDictionary numberOfFields="7"> <DataField name="age" optype="continuous" dataType="double" /> <DataField name="occupation" optype="categorical" dataType="string"> <Value value="SKYDIVER" /> <Value value="ASTRONAUT" /> <Value value="PROGRAMMER" /> <Value value="TEACHER" /> <Value value="INSTRUCTOR" /> </DataField> <DataField name="residenceState" optype="categorical" dataType="string"> <Value value="AP" /> <Value value="KN" /> <Value value="TN" /> </DataField> <DataField name="validLicense" optype="categorical" dataType="boolean" /> <DataField name="overallScore" optype="continuous" dataType="double" /> <DataField name="grade" optype="categorical" dataType="string"> <Value value="A" /> <Value value="B" /> <Value value="C" /> <Value value="D" /> <Value value="F" /> </DataField> <DataField name="qualificationLevel" optype="categorical" dataType="string"> <Value value="Unqualified" /> <Value value="Barely" /> <Value value="Well" /> <Value value="Over" /> </DataField> </DataDictionary> <MiningModel modelName="SampleModelChainMine" functionName="classification"> <MiningSchema> <MiningField name="age" /> <MiningField name="occupation" /> <MiningField name="residenceState" /> <MiningField name="validLicense" /> <MiningField name="overallScore" /> <MiningField name="qualificationLevel" usageType="target"/> </MiningSchema> <Segmentation multipleModelMethod="modelChain"> <Segment id="1"> <True /> <Scorecard modelName="Sample Score 1" useReasonCodes="true" isScorable="true" functionName="regression" baselineScore="0.0" initialScore="0.345"> <MiningSchema> <MiningField name="age" usageType="active" invalidValueTreatment="asMissing" /> <MiningField name="occupation" usageType="active" invalidValueTreatment="asMissing" /> <MiningField name="residenceState" usageType="active" invalidValueTreatment="asMissing" /> <MiningField name="validLicense" usageType="active" invalidValueTreatment="asMissing" /> <MiningField name="overallScore" usageType="predicted" /> </MiningSchema> <Output> <OutputField name="calculatedScore" displayName="Final Score" dataType="double" feature="predictedValue" targetField="overallScore" /> </Output> <Characteristics> <Characteristic name="AgeScore" baselineScore="0.0" reasonCode="ABZ"> <Extension name="cellRef" value="$B$8" /> <Attribute partialScore="10.0"> <Extension name="cellRef" value="$C$10" /> <SimplePredicate field="age" operator="lessOrEqual" value="5" /> </Attribute> <Attribute partialScore="30.0" reasonCode="CX1"> <Extension name="cellRef" value="$C$11" /> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="age" operator="greaterOrEqual" value="5" /> <SimplePredicate field="age" operator="lessThan" value="12" /> </CompoundPredicate> </Attribute> <Attribute partialScore="40.0" reasonCode="CX2"> <Extension name="cellRef" value="$C$12" /> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="age" operator="greaterOrEqual" value="13" /> <SimplePredicate field="age" operator="lessThan" value="44" /> </CompoundPredicate> </Attribute> <Attribute partialScore="25.0"> <Extension name="cellRef" value="$C$13" /> <SimplePredicate field="age" operator="greaterOrEqual" value="45" /> </Attribute> </Characteristic> <Characteristic name="OccupationScore" baselineScore="0.0"> <Extension name="cellRef" value="$B$16" /> <Attribute partialScore="-10.0" reasonCode="CX2"> <Extension name="description" value="skydiving is a risky occupation" /> <Extension name="cellRef" value="$C$18" /> <SimpleSetPredicate field="occupation" booleanOperator="isIn"> <Array n="2" type="string">SKYDIVER ASTRONAUT</Array> </SimpleSetPredicate> </Attribute> <Attribute partialScore="10.0"> <Extension name="cellRef" value="$C$19" /> <SimpleSetPredicate field="occupation" booleanOperator="isIn"> <Array n="2" type="string">TEACHER INSTRUCTOR</Array> </SimpleSetPredicate> </Attribute> <Attribute partialScore="5.0"> <Extension name="cellRef" value="$C$20" /> <SimplePredicate field="occupation" operator="equal" value="PROGRAMMER" /> </Attribute> </Characteristic> <Characteristic name="ResidenceStateScore" baselineScore="0.0" reasonCode="RES"> <Extension name="cellRef" value="$B$22" /> <Attribute partialScore="-10.0"> <Extension name="cellRef" value="$C$24" /> <SimplePredicate field="residenceState" operator="equal" value="AP" /> </Attribute> <Attribute partialScore="10.0"> <Extension name="cellRef" value="$C$25" /> <SimplePredicate field="residenceState" operator="equal" value="KN" /> </Attribute> <Attribute partialScore="5.0"> <Extension name="cellRef" value="$C$26" /> <SimplePredicate field="residenceState" operator="equal" value="TN" /> </Attribute> </Characteristic> <Characteristic name="ValidLicenseScore" baselineScore="0.0"> <Extension name="cellRef" value="$B$28" /> <Attribute partialScore="1.0" reasonCode="LX00"> <Extension name="cellRef" value="$C$30" /> <SimplePredicate field="validLicense" operator="equal" value="true" /> </Attribute> <Attribute partialScore="-1.0" reasonCode="LX00"> <Extension name="cellRef" value="$C$31" /> <SimplePredicate field="validLicense" operator="equal" value="false" /> </Attribute> </Characteristic> </Characteristics> </Scorecard> </Segment> <Segment id="2"> <True /> <TreeModel modelName="SampleTree" functionName="classification" missingValueStrategy="lastPrediction" noTrueChildStrategy="returnLastPrediction"> <MiningSchema> <MiningField name="age" usageType="active" /> <MiningField name="validLicense" usageType="active" /> <MiningField name="calculatedScore" usageType="active" /> <MiningField name="qualificationLevel" usageType="predicted" /> </MiningSchema> <Output> <OutputField name="qualification" displayName="Qualification Level" dataType="string" feature="predictedValue" targetField="qualificationLevel" /> </Output> <Node score="Well" id="1"> <True/> <Node score="Barely" id="2"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="age" operator="greaterOrEqual" value="16" /> <SimplePredicate field="validLicense" operator="equal" value="true" /> </CompoundPredicate> <Node score="Barely" id="3"> <SimplePredicate field="calculatedScore" operator="lessOrEqual" value="50.0" /> </Node> <Node score="Well" id="4"> <CompoundPredicate booleanOperator="and"> <SimplePredicate field="calculatedScore" operator="greaterThan" value="50.0" /> <SimplePredicate field="calculatedScore" operator="lessOrEqual" value="60.0" /> </CompoundPredicate> </Node> <Node score="Over" id="5"> <SimplePredicate field="calculatedScore" operator="greaterThan" value="60.0" /> </Node> </Node> <Node score="Unqualified" id="6"> <CompoundPredicate booleanOperator="surrogate"> <SimplePredicate field="age" operator="lessThan" value="16" /> <SimplePredicate field="calculatedScore" operator="lessOrEqual" value="40.0" /> <True /> </CompoundPredicate> </Node> </Node> </TreeModel> </Segment> </Segmentation> </MiningModel> </PMML>
Example PMML Clustering model
<?xml version="1.0" encoding="UTF-8"?> <PMML version="4.1" xmlns="http://www.dmg.org/PMML-4_1"> <Header> <Application name="KNIME" version="2.8.0"/> </Header> <DataDictionary numberOfFields="5"> <DataField name="sepal_length" optype="continuous" dataType="double"> <Interval closure="closedClosed" leftMargin="4.3" rightMargin="7.9"/> </DataField> <DataField name="sepal_width" optype="continuous" dataType="double"> <Interval closure="closedClosed" leftMargin="2.0" rightMargin="4.4"/> </DataField> <DataField name="petal_length" optype="continuous" dataType="double"> <Interval closure="closedClosed" leftMargin="1.0" rightMargin="6.9"/> </DataField> <DataField name="petal_width" optype="continuous" dataType="double"> <Interval closure="closedClosed" leftMargin="0.1" rightMargin="2.5"/> </DataField> <DataField name="class" optype="categorical" dataType="string"/> </DataDictionary> <ClusteringModel modelName="SingleIrisKMeansClustering" functionName="clustering" modelClass="centerBased" numberOfClusters="4"> <MiningSchema> <MiningField name="sepal_length" invalidValueTreatment="asIs"/> <MiningField name="sepal_width" invalidValueTreatment="asIs"/> <MiningField name="petal_length" invalidValueTreatment="asIs"/> <MiningField name="petal_width" invalidValueTreatment="asIs"/> <MiningField name="class" usageType="predicted"/> </MiningSchema> <ComparisonMeasure kind="distance"> <squaredEuclidean/> </ComparisonMeasure> <ClusteringField field="sepal_length" compareFunction="absDiff"/> <ClusteringField field="sepal_width" compareFunction="absDiff"/> <ClusteringField field="petal_length" compareFunction="absDiff"/> <ClusteringField field="petal_width" compareFunction="absDiff"/> <Cluster name="virginica" size="32"> <Array n="4" type="real">6.9125000000000005 3.099999999999999 5.846874999999999 2.1312499999999996</Array> </Cluster> <Cluster name="versicolor" size="41"> <Array n="4" type="real">6.23658536585366 2.8585365853658535 4.807317073170731 1.6219512195121943</Array> </Cluster> <Cluster name="setosa" size="50"> <Array n="4" type="real">5.005999999999999 3.4180000000000006 1.464 0.2439999999999999</Array> </Cluster> <Cluster name="unknown" size="27"> <Array n="4" type="real">5.529629629629629 2.6222222222222222 3.940740740740741 1.2185185185185188</Array> </Cluster> </ClusteringModel> </PMML>
Chapter 12. PMML support in Red Hat Decision Manager
Red Hat Decision Manager includes consumer conformance support for the following PMML model types:
- Regression models
- Scorecard models
- Tree models
-
Mining models (with sub-types
modelChain
,selectAll
, andselectFirst
) - Clustering models
For a list of all PMML model types, including those not supported in Red Hat Decision Manager, see the DMG PMML specification.
Red Hat Decision Manager offers two implementations including PMML legacy and PMML trusty.
The PMML legacy implementation is deprecated with Red Hat Decision Manager 7.10.0 and will be replaced by PMML trusty implementation in a future Red Hat Decision Manager release.
Red Hat Decision Manager does not include a built-in PMML model editor, but you can use an XML or PMML-specific authoring tool to create PMML models and then integrate the PMML models in your decision services in Red Hat Decision Manager. You can import PMML files into your project in Business Central (Menu → Design → Projects → Import Asset) or package the PMML files as part of your project knowledge JAR (KJAR) file without Business Central.
For more information about including assets such as PMML files with your project packaging and deployment method, see Packaging and deploying an Red Hat Decision Manager project.
You can migrate a PMML service to a Red Hat build of Kogito microservice. For more information about migrating to Red Hat build of Kogito microservices, see Migrating to Red Hat build of Kogito microservices.
12.1. PMML trusty support and naming conventions in Red Hat Decision Manager
When you add a PMML file to a project in Red Hat Decision Manager, multiple assets are generated. The tree and scorecard models are translated to rules, and regression and mining models are translated to Java classes. Each type of PMML model generates a different set of assets, but all PMML model types generate at least the following set of assets:
- A root package whose name is derived from the PMML file name
- In the root package, a Java factory class that is used to instantiate the model
- A subpackage specific to the model whose name is derived from the model name
-
For rule models, two
rule-mapper
classes that are used to instantiate the rule network - For mining models, children model packages and classes are nested in the parent model
Currently, only one model for each PMML file is allowed. Also, extensions are temporarily not supported.
The following are naming conventions for generated PMML packages and classes:
-
The root package name is the name of the original PMML file in lowercase and without space, for example,
sampleregression
. -
The name of the generated
factory
Java class is the PMML file name withFactory
added to it in the formatfileName+"Factory"
and first uppercase letter, for example,SampleRegressionFactory
. -
The subpackage name of a model is the name of the original model in lowercase and without space, for example,
compoundnestedpredicatescorecard
. The names of the generated data classes are determined by the model type:
-
Rules models: A top-level
PMMLRuleMappersImpl
is generated including references toPMMLRuleMapperImpl
classes that are nested in the subpackages. Mining models:
-
The name of the created
segmentation
subpackage is the name of the original model in lowercase, without space, andsegmentation
added to it in the formatmodelName+”segmentation”
, for example,mixedminingsegmentation
. -
In the
segmentation
subpackage, asegmentation
Java class is created that contains the references to the nested models. The name of the createdsegmentation
Java class is the model name withSegmentation
added to it in the formatmodelName+Segmentation
, for example,MixedMiningSegmentation
. -
For each segment, a specific subpackage is created. The name of the segment specific subpackage is the original model name in lowercase with
segment
and a progressive integer starting from 0 added to it in the formatmodelName+segment+integer
. For example,mixedminingsegment0
,mixedminingsegment1
.
-
The name of the created
-
Rules models: A top-level
Known limitations of PMML trusty implementation
The following list shows elements that are not implemented for PMML trusty:
-
Target
element is not implemented -
Extension
element is not implemented MiningSchema
orMiningField
elements that are not implemented, include:-
importance
-
outliers
-
lowValue
-
highValue
-
invalidValueTreatment
-
invalidValueReplacement
-
OutputField
elements that are not implemented, include:- Decisions
- Value
- Rule feature
- Algorithm
-
isMultiValued
-
segmentId
-
isFinalResult
TransformationDictionary
orLocalTransformation
expressions that are not supported, include:-
NormContinuous
-
NormDiscrete
-
MapValues
-
TextIndex
-
Aggregate
-
Lag
-
-
ModelStats
,ModelExplanation
, andModelExplanation
element is not implemented in all models including regression, tree, scorecard, and mining -
verification
element is not implemented in tree, scorecard, and mining model -
VariableWeight
element is not implemented in mining model Tree model elements that are not implemented, include:
-
IsMissing
orIsNotMissing
-
Surrogate
inCompoundPredicate
-
missingValuePenalty
-
splitCharacteristic
-
isScorable
-
12.2. PMML legacy support and naming conventions in Red Hat Decision Manager
When you add a PMML file to a project in Red Hat Decision Manager, multiple assets are generated. Each type of PMML model generates a different set of assets, but all PMML model types generate at least the following set of assets:
- A DRL file that contains all of the rules associated with your PMML model
At least two Java classes:
- A data class that is used as the default object type for the model type
-
A
RuleUnit
class that is used to manage data sources and rule execution
If a PMML file has MiningModel
as the root model, multiple instances of each of these files are generated.
The following are naming conventions for generated PMML legacy packages, classes, and rules:
-
If no package name is given in a PMML model file, then the default package name
org.kie.pmml.pmml_4_2
is prefixed to the model name for the generated rules in the format"org.kie.pmml.pmml_4_2"+modelName
. -
The package name for the generated
RuleUnit
Java class is the same as the package name for the generated rules. -
The name of the generated
RuleUnit
Java class is the model name withRuleUnit
added to it in the formatmodelName+"RuleUnit"
. -
Each PMML model has at least one data class that is generated. The package name for these classes is
org.kie.pmml.pmml_4_2.model
. The names of generated data classes are determined by the model type, prefixed with the model name:
-
Regression models: One data class named
modelName+"RegressionData"
-
Scorecard models: One data class named
modelName+"ScoreCardData"
-
Tree models: Two data classes, the first named
modelName+"TreeNode"
and the second namedmodelName+"TreeToken"
-
Mining models: One data class named
modelName+"MiningModelData"
-
Regression models: One data class named
The mining model also generates all of the rules and classes that are within each of its segments.
12.2.1. PMML extensions in Red Hat Decision Manager
The PMML legacy specification supports Extension
elements that extend the content of a PMML model. You can use extensions at almost every level of a PMML model definition, and as the first and last child in the main element of a model for maximum flexibility. For more information about PMML extensions, see the DMG PMML Extension Mechanism.
To optimize PMML integration, Red Hat Decision Manager supports the following additional PMML extensions:
-
modelPackage
: Designates a package name for the generated rules and Java classes. Include this extension in theHeader
section of the PMML model file. -
adapter
: Designates the type of construct (bean
ortrait
) that is used to contain input and output data for rules. Insert this extension in theMiningSchema
orOutput
section (or both) of the PMML model file. -
externalClass
: Used in conjunction with theadapter
extension in defining aMiningField
orOutputField
. This extension contains a class with an attribute name that matches the name of theMiningField
orOutputField
element.
Chapter 13. PMML model execution
You can import PMML files into your Red Hat Decision Manager project using Business Central (Menu → Design → Projects → Import Asset) or package the PMML files as part of your project knowledge JAR (KJAR) file without Business Central. After you implement your PMML files in your Red Hat Decision Manager project, you can execute the PMML-based decision service by embedding PMML calls directly in your Java application or by sending an ApplyPmmlModelCommand
command to a configured KIE Server.
For more information about including PMML assets with your project packaging and deployment method, see Packaging and deploying an Red Hat Decision Manager project.
You can also include a PMML model as part of a Decision Model and Notation (DMN) service in Business Central. When you include a PMML model within a DMN file, you can invoke that PMML model as a boxed function expression for a DMN decision node or business knowledge model node. For more information about including PMML models in a DMN service, see Designing a decision service using DMN models.
13.1. Embedding a PMML trusty call directly in a Java application
A KIE container is local when the knowledge assets are either embedded directly into the calling program or are physically pulled in using Maven dependencies for the KJAR. You embed knowledge assets directly into a project if there is a tight relationship between the version of the code and the version of the PMML definition. Any changes to the decision take effect after you have intentionally updated and redeployed the application. A benefit of this approach is that proper operation does not rely on any external dependencies to the run time, which can be a limitation of locked-down environments.
Prerequisites
- A KJAR containing the PMML model to execute has been created. For more information about project packaging, see Packaging and deploying an Red Hat Decision Manager project.
Procedure
In your client application, add the following dependencies to the relevant classpath of your Java project:
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml-dependencies</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
The
<version>
is the Maven artifact version for Red Hat Decision Manager currently used in your project (for example, 7.67.0.Final-redhat-00024).NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency to your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
Create a KIE container from
classpath
orReleaseId
:KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
Alternative option:
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
Create an instance of the
PMMLRuntime
that is used to execute the model:PMMLRuntime pmmlRuntime = KieRuntimeFactory.of(kieContainer.getKieBase()).get(PMMLRuntime.class);
Create an instance of the
PMMLRequestData
class that applies your PMML model to a data set:PMMLRequestData pmmlRequestData = new PMMLRequestData({correlation_id}, {model_name}); pmmlRequestData.addRequestParam({parameter_name}, {parameter_value}) ...
Create an instance of the
PMMLContext
class that contains the input data:PMMLContext pmmlContext = new PMMLContextImpl(pmmlRequestData);
Retrieve the
PMML4Result
while executing the PMML model with the required PMML class instances that you created:PMML4Result pmml4Result = pmmlRuntime.evaluate({model_name}, pmmlContext);
13.2. Embedding a PMML legacy call directly in a Java application
A KIE container is local when the knowledge assets are either embedded directly into the calling program or are physically pulled in using Maven dependencies for the KJAR. You embed knowledge assets directly into a project if there is a tight relationship between the version of the code and the version of the PMML definition. Any changes to the decision take effect after you have intentionally updated and redeployed the application. A benefit of this approach is that proper operation does not rely on any external dependencies to the run time, which can be a limitation of locked-down environments.
Using Maven dependencies enables further flexibility because the specific version of the decision can dynamically change (for example, by using a system property), and it can be periodically scanned for updates and automatically updated. This introduces an external dependency on the deploy time of the service, but executes the decision locally, reducing reliance on an external service being available during run time.
Prerequisites
- A KJAR containing the PMML model to execute has been created. For more information about project packaging, see Packaging and deploying an Red Hat Decision Manager project.
Procedure
In your client application, add the following dependencies to the relevant classpath of your Java project:
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
The
<version>
is the Maven artifact version for Red Hat Decision Manager currently used in your project (for example, 7.67.0.Final-redhat-00024).NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency to your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
ImportantTo use the legacy implementation, ensure that the
kie-pmml-implementation
system property is set aslegacy
.Create a KIE container from
classpath
orReleaseId
:KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
Alternative option:
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
Create an instance of the
PMMLRequestData
class, which applies your PMML model to a set of data:public class PMMLRequestData { private String correlationId; 1 private String modelName; 2 private String source; 3 private List<ParameterInfo<?>> requestParams; 4 ... }
Create an instance of the
PMML4Result
class, which holds the output information that is the result of applying the PMML-based rules to the input data:public class PMML4Result { private String correlationId; private String segmentationId; 1 private String segmentId; 2 private int segmentIndex; 3 private String resultCode; 4 private Map<String, Object> resultVariables; 5 ... }
- 1
- Used when the model type is
MiningModel
. ThesegmentationId
is used to differentiate between multiple segmentations. - 2
- Used in conjunction with the
segmentationId
to identify which segment generated the results. - 3
- Used to maintain the order of segments.
- 4
- Used to determine whether the model was successfully applied, where
OK
indicates success. - 5
- Contains the name of a resultant variable and its associated value.
In addition to the normal getter methods, the
PMML4Result
class also supports the following methods for directly retrieving the values for result variables:public <T> Optional<T> getResultValue(String objName, String objField, Class<T> clazz, Object...params) public Object getResultValue(String objName, String objField, Object...params)
Create an instance of the
ParameterInfo
class, which serves as a wrapper for basic data type objects used as part of thePMMLRequestData
class:public class ParameterInfo<T> { 1 private String correlationId; private String name; 2 private String capitalizedName; private Class<T> type; 3 private T value; 4 ... }
Execute the PMML model based on the required PMML class instances that you have created:
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String correlationId, String modelPkgName) { RuleUnitExecutor executor = RuleUnitExecutor.create().bind(kbase); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); PMML4Result resultHolder = new PMML4Result(correlationId); variables.entrySet().forEach( es -> { request.addRequestParam(es.getKey(), es.getValue()); }); DataSource<PMMLRequestData> requestData = executor.newDataSource("request"); DataSource<PMML4Result> resultData = executor.newDataSource("results"); DataSource<PMMLData> internalData = executor.newDataSource("pmmlData"); requestData.insert(request); resultData.insert(resultHolder); List<String> possiblePackageNames = calculatePossiblePackageNames(modelName, modelPkgName); Class<? extends RuleUnit> ruleUnitClass = getStartingRuleUnit("RuleUnitIndicator", (InternalKnowledgeBase)kbase, possiblePackageNames); if (ruleUnitClass != null) { executor.run(ruleUnitClass); if ( "OK".equals(resultHolder.getResultCode()) ) { // extract result variables here } } } protected Class<? extends RuleUnit> getStartingRuleUnit(String startingRule, InternalKnowledgeBase ikb, List<String> possiblePackages) { RuleUnitRegistry unitRegistry = ikb.getRuleUnitRegistry(); Map<String,InternalKnowledgePackage> pkgs = ikb.getPackagesMap(); RuleImpl ruleImpl = null; for (String pkgName: possiblePackages) { if (pkgs.containsKey(pkgName)) { InternalKnowledgePackage pkg = pkgs.get(pkgName); ruleImpl = pkg.getRule(startingRule); if (ruleImpl != null) { RuleUnitDescr descr = unitRegistry.getRuleUnitFor(ruleImpl).orElse(null); if (descr != null) { return descr.getRuleUnitClass(); } } } } return null; } protected List<String> calculatePossiblePackageNames(String modelId, String...knownPackageNames) { List<String> packageNames = new ArrayList<>(); String javaModelId = modelId.replaceAll("\\s",""); if (knownPackageNames != null && knownPackageNames.length > 0) { for (String knownPkgName: knownPackageNames) { packageNames.add(knownPkgName + "." + javaModelId); } } String basePkgName = PMML4UnitImpl.DEFAULT_ROOT_PACKAGE+"."+javaModelId; packageNames.add(basePkgName); return packageNames; }
Rules are executed by the
RuleUnitExecutor
class. TheRuleUnitExecutor
class creates KIE sessions and adds the requiredDataSource
objects to those sessions, and then executes the rules based on theRuleUnit
that is passed as a parameter to therun()
method. ThecalculatePossiblePackageNames
and thegetStartingRuleUnit
methods determine the fully qualified name of theRuleUnit
class that is passed to therun()
method.
To facilitate your PMML model execution, you can also use a PMML4ExecutionHelper
class supported in Red Hat Decision Manager. For more information about the PMML helper class, see Section 13.2.1, “PMML execution helper class”.
13.2.1. PMML execution helper class
Red Hat Decision Manager provides a PMML4ExecutionHelper
class that helps create the PMMLRequestData
class required for PMML model execution and that helps execute rules using the RuleUnitExecutor
class.
The following are examples of a PMML model execution without and with the PMML4ExecutionHelper
class, as a comparison:
Example PMML model execution without using PMML4ExecutionHelper
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String correlationId, String modelPkgName) { RuleUnitExecutor executor = RuleUnitExecutor.create().bind(kbase); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); PMML4Result resultHolder = new PMML4Result(correlationId); variables.entrySet().forEach( es -> { request.addRequestParam(es.getKey(), es.getValue()); }); DataSource<PMMLRequestData> requestData = executor.newDataSource("request"); DataSource<PMML4Result> resultData = executor.newDataSource("results"); DataSource<PMMLData> internalData = executor.newDataSource("pmmlData"); requestData.insert(request); resultData.insert(resultHolder); List<String> possiblePackageNames = calculatePossiblePackageNames(modelName, modelPkgName); Class<? extends RuleUnit> ruleUnitClass = getStartingRuleUnit("RuleUnitIndicator", (InternalKnowledgeBase)kbase, possiblePackageNames); if (ruleUnitClass != null) { executor.run(ruleUnitClass); if ( "OK".equals(resultHolder.getResultCode()) ) { // extract result variables here } } } protected Class<? extends RuleUnit> getStartingRuleUnit(String startingRule, InternalKnowledgeBase ikb, List<String> possiblePackages) { RuleUnitRegistry unitRegistry = ikb.getRuleUnitRegistry(); Map<String,InternalKnowledgePackage> pkgs = ikb.getPackagesMap(); RuleImpl ruleImpl = null; for (String pkgName: possiblePackages) { if (pkgs.containsKey(pkgName)) { InternalKnowledgePackage pkg = pkgs.get(pkgName); ruleImpl = pkg.getRule(startingRule); if (ruleImpl != null) { RuleUnitDescr descr = unitRegistry.getRuleUnitFor(ruleImpl).orElse(null); if (descr != null) { return descr.getRuleUnitClass(); } } } } return null; } protected List<String> calculatePossiblePackageNames(String modelId, String...knownPackageNames) { List<String> packageNames = new ArrayList<>(); String javaModelId = modelId.replaceAll("\\s",""); if (knownPackageNames != null && knownPackageNames.length > 0) { for (String knownPkgName: knownPackageNames) { packageNames.add(knownPkgName + "." + javaModelId); } } String basePkgName = PMML4UnitImpl.DEFAULT_ROOT_PACKAGE+"."+javaModelId; packageNames.add(basePkgName); return packageNames; }
Example PMML model execution using PMML4ExecutionHelper
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String modelPkgName, String correlationId) { PMML4ExecutionHelper helper = PMML4ExecutionHelperFactory.getExecutionHelper(modelName, kbase); helper.addPossiblePackageName(modelPkgName); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); variables.entrySet().forEach(entry -> { request.addRequestParam(entry.getKey(), entry.getValue); }); PMML4Result resultHolder = helper.submitRequest(request); if ("OK".equals(resultHolder.getResultCode)) { // extract result variables here } }
When you use the PMML4ExecutionHelper
, you do not need to specify the possible package names nor the RuleUnit
class as you would in a typical PMML model execution.
To construct a PMML4ExecutionHelper
class, you use the PMML4ExecutionHelperFactory
class to determine how instances of PMML4ExecutionHelper
are retrieved.
The following are the available PMML4ExecutionHelperFactory
class methods for constructing a PMML4ExecutionHelper
class:
- PMML4ExecutionHelperFactory methods for PMML assets in a KIE base
Use these methods when PMML assets have already been compiled and are being used from an existing KIE base:
public static PMML4ExecutionHelper getExecutionHelper(String modelName, KieBase kbase) public static PMML4ExecutionHelper getExecutionHelper(String modelName, KieBase kbase, boolean includeMiningDataSources)
- PMML4ExecutionHelperFactory methods for PMML assets on the project classpath
Use these methods when PMML assets are on the project classpath. The
classPath
argument is the project classpath location of the PMML file:public static PMML4ExecutionHelper getExecutionHelper(String modelName, String classPath, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName,String classPath, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
- PMML4ExecutionHelperFactory methods for PMML assets in a byte array
Use these methods when PMML assets are in the form of a byte array:
public static PMML4ExecutionHelper getExecutionHelper(String modelName, byte[] content, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName, byte[] content, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
- PMML4ExecutionHelperFactory methods for PMML assets in a
Resource
Use these methods when PMML assets are in the form of an
org.kie.api.io.Resource
object:public static PMML4ExecutionHelper getExecutionHelper(String modelName, Resource resource, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName, Resource resource, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
The classpath, byte array, and resource PMML4ExecutionHelperFactory
methods create a KIE container for the generated rules and Java classes. The container is used as the source of the KIE base that the RuleUnitExecutor
uses. The container is not persisted. The PMML4ExecutionHelperFactory
method for PMML assets that are already in a KIE base does not create a KIE container in this way.
13.3. Executing a PMML model using KIE Server
You can execute PMML models that have been deployed to KIE Server by sending the ApplyPmmlModelCommand
command to the configured KIE Server. When you use this command, a PMMLRequestData
object is sent to KIE Server and a PMML4Result
result object is received as a reply. You can send PMML requests to KIE Server through the KIE Server REST API from a configured Java class or directly from a REST client.
Prerequisites
-
KIE Server is installed and configured, including a known user name and credentials for a user with the
kie-server
role. For installation options, see Planning a Red Hat Decision Manager installation. - A KIE container is deployed in KIE Server in the form of a KJAR that includes the PMML model. For more information about project packaging, see Packaging and deploying an Red Hat Decision Manager project.
- You have the container ID of the KIE container containing the PMML model.
Procedure
In your client application, add the following dependencies to the relevant classpath of your Java project:
Example of legacy implementation
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required for the KIE Server Java client API --> <dependency> <groupId>org.kie.server</groupId> <artifactId>kie-server-client</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
ImportantTo use the legacy implementation, ensure that the
kie-pmml-implementation
system property is set aslegacy
.Example of trusty implementation
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml-dependencies</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required for the KIE Server Java client API --> <dependency> <groupId>org.kie.server</groupId> <artifactId>kie-server-client</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
The
<version>
is the Maven artifact version for Red Hat Decision Manager currently used in your project (for example, 7.67.0.Final-redhat-00024).NoteInstead of specifying a Red Hat Decision Manager
<version>
for individual dependencies, consider adding the Red Hat Business Automation bill of materials (BOM) dependency to your projectpom.xml
file. The Red Hat Business Automation BOM applies to both Red Hat Decision Manager and Red Hat Process Automation Manager. When you add the BOM files, the correct versions of transitive dependencies from the provided Maven repositories are included in the project.Example BOM dependency:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.13.5.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
For more information about the Red Hat Business Automation BOM, see What is the mapping between RHDM product and maven library version?.
Create a KIE container from
classpath
orReleaseId
:KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
Alternative option:
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
Create a class for sending requests to KIE Server and receiving responses:
public class ApplyScorecardModel { private static final ReleaseId releaseId = new ReleaseId("org.acme","my-kjar","1.0.0"); private static final String containerId = "SampleModelContainer"; private static KieCommands commandFactory; private static ClassLoader kjarClassLoader; 1 private RuleServicesClient serviceClient; 2 // Attributes specific to your class instance private String rankedFirstCode; private Double score; // Initialization of non-final static attributes static { commandFactory = KieServices.Factory.get().getCommands(); // Specifications for kjarClassLoader, if used KieMavenRepository kmp = KieMavenRepository.getMavenRepository(); File artifactFile = kmp.resolveArtifact(releaseId).getFile(); if (artifactFile != null) { URL urls[] = new URL[1]; try { urls[0] = artifactFile.toURI().toURL(); classLoader = new KieURLClassLoader(urls,PMML4Result.class.getClassLoader()); } catch (MalformedURLException e) { logger.error("Error getting classLoader for "+containerId); logger.error(e.getMessage()); } } else { logger.warn("Did not find the artifact file for "+releaseId.toString()); } } public ApplyScorecardModel(KieServicesConfiguration kieConfig) { KieServicesClient clientFactory = KieServicesFactory.newKieServicesClient(kieConfig); serviceClient = clientFactory.getServicesClient(RuleServicesClient.class); } ... // Getters and setters ... // Method for executing the PMML model on KIE Server public void applyModel(String occupation, int age) { PMMLRequestData input = new PMMLRequestData("1234","SampleModelName"); 3 input.addRequestParam(new ParameterInfo("1234","occupation",String.class,occupation)); input.addRequestParam(new ParameterInfo("1234","age",Integer.class,age)); CommandFactoryServiceImpl cf = (CommandFactoryServiceImpl)commandFactory; ApplyPmmlModelCommand command = (ApplyPmmlModelCommand) cf.newApplyPmmlModel(request); 4 ServiceResponse<ExecutionResults> results = ruleClient.executeCommandsWithResults(CONTAINER_ID, command); 5 if (results != null) { 6 PMML4Result resultHolder = (PMML4Result)results.getResult().getValue("results"); if (resultHolder != null && "OK".equals(resultHolder.getResultCode())) { this.score = resultHolder.getResultValue("ScoreCard","score",Double.class).get(); Map<String,Object> rankingMap = (Map<String,Object>)resultHolder.getResultValue("ScoreCard","ranking"); if (rankingMap != null && !rankingMap.isEmpty()) { this.rankedFirstCode = rankingMap.keySet().iterator().next(); } } } } }
- 1
- Defines the class loader if you did not include the KJAR in your client project dependencies
- 2
- Identifies the service client as defined in the configuration settings, including KIE Server REST API access credentials
- 3
- Initializes a
PMMLRequestData
object - 4
- Creates an instance of the
ApplyPmmlModelCommand
- 5
- Sends the command using the service client
- 6
- Retrieves the results of the executed PMML model
Execute the class instance to send the PMML invocation request to KIE Server.
Alternatively, you can use JMS and REST interfaces to send the
ApplyPmmlModelCommand
command to KIE Server. For REST requests, you use theApplyPmmlModelCommand
command as aPOST
request tohttp://SERVER:PORT/kie-server/services/rest/server/containers/instances/{containerId}
in JSON, JAXB, or XStream request format.Example POST endpoint
http://localhost:8080/kie-server/services/rest/server/containers/instances/SampleModelContainer
Example JSON request body
{ "commands": [ { "apply-pmml-model-command": { "outIdentifier": null, "packageName": null, "hasMining": false, "requestData": { "correlationId": "123", "modelName": "SimpleScorecard", "source": null, "requestParams": [ { "correlationId": "123", "name": "param1", "type": "java.lang.Double", "value": "10.0" }, { "correlationId": "123", "name": "param2", "type": "java.lang.Double", "value": "15.0" } ] } } } ] }
Example curl request with endpoint and body
curl -X POST "http://localhost:8080/kie-server/services/rest/server/containers/instances/SampleModelContainer" -H "accept: application/json" -H "content-type: application/json" -d "{ \"commands\": [ { \"apply-pmml-model-command\": { \"outIdentifier\": null, \"packageName\": null, \"hasMining\": false, \"requestData\": { \"correlationId\": \"123\", \"modelName\": \"SimpleScorecard\", \"source\": null, \"requestParams\": [ { \"correlationId\": \"123\", \"name\": \"param1\", \"type\": \"java.lang.Double\", \"value\": \"10.0\" }, { \"correlationId\": \"123\", \"name\": \"param2\", \"type\": \"java.lang.Double\", \"value\": \"15.0\" } ] } } } ]}"
Example JSON response
{ "results" : [ { "value" : {"org.kie.api.pmml.DoubleFieldOutput":{ "value" : 40.8, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "name" : "OverallScore", "displayValue" : "OverallScore", "weight" : 1.0 }}, "key" : "OverallScore" }, { "value" : {"org.kie.api.pmml.PMML4Result":{ "resultVariables" : { "OverallScore" : { "value" : 40.8, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "name" : "OverallScore", "displayValue" : "OverallScore", "weight" : 1.0 }, "ScoreCard" : { "modelName" : "SimpleScorecard", "score" : 40.8, "holder" : { "modelName" : "SimpleScorecard", "correlationId" : "123", "voverallScore" : null, "moverallScore" : true, "vparam1" : 10.0, "mparam1" : false, "vparam2" : 15.0, "mparam2" : false }, "enableRC" : true, "pointsBelow" : true, "ranking" : { "reasonCh1" : 5.0, "reasonCh2" : -6.0 } } }, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "segmentIndex" : 0, "resultCode" : "OK", "resultObjectName" : null }}, "key" : "results" } ], "facts" : [ ] }
Chapter 14. Additional resources
Part III. Designing a decision service using DRL rules
As a business rules developer, you can define business rules using the DRL (Drools Rule Language) designer in Business Central. DRL rules are defined directly in free-form .drl
text files instead of in a guided or tabular format like other types of rule assets in Business Central. These DRL files form the core of the decision service for your project.
You can also design your decision service using Decision Model and Notation (DMN) models instead of rule-based or table-based assets. For information about DMN support in Red Hat Decision Manager 7.13, see the following resources:
- Getting started with decision services (step-by-step tutorial with a DMN decision service example)
- Designing a decision service using DMN models (overview of DMN support and capabilities in Red Hat Decision Manager)
Prerequisites
- The space and project for the DRL rules have been created in Business Central. Each asset is associated with a project assigned to a space. For details, see Getting started with decision services.
Chapter 15. Decision-authoring assets in Red Hat Decision Manager
Red Hat Decision Manager supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.
The following table highlights the main decision-authoring assets supported in Red Hat Decision Manager projects to help you decide or confirm the best method for defining decisions in your decision service.
Asset | Highlights | Authoring tools | Documentation |
---|---|---|---|
Decision Model and Notation (DMN) models |
| Business Central or other DMN-compliant editor | |
Guided decision tables |
| Business Central | |
Spreadsheet decision tables |
| Spreadsheet editor | Designing a decision service using spreadsheet decision tables |
Guided rules |
| Business Central | |
Guided rule templates |
| Business Central | |
DRL rules |
| Business Central or integrated development environment (IDE) | |
Predictive Model Markup Language (PMML) models |
| PMML or XML editor |
When you define business decisions, you can also consider using Red Hat build of Kogito for your cloud-native decision services. For more information about getting started with Red Hat build of Kogito microservices, see Getting started with Red Hat build of Kogito in Red Hat Decision Manager.
Chapter 16. DRL (Drools Rule Language) rules
DRL (Drools Rule Language) rules are business rules that you define directly in .drl
text files. These DRL files are the source in which all other rule assets in Business Central are ultimately rendered. You can create and manage DRL files within the Business Central interface, or create them externally as part of a Maven or Java project using Red Hat CodeReady Studio or another integrated development environment (IDE). A DRL file can contain one or more rules that define at a minimum the rule conditions (when
) and actions (then
). The DRL designer in Business Central provides syntax highlighting for Java, DRL, and XML.
DRL files consist of the following components:
Components in a DRL file
package import function // Optional query // Optional declare // Optional global // Optional rule "rule name" // Attributes when // Conditions then // Actions end rule "rule2 name" ...
The following example DRL rule determines the age limit in a loan application decision service:
Example rule for loan application age limit
rule "Underage" salience 15 agenda-group "applicationGroup" when $application : LoanApplication() Applicant( age < 21 ) then $application.setApproved( false ); $application.setExplanation( "Underage" ); end
A DRL file can contain single or multiple rules, queries, and functions, and can define resource declarations such as imports, globals, and attributes that are assigned and used by your rules and queries. The DRL package must be listed at the top of a DRL file and the rules are typically listed last. All other DRL components can follow any order.
Each rule must have a unique name within the rule package. If you use the same rule name more than once in any DRL file in the package, the rules fail to compile. Always enclose rule names with double quotation marks (rule "rule name"
) to prevent possible compilation errors, especially if you use spaces in rule names.
All data objects related to a DRL rule must be in the same project package as the DRL file in Business Central. Assets in the same package are imported by default. Existing assets in other packages can be imported with the DRL rule.
16.1. Packages in DRL
A package is a folder of related assets in Red Hat Decision Manager, such as data objects, DRL files, decision tables, and other asset types. A package also serves as a unique namespace for each group of rules. A single rule base can contain multiple packages. You typically store all the rules for a package in the same file as the package declaration so that the package is self-contained. However, you can import objects from other packages that you want to use in the rules.
The following example is a package name and namespace for a DRL file in a mortgage application decision service:
Example package definition in a DRL file
package org.mortgages;
16.2. Import statements in DRL
Similar to import statements in Java, imports in DRL files identify the fully qualified paths and type names for any objects that you want to use in the rules. You specify the package and data object in the format packageName.objectName
, with multiple imports on separate lines. The decision engine automatically imports classes from the Java package with the same name as the DRL package and from the package java.lang
.
The following example is an import statement for a loan application object in a mortgage application decision service:
Example import statement in a DRL file
import org.mortgages.LoanApplication;
16.3. Functions in DRL
Functions in DRL files put semantic code in your rule source file instead of in Java classes. Functions are especially useful if an action (then
) part of a rule is used repeatedly and only the parameters differ for each rule. Above the rules in the DRL file, you can declare the function or import a static method from a helper class as a function, and then use the function by name in an action (then
) part of the rule.
The following examples illustrate a function that is either declared or imported in a DRL file:
Example function declaration with a rule (option 1)
function String hello(String applicantName) { return "Hello " + applicantName + "!"; } rule "Using a function" when // Empty then System.out.println( hello( "James" ) ); end
Example function import with a rule (option 2)
import function my.package.applicant.hello; rule "Using a function" when // Empty then System.out.println( hello( "James" ) ); end
16.4. Queries in DRL
Queries in DRL files search the working memory of the decision engine for facts related to the rules in the DRL file. You add the query definitions in DRL files and then obtain the matching results in your application code. Queries search for a set of defined conditions and do not require when
or then
specifications. Query names are global to the KIE base and therefore must be unique among all other rule queries in the project. To return the results of a query, you construct a QueryResults
definition using ksession.getQueryResults("name")
, where "name"
is the query name. This returns a list of query results, which enable you to retrieve the objects that matched the query. You define the query and query results parameters above the rules in the DRL file.
The following example is a query definition in a DRL file for underage applicants in a mortgage application decision service, with the accompanying application code:
Example query definition in a DRL file
query "people under the age of 21" $person : Person( age < 21 ) end
Example application code to obtain query results
QueryResults results = ksession.getQueryResults( "people under the age of 21" ); System.out.println( "we have " + results.size() + " people under the age of 21" );
You can also iterate over the returned QueryResults
using a standard for
loop. Each element is a QueryResultsRow
that you can use to access each of the columns in the tuple.
Example application code to obtain and iterate over query results
QueryResults results = ksession.getQueryResults( "people under the age of 21" ); System.out.println( "we have " + results.size() + " people under the age of 21" ); System.out.println( "These people are under the age of 21:" ); for ( QueryResultsRow row : results ) { Person person = ( Person ) row.get( "person" ); System.out.println( person.getName() + "\n" ); }
16.5. Type declarations and metadata in DRL
Declarations in DRL files define new fact types or metadata for fact types to be used by rules in the DRL file:
-
New fact types: The default fact type in the
java.lang
package of Red Hat Decision Manager isObject
, but you can declare other types in DRL files as needed. Declaring fact types in DRL files enables you to define a new fact model directly in the decision engine, without creating models in a lower-level language like Java. You can also declare a new type when a domain model is already built and you want to complement this model with additional entities that are used mainly during the reasoning process. -
Metadata for fact types: You can associate metadata in the format
@key(value)
with new or existing facts. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the decision engine and used in the reasoning process.
16.5.1. Type declarations without metadata in DRL
A declaration of a new fact does not require any metadata, but must include a list of attributes or fields. If a type declaration does not include identifying attributes, the decision engine searches for an existing fact class in the classpath and raises an error if the class is missing.
The following example is a declaration of a new fact type Person
with no metadata in a DRL file:
Example declaration of a new fact type with a rule
declare Person name : String dateOfBirth : java.util.Date address : Address end rule "Using a declared type" when $p : Person( name == "James" ) then // Insert Mark, who is a customer of James. Person mark = new Person(); mark.setName( "Mark" ); insert( mark ); end
In this example, the new fact type Person
has the three attributes name
, dateOfBirth
, and address
. Each attribute has a type that can be any valid Java type, including another class that you create or a fact type that you previously declared. The dateOfBirth
attribute has the type java.util.Date
, from the Java API, and the address
attribute has the previously defined fact type Address
.
To avoid writing the fully qualified name of a class every time you declare it, you can define the full class name as part of the import
clause:
Example type declaration with the fully qualified class name in the import
import java.util.Date declare Person name : String dateOfBirth : Date address : Address end
When you declare a new fact type, the decision engine generates at compile time a Java class representing the fact type. The generated Java class is a one-to-one JavaBeans mapping of the type definition.
For example, the following Java class is generated from the example Person
type declaration:
Generated Java class for the Person fact type declaration
public class Person implements Serializable { private String name; private java.util.Date dateOfBirth; private Address address; // Empty constructor public Person() {...} // Constructor with all fields public Person( String name, Date dateOfBirth, Address address ) {...} // If keys are defined, constructor with keys public Person( ...keys... ) {...} // Getters and setters // `equals` and `hashCode` // `toString` }
You can then use the generated class in your rules like any other fact, as illustrated in the previous rule example with the Person
type declaration:
Example rule that uses the declared Person fact type
rule "Using a declared type" when $p : Person( name == "James" ) then // Insert Mark, who is a customer of James. Person mark = new Person(); mark.setName( "Mark" ); insert( mark ); end
16.5.2. Enumerative type declarations in DRL
DRL supports the declaration of enumerative types in the format declare enum <factType>
, followed by a comma-separated list of values ending with a semicolon. You can then use the enumerative list in the rules in the DRL file.
For example, the following enumerative type declaration defines days of the week for an employee scheduling rule:
Example enumerative type declaration with a scheduling rule
declare enum DaysOfWeek SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday"); fullName : String end rule "Using a declared Enum" when $emp : Employee( dayOff == DaysOfWeek.MONDAY ) then ... end
16.5.3. Extended type declarations in DRL
DRL supports type declaration inheritance in the format declare <factType1> extends <factType2>
. To extend a type declared in Java by a subtype declared in DRL, you repeat the parent type in a declaration statement without any fields.
For example, the following type declarations extend a Student
type from a top-level Person
type, and a LongTermStudent
type from the Student
subtype:
Example extended type declarations
import org.people.Person declare Person end declare Student extends Person school : String end declare LongTermStudent extends Student years : int course : String end
16.5.4. Type declarations with metadata in DRL
You can associate metadata in the format @key(value)
(the value is optional) with fact types or fact attributes. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the decision engine and used in the reasoning process. Any metadata that you declare before the attributes of a fact type are assigned to the fact type, while metadata that you declare after an attribute are assigned to that particular attribute.
In the following example, the two metadata attributes @author
and @dateOfCreation
are declared for the Person
fact type, and the two metadata items @key
and @maxLength
are declared for the name
attribute. The @key
metadata attribute has no required value, so the parentheses and the value are omitted.
Example metadata declaration for fact types and attributes
import java.util.Date declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) name : String @key @maxLength( 30 ) dateOfBirth : Date address : Address end
For declarations of metadata attributes for existing types, you can identify the fully qualified class name as part of the import
clause for all declarations or as part of the individual declare
clause:
Example metadata declaration for an imported type
import org.drools.examples.Person declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
Example metadata declaration for a declared type
declare org.drools.examples.Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
16.5.5. Metadata tags for fact type and attribute declarations in DRL
Although you can define custom metadata attributes in DRL declarations, the decision engine also supports the following predefined metadata tags for declarations of fact types or fact type attributes.
The examples in this section that refer to the VoiceCall
class assume that the sample application domain model includes the following class details:
VoiceCall fact class in an example Telecom domain model
public class VoiceCall { private String originNumber; private String destinationNumber; private Date callDateTime; private long callDuration; // in milliseconds // Constructors, getters, and setters }
- @role
This tag determines whether a given fact type is handled as a regular fact or an event in the decision engine during complex event processing.
Default parameter:
fact
Supported parameters:
fact
,event
@role( fact | event )
Example: Declare VoiceCall as event type
declare VoiceCall @role( event ) end
- @timestamp
This tag is automatically assigned to every event in the decision engine. By default, the time is provided by the session clock and assigned to the event when it is inserted into the working memory of the decision engine. You can specify a custom time stamp attribute instead of the default time stamp added by the session clock.
Default parameter: The time added by the decision engine session clock
Supported parameters: Session clock time or custom time stamp attribute
@timestamp( <attributeName> )
Example: Declare VoiceCall timestamp attribute
declare VoiceCall @role( event ) @timestamp( callDateTime ) end
- @duration
This tag determines the duration time for events in the decision engine. Events can be interval-based events or point-in-time events. Interval-based events have a duration time and persist in the working memory of the decision engine until their duration time has lapsed. Point-in-time events have no duration and are essentially interval-based events with a duration of zero. By default, every event in the decision engine has a duration of zero. You can specify a custom duration attribute instead of the default.
Default parameter: Null (zero)
Supported parameters: Custom duration attribute
@duration( <attributeName> )
Example: Declare VoiceCall duration attribute
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) end
- @expires
This tag determines the time duration before an event expires in the working memory of the decision engine. By default, an event expires when the event can no longer match and activate any of the current rules. You can define an amount of time after which an event should expire. This tag definition also overrides the implicit expiration offset calculated from temporal constraints and sliding windows in the KIE base. This tag is available only when the decision engine is running in stream mode.
Default parameter: Null (event expires after event can no longer match and activate rules)
Supported parameters: Custom
timeOffset
attribute in the format[#d][#h][#m][#s][[ms]]
@expires( <timeOffset> )
Example: Declare expiration offset for VoiceCall events
declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) @expires( 1h35m ) end
- @typesafe
This tab determines whether a given fact type is compiled with or without type safety. By default, all type declarations are compiled with type safety enabled. You can override this behavior to type-unsafe evaluation, where all constraints are generated as MVEL constraints and executed dynamically. This is useful when dealing with collections that do not have any generics or mixed type collections.
Default parameter:
true
Supported parameters:
true
,false
@typesafe( <boolean> )
Example: Declare VoiceCall for type-unsafe evaluation
declare VoiceCall @role( fact ) @typesafe( false ) end
- @serialVersionUID
This tag defines an identifying
serialVersionUID
value for a serializable class in a fact declaration. If a serializable class does not explicitly declare aserialVersionUID
, the serialization run time calculates a defaultserialVersionUID
value for that class based on various aspects of the class, as described in the Java Object Serialization Specification. However, for optimal deserialization results and for greater compatibility with serialized KIE sessions, set theserialVersionUID
as needed in the relevant class or in your DRL declarations.Default parameter: Null
Supported parameters: Custom
serialVersionUID
integer@serialVersionUID( <integer> )
Example: Declare serialVersionUID for a VoiceCall class
declare VoiceCall @serialVersionUID( 42 ) end
- @key
This tag enables a fact type attribute to be used as a key identifier for the fact type. The generated class can then implement the
equals()
andhashCode()
methods to determine if two instances of the type are equal to each other. The decision engine can also generate a constructor using all the key attributes as parameters.Default parameter: None
Supported parameters: None
<attributeDefinition> @key
Example: Declare Person type attributes as keys
declare Person firstName : String @key lastName : String @key age : int end
For this example, the decision engine checks the
firstName
andlastName
attributes to determine if two instances ofPerson
are equal to each other, but it does not check theage
attribute. The decision engine also implicitly generates three constructors: one without parameters, one with the@key
fields, and one with all fields:Example constructors from the key declarations
Person() // Empty constructor Person( String firstName, String lastName ) Person( String firstName, String lastName, int age )
You can then create instances of the type based on the key constructors, as shown in the following example:
Example instance using the key constructor
Person person = new Person( "John", "Doe" );
- @position
This tag determines the position of a declared fact type attribute or field in a positional argument, overriding the default declared order of attributes. You can use this tag to modify positional constraints in patterns while maintaining a consistent format in your type declarations and positional arguments. You can use this tag only for fields in classes on the classpath. If some fields in a single class use this tag and some do not, the attributes without this tag are positioned last, in the declared order. Inheritance of classes is supported, but not interfaces of methods.
Default parameter: None
Supported parameters: Any integer
<attributeDefinition> @position ( <integer> )
Example: Declare a fact type and override declared order
declare Person firstName : String @position( 1 ) lastName : String @position( 0 ) age : int @position( 2 ) occupation: String end
In this example, the attributes are prioritized in positional arguments in the following order:
-
lastName
-
firstName
-
age
-
occupation
In positional arguments, you do not need to specify the field name because the position maps to a known named field. For example, the argument
Person( lastName == "Doe" )
is the same asPerson( "Doe"; )
, where thelastName
field has the highest position annotation in the DRL declaration. The semicolon;
indicates that everything before it is a positional argument. You can mix positional and named arguments on a pattern by using the semicolon to separate them. Any variables in a positional argument that have not yet been bound are bound to the field that maps to that position.The following example patterns illustrate different ways of constructing positional and named arguments. The patterns have two constraints and a binding, and the semicolon differentiates the positional section from the named argument section. Variables and literals and expressions using only literals are supported in positional arguments, but not variables alone.
Example patterns with positional and named arguments
Person( "Doe", "John", $a; ) Person( "Doe", "John"; $a : age ) Person( "Doe"; firstName == "John", $a : age ) Person( lastName == "Doe"; firstName == "John", $a : age )
Positional arguments can be classified as input arguments or output arguments. Input arguments contain a previously declared binding and constrain against that binding using unification. Output arguments generate the declaration and bind it to the field represented by the positional argument when the binding does not yet exist.
In extended type declarations, use caution when defining
@position
annotations because the attribute positions are inherited in subtypes. This inheritance can result in a mixed attribute order that can be confusing in some cases. Two fields can have the same@position
value and consecutive values do not need to be declared. If a position is repeated, the conflict is solved using inheritance, where position values in the parent type have precedence, and then using the declaration order from the first to last declaration.For example, the following extended type declarations result in mixed positional priorities:
Example extended fact type with mixed position annotations
declare Person firstName : String @position( 1 ) lastName : String @position( 0 ) age : int @position( 2 ) occupation: String end declare Student extends Person degree : String @position( 1 ) school : String @position( 0 ) graduationDate : Date end
In this example, the attributes are prioritized in positional arguments in the following order:
-
lastName
(position 0 in the parent type) -
school
(position 0 in the subtype) -
firstName
(position 1 in the parent type) -
degree
(position 1 in the subtype) -
age
(position 2 in the parent type) -
occupation
(first field with no position annotation) -
graduationDate
(second field with no position annotation)
-
16.5.6. Property-change settings and listeners for fact types
By default, the decision engine does not re-evaluate all fact patterns for fact types each time a rule is triggered, but instead reacts only to modified properties that are constrained or bound inside a given pattern. For example, if a rule calls modify()
as part of the rule actions but the action does not generate new data in the KIE base, the decision engine does not automatically re-evaluate all fact patterns because no data was modified. This property reactivity behavior prevents unwanted recursions in the KIE base and results in more efficient rule evaluation. This behavior also means that you do not always need to use the no-loop
rule attribute to avoid infinite recursion.
You can modify or disable this property reactivity behavior with the following KnowledgeBuilderConfiguration
options, and then use a property-change setting in your Java class or DRL files to fine-tune property reactivity as needed:
-
ALWAYS
: (Default) All types are property reactive, but you can disable property reactivity for a specific type by using the@classReactive
property-change setting. -
ALLOWED
: No types are property reactive, but you can enable property reactivity for a specific type by using the@propertyReactive
property-change setting. -
DISABLED
: No types are property reactive. All property-change listeners are ignored.
Example property reactivity setting in KnowledgeBuilderConfiguration
KnowledgeBuilderConfiguration config = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(); config.setOption(PropertySpecificOption.ALLOWED); KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);
Alternatively, you can update the drools.propertySpecific
system property in the standalone.xml
file of your Red Hat Decision Manager distribution:
Example property reactivity setting in system properties
<system-properties> ... <property name="drools.propertySpecific" value="ALLOWED"/> ... </system-properties>
The decision engine supports the following property-change settings and listeners for fact classes or declared DRL fact types:
- @classReactive
If property reactivity is set to
ALWAYS
in the decision engine (all types are property reactive), this tag disables the default property reactivity behavior for a specific Java class or a declared DRL fact type. You can use this tag if you want the decision engine to re-evaluate all fact patterns for the specified fact type each time the rule is triggered, instead of reacting only to modified properties that are constrained or bound inside a given pattern.Example: Disable default property reactivity in a DRL type declaration
declare Person @classReactive firstName : String lastName : String end
Example: Disable default property reactivity in a Java class
@classReactive public static class Person { private String firstName; private String lastName; }
- @propertyReactive
If property reactivity is set to
ALLOWED
in the decision engine (no types are property reactive unless specified), this tag enables property reactivity for a specific Java class or a declared DRL fact type. You can use this tag if you want the decision engine to react only to modified properties that are constrained or bound inside a given pattern for the specified fact type, instead of re-evaluating all fact patterns for the fact each time the rule is triggered.Example: Enable property reactivity in a DRL type declaration (when reactivity is disabled globally)
declare Person @propertyReactive firstName : String lastName : String end
Example: Enable property reactivity in a Java class (when reactivity is disabled globally)
@propertyReactive public static class Person { private String firstName; private String lastName; }
- @watch
This tag enables property reactivity for additional properties that you specify in-line in fact patterns in DRL rules. This tag is supported only if property reactivity is set to
ALWAYS
in the decision engine, or if property reactivity is set toALLOWED
and the relevant fact type uses the@propertyReactive
tag. You can use this tag in DRL rules to add or exclude specific properties in fact property reactivity logic.Default parameter: None
Supported parameters: Property name,
*
(all),!
(not),!*
(no properties)<factPattern> @watch ( <property> )
Example: Enable or disable property reactivity in fact patterns
// Listens for changes in both `firstName` (inferred) and `lastName`: Person(firstName == $expectedFirstName) @watch( lastName ) // Listens for changes in all properties of the `Person` fact: Person(firstName == $expectedFirstName) @watch( * ) // Listens for changes in `lastName` and explicitly excludes changes in `firstName`: Person(firstName == $expectedFirstName) @watch( lastName, !firstName ) // Listens for changes in all properties of the `Person` fact except `age`: Person(firstName == $expectedFirstName) @watch( *, !age ) // Excludes changes in all properties of the `Person` fact (equivalent to using `@classReactivity` tag): Person(firstName == $expectedFirstName) @watch( !* )
The decision engine generates a compilation error if you use the
@watch
tag for properties in a fact type that uses the@classReactive
tag (disables property reactivity) or when property reactivity is set toALLOWED
in the decision engine and the relevant fact type does not use the@propertyReactive
tag. Compilation errors also arise if you duplicate properties in listener annotations, such as@watch( firstName, ! firstName )
.- @propertyChangeSupport
For facts that implement support for property changes as defined in the JavaBeans Specification, this tag enables the decision engine to monitor changes in the fact properties.
Example: Declare property change support in JavaBeans object
declare Person @propertyChangeSupport end
16.5.7. Access to DRL declared types in application code
Declared types in DRL are typically used within the DRL files while Java models are typically used when the model is shared between rules and applications. Because declared types are generated at KIE base compile time, an application cannot access them until application run time. In some cases, an application needs to access and handle facts directly from the declared types, especially when the application wraps the decision engine and provides higher-level, domain-specific user interfaces for rules management.
To handle declared types directly from the application code, you can use the org.drools.definition.type.FactType
API in Red Hat Decision Manager. Through this API, you can instantiate, read, and write fields in the declared fact types.
The following example code modifies a Person
fact type directly from an application:
Example application code to handle a declared fact type through the FactType API
import java.util.Date; import org.kie.api.definition.type.FactType; import org.kie.api.KieBase; import org.kie.api.runtime.KieSession; ... // Get a reference to a KIE base with the declared type: KieBase kbase = ... // Get the declared fact type: FactType personType = kbase.getFactType("org.drools.examples", "Person"); // Create instances: Object bob = personType.newInstance(); // Set attribute values: personType.set(bob, "name", "Bob" ); personType.set(bob, "dateOfBirth", new Date()); personType.set(bob, "address", new Address("King's Road","London","404")); // Insert the fact into a KIE session: KieSession ksession = ... ksession.insert(bob); ksession.fireAllRules(); // Read attributes: String name = (String) personType.get(bob, "name"); Date date = (Date) personType.get(bob, "dateOfBirth");
The API also includes other helpful methods, such as setting all the attributes at once, reading values from a Map
collection, or reading all attributes at once into a Map
collection.
Although the API behavior is similar to Java reflection, the API does not use reflection and relies on more performant accessors that are implemented with generated bytecode.
16.6. Global variables in DRL
Global variables in DRL files typically provide data or services for the rules, such as application services used in rule consequences, and return data from rules, such as logs or values added in rule consequences. You set the global value in the working memory of the decision engine through a KIE session configuration or REST operation, declare the global variable above the rules in the DRL file, and then use it in an action (then
) part of the rule. For multiple global variables, use separate lines in the DRL file.
The following example illustrates a global variable list configuration for the decision engine and the corresponding global variable definition in the DRL file:
Example global list configuration for the decision engine
List<String> list = new ArrayList<>(); KieSession kieSession = kiebase.newKieSession(); kieSession.setGlobal( "myGlobalList", list );
Example global variable definition with a rule
global java.util.List myGlobalList; rule "Using a global" when // Empty then myGlobalList.add( "My global list" ); end
Do not use global variables to establish conditions in rules unless a global variable has a constant immutable value. Global variables are not inserted into the working memory of the decision engine, so the decision engine cannot track value changes of variables.
Do not use global variables to share data between rules. Rules always reason and react to the working memory state, so if you want to pass data from rule to rule, assert the data as facts into the working memory of the decision engine.
A use case for a global variable might be an instance of an email service. In your integration code that is calling the decision engine, you obtain your emailService
object and then set it in the working memory of the decision engine. In the DRL file, you declare that you have a global of type emailService
and give it the name "email"
, and then in your rule consequences, you can use actions such as email.sendSMS(number, message)
.
If you declare global variables with the same identifier in multiple packages, then you must set all the packages with the same type so that they all reference the same global value.
16.7. Rule attributes in DRL
Rule attributes are additional specifications that you can add to business rules to modify rule behavior. In DRL files, you typically define rule attributes above the rule conditions and actions, with multiple attributes on separate lines, in the following format:
rule "rule_name" // Attribute // Attribute when // Conditions then // Actions end
The following table lists the names and supported values of the attributes that you can assign to rules:
Attribute | Value |
---|---|
| An integer defining the priority of the rule. Rules with a higher salience value are given higher priority when ordered in the activation queue.
Example: |
| A Boolean value. When the option is selected, the rule is enabled. When the option is not selected, the rule is disabled.
Example: |
|
A string containing a date and time definition. The rule can be activated only if the current date and time is after a
Example: |
|
A string containing a date and time definition. The rule cannot be activated if the current date and time is after the
Example: |
| A Boolean value. When the option is selected, the rule cannot be reactivated (looped) if a consequence of the rule re-triggers a previously met condition. When the condition is not selected, the rule can be looped in these circumstances.
Example: |
| A string identifying an agenda group to which you want to assign the rule. Agenda groups allow you to partition the agenda to provide more execution control over groups of rules. Only rules in an agenda group that has acquired a focus are able to be activated.
Example: |
| A string identifying an activation (or XOR) group to which you want to assign the rule. In activation groups, only one rule can be activated. The first rule to fire will cancel all pending activations of all rules in the activation group.
Example: |
| A long integer value defining the duration of time in milliseconds after which the rule can be activated, if the rule conditions are still met.
Example: |
|
A string identifying either
Example: |
| A Quartz calendar definition for scheduling the rule.
Example: |
| A Boolean value, applicable only to rules within agenda groups. When the option is selected, the next time the rule is activated, a focus is automatically given to the agenda group to which the rule is assigned.
Example: |
|
A Boolean value, applicable only to rules within rule flow groups or agenda groups. When the option is selected, the next time the ruleflow group for the rule becomes active or the agenda group for the rule receives a focus, the rule cannot be activated again until the ruleflow group is no longer active or the agenda group loses the focus. This is a stronger version of the
Example: |
| A string identifying a rule flow group. In rule flow groups, rules can fire only when the group is activated by the associated rule flow.
Example: |
|
A string identifying either
Example: Note
When you use Red Hat Decision Manager without the executable model, the |
16.7.1. Timer and calendar rule attributes in DRL
Timers and calendars are DRL rule attributes that enable you to apply scheduling and timing constraints to your DRL rules. These attributes require additional configurations depending on the use case.
The timer
attribute in DRL rules is a string identifying either int
(interval) or cron
timer definitions for scheduling a rule and supports the following formats:
Timer attribute formats
timer ( int: <initial delay> <repeat interval> ) timer ( cron: <cron expression> )
Example interval timer attributes
// Run after a 30-second delay timer ( int: 30s ) // Run every 5 minutes after a 30-second delay each time timer ( int: 30s 5m )
Example cron timer attribute
// Run every 15 minutes timer ( cron:* 0/15 * * * ? )
Interval timers follow the semantics of java.util.Timer
objects, with an initial delay and an optional repeat interval. Cron timers follow standard Unix cron expressions.
The following example DRL rule uses a cron timer to send an SMS text message every 15 minutes:
Example DRL rule with a cron timer
rule "Send SMS message every 15 minutes" timer ( cron:* 0/15 * * * ? ) when $a : Alarm( on == true ) then channels[ "sms" ].insert( new Sms( $a.mobileNumber, "The alarm is still on." ); end
Generally, a rule that is controlled by a timer becomes active when the rule is triggered and the rule consequence is executed repeatedly, according to the timer settings. The execution stops when the rule condition no longer matches incoming facts. However, the way the decision engine handles rules with timers depends on whether the decision engine is in active mode or in passive mode.
By default, the decision engine runs in passive mode and evaluates rules, according to the defined timer settings, when a user or an application explicitly calls fireAllRules()
. Conversely, if a user or application calls fireUntilHalt()
, the decision engine starts in active mode and evaluates rules continually until the user or application explicitly calls halt()
.
When the decision engine is in active mode, rule consequences are executed even after control returns from a call to fireUntilHalt()
and the decision engine remains reactive to any changes made to the working memory. For example, removing a fact that was involved in triggering the timer rule execution causes the repeated execution to terminate, and inserting a fact so that some rule matches causes that rule to be executed. However, the decision engine is not continually active, but is active only after a rule is executed. Therefore, the decision engine does not react to asynchronous fact insertions until the next execution of a timer-controlled rule. Disposing a KIE session terminates all timer activity.
When the decision engine is in passive mode, rule consequences of timed rules are evaluated only when fireAllRules()
is invoked again. However, you can change the default timer-execution behavior in passive mode by configuring the KIE session with a TimedRuleExecutionOption
option, as shown in the following example:
KIE session configuration to automatically execute timed rules in passive mode
KieSessionConfiguration ksconf = KieServices.Factory.get().newKieSessionConfiguration(); ksconf.setOption( TimedRuleExecutionOption.YES ); KSession ksession = kbase.newKieSession(ksconf, null);
You can additionally set a FILTERED
specification on the TimedRuleExecutionOption
option that enables you to define a callback to filter those rules, as shown in the following example:
KIE session configuration to filter which timed rules are automatically executed
KieSessionConfiguration ksconf = KieServices.Factory.get().newKieSessionConfiguration(); conf.setOption( new TimedRuleExecutionOption.FILTERED(new TimedRuleExecutionFilter() { public boolean accept(Rule[] rules) { return rules[0].getName().equals("MyRule"); } }) );
For interval timers, you can also use an expression timer with expr
instead of int
to define both the delay and interval as an expression instead of a fixed value.
The following example DRL file declares a fact type with a delay and period that are then used in the subsequent rule with an expression timer:
Example rule with an expression timer
declare Bean delay : String = "30s" period : long = 60000 end rule "Expression timer" timer ( expr: $d, $p ) when Bean( $d : delay, $p : period ) then // Actions end
The expressions, such as $d
and $p
in this example, can use any variable defined in the pattern-matching part of the rule. The variable can be any String
value that can be parsed into a time duration or any numeric value that is internally converted in a long
value for a duration in milliseconds.
Both interval and expression timers can use the following optional parameters:
-
start
andend
: ADate
or aString
representing aDate
or along
value. The value can also be aNumber
that is transformed into a JavaDate
in the formatnew Date( ((Number) n).longValue() )
. -
repeat-limit
: An integer that defines the maximum number of repetitions allowed by the timer. If both theend
and therepeat-limit
parameters are set, the timer stops when the first of the two is reached.
Example timer attribute with optional start
, end
, and repeat-limit
parameters
timer (int: 30s 1h; start=3-JAN-2020, end=4-JAN-2020, repeat-limit=50)
In this example, the rule is scheduled for every hour, after a delay of 30 seconds each hour, beginning on 3 January 2020 and ending either on 4 January 2020 or when the cycle repeats 50 times.
If the system is paused (for example, the session is serialized and then later deserialized), the rule is scheduled only one time to recover from missing activations regardless of how many activations were missed during the pause, and then the rule is subsequently scheduled again to continue in sync with the timer setting.
The calendar
attribute in DRL rules is a Quartz calendar definition for scheduling a rule and supports the following format:
Calendar attribute format
calendars "<definition or registered name>"
Example calendar attributes
// Exclude non-business hours calendars "* * 0-7,18-23 ? * *" // Weekdays only, as registered in the KIE session calendars "weekday"
You can adapt a Quartz calendar based on the Quartz calendar API and then register the calendar in the KIE session, as shown in the following example:
Adapting a Quartz Calendar
Calendar weekDayCal = QuartzHelper.quartzCalendarAdapter(org.quartz.Calendar quartzCal)
Registering the calendar in the KIE session
ksession.getCalendars().set( "weekday", weekDayCal );
You can use calendars with standard rules and with rules that use timers. The calendar attribute can contain one or more comma-separated calendar names written as String
literals.
The following example rules use both calendars and timers to schedule the rules:
Example rules with calendars and timers
rule "Weekdays are high priority" calendars "weekday" timer ( int:0 1h ) when Alarm() then send( "priority high - we have an alarm" ); end rule "Weekends are low priority" calendars "weekend" timer ( int:0 4h ) when Alarm() then send( "priority low - we have an alarm" ); end
16.8. Rule conditions in DRL (WHEN)
The when
part of a DRL rule (also known as the Left Hand Side (LHS) of the rule) contains the conditions that must be met to execute an action. Conditions consist of a series of stated patterns and constraints, with optional bindings and supported rule condition elements (keywords), based on the available data objects in the package. For example, if a bank requires loan applicants to have over 21 years of age, then the when
condition of an "Underage"
rule would be Applicant( age < 21 )
.
DRL uses when
instead of if
because if
is typically part of a procedural execution flow during which a condition is checked at a specific point in time. In contrast, when
indicates that the condition evaluation is not limited to a specific evaluation sequence or point in time, but instead occurs continually at any time. Whenever the condition is met, the actions are executed.
If the when
section is empty, then the conditions are considered to be true and the actions in the then
section are executed the first time a fireAllRules()
call is made in the decision engine. This is useful if you want to use rules to set up the decision engine state.
The following example rule uses empty conditions to insert a fact every time the rule is executed:
Example rule without conditions
rule "Always insert applicant" when // Empty then // Actions to be executed once insert( new Applicant() ); end // The rule is internally rewritten in the following way: rule "Always insert applicant" when eval( true ) then insert( new Applicant() ); end
If rule conditions use multiple patterns with no defined keyword conjunctions (such as and
, or
, or not
), the default conjunction is and
:
Example rule without keyword conjunctions
rule "Underage" when application : LoanApplication() Applicant( age < 21 ) then // Actions end // The rule is internally rewritten in the following way: rule "Underage" when application : LoanApplication() and Applicant( age < 21 ) then // Actions end
16.8.1. Patterns and constraints
A pattern in a DRL rule condition is the segment to be matched by the decision engine. A pattern can potentially match each fact that is inserted into the working memory of the decision engine. A pattern can also contain constraints to further define the facts to be matched.
In the simplest form, with no constraints, a pattern matches a fact of the given type. In the following example, the type is Person
, so the pattern will match against all Person
objects in the working memory of the decision engine:
Example pattern for a single fact type
Person()
The type does not need to be the actual class of some fact object. Patterns can refer to superclasses or even interfaces, potentially matching facts from many different classes. For example, the following pattern matches all objects in the working memory of the decision engine:
Example pattern for all objects
Object() // Matches all objects in the working memory
The parentheses of a pattern enclose the constraints, such as the following constraint on the person’s age:
Example pattern with a constraint
Person( age == 50 )
A constraint is an expression that returns true
or false
. Pattern constraints in DRL are essentially Java expressions with some enhancements, such as property access, and some differences, such as equals()
and !equals()
semantics for ==
and !=
(instead of the usual same
and not same
semantics).
Any JavaBeans property can be accessed directly from pattern constraints. A bean property is exposed internally using a standard JavaBeans getter that takes no arguments and returns something. For example, the age
property is written as age
in DRL instead of the getter getAge()
:
DRL constraint syntax with JavaBeans properties
Person( age == 50 ) // This is the same as the following getter format: Person( getAge() == 50 )
Red Hat Decision Manager uses the standard JDK Introspector
class to achieve this mapping, so it follows the standard JavaBeans specification. For optimal decision engine performance, use the property access format, such as age
, instead of using getters explicitly, such as getAge()
.
Do not use property accessors to change the state of the object in a way that might affect the rules because the decision engine caches the results of the match between invocations for higher efficiency.
For example, do not use property accessors in the following ways:
public int getAge() { age++; // Do not do this. return age; }
public int getAge() { Date now = DateUtil.now(); // Do not do this. return DateUtil.differenceInYears(now, birthday); }
Instead of following the second example, insert a fact that wraps the current date in the working memory and update that fact between fireAllRules()
as needed.
However, if the getter of a property cannot be found, the compiler uses the property name as a fallback method name, without arguments:
Fallback method if object is not found
Person( age == 50 ) // If `Person.getAge()` does not exist, the compiler uses the following syntax: Person( age() == 50 )
You can also nest access properties in patterns, as shown in the following example. Nested properties are indexed by the decision engine.
Example pattern with nested property access
Person( address.houseNumber == 50 ) // This is the same as the following format: Person( getAddress().getHouseNumber() == 50 )
In stateful KIE sessions, use nested accessors carefully because the working memory of the decision engine is not aware of any of the nested values and does not detect when they change. Either consider the nested values immutable while any of their parent references are inserted into the working memory, or, if you want to modify a nested value, mark all of the outer facts as updated. In the previous example, when the houseNumber
property changes, any Person
with that Address
must be marked as updated.
You can use any Java expression that returns a boolean
value as a constraint inside the parentheses of a pattern. Java expressions can be mixed with other expression enhancements, such as property access:
Example pattern with a constraint using property access and Java expression
Person( age == 50 )
You can change the evaluation priority by using parentheses, as in any logical or mathematical expression:
Example evaluation order of constraints
Person( age > 100 && ( age % 10 == 0 ) )
You can also reuse Java methods in constraints, as shown in the following example:
Example constraints with reused Java methods
Person( Math.round( weight / ( height * height ) ) < 25.0 )
Do not use constraints to change the state of the object in a way that might affect the rules because the decision engine caches the results of the match between invocations for higher efficiency. Any method that is executed on a fact in the rule conditions must be a read-only method. Also, the state of a fact should not change between rule invocations unless those facts are marked as updated in the working memory on every change.
For example, do not use a pattern constraint in the following ways:
Person( incrementAndGetAge() == 10 ) // Do not do this.
Person( System.currentTimeMillis() % 1000 == 0 ) // Do not do this.
Standard Java operator precedence applies to constraint operators in DRL, and DRL operators follow standard Java semantics except for the ==
and !=
operators.
The ==
operator uses null-safe equals()
semantics instead of the usual same
semantics. For example, the pattern Person( firstName == "John" )
is similar to java.util.Objects.equals(person.getFirstName(), "John")
, and because "John"
is not null, the pattern is also similar to "John".equals(person.getFirstName())
.
The !=
operator uses null-safe !equals()
semantics instead of the usual not same
semantics. For example, the pattern Person( firstName != "John" )
is similar to !java.util.Objects.equals(person.getFirstName(), "John")
.
If the field and the value of a constraint are of different types, the decision engine uses type coercion to resolve the conflict and reduce compilation errors. For instance, if "ten"
is provided as a string in a numeric evaluator, a compilation error occurs, whereas "10"
is coerced to a numeric 10. In coercion, the field type always takes precedence over the value type:
Example constraint with a value that is coerced
Person( age == "10" ) // "10" is coerced to 10
For groups of constraints, you can use a delimiting comma ,
to use implicit and
connective semantics:
Example patterns with multiple constraints
// Person is at least 50 years old and weighs at least 80 kilograms: Person( age > 50, weight > 80 ) // Person is at least 50 years old, weighs at least 80 kilograms, and is taller than 2 meters: Person( age > 50, weight > 80, height > 2 )
Although the &&
and ,
operators have the same semantics, they are resolved with different priorities. The &&
operator precedes the ||
operator, and both the &&
and ||
operators together precede the ,
operator. Use the comma operator at the top-level constraint for optimal decision engine performance and human readability.
You cannot embed a comma operator in a composite constraint expression, such as in parentheses:
Example of misused comma in composite constraint expression
// Do not use the following format: Person( ( age > 50, weight > 80 ) || height > 2 ) // Use the following format instead: Person( ( age > 50 && weight > 80 ) || height > 2 )
16.8.2. Bound variables in patterns and constraints
You can bind variables to patterns and constraints to refer to matched objects in other portions of a rule. Bound variables can help you define rules more efficiently or more consistently with how you annotate facts in your data model. To differentiate more easily between variables and fields in a rule, use the standard format $variable
for variables, especially in complex rules. This convention is helpful but not required in DRL.
For example, the following DRL rule uses the variable $p
for a pattern with the Person
fact:
Pattern with a bound variable
rule "simple rule" when $p : Person() then System.out.println( "Person " + $p ); end
Similarly, you can also bind variables to properties in pattern constraints, as shown in the following example:
// Two persons of the same age: Person( $firstAge : age ) // Binding Person( age == $firstAge ) // Constraint expression
Constraint binding considers only the first atomic expression that follows it. In the following example the pattern only binds the age of the person to the variable $a
:
Person( $a : age * 2 < 100 )
For clearer and more efficient rule definitions, separate constraint bindings and constraint expressions. Although mixed bindings and expressions are supported, which can complicate patterns and affect evaluation efficiency.
// Do not use the following format: Person( $a : age * 2 < 100 ) // Use the following format instead: Person( age * 2 < 100, $a : age )
In the preceding example, if you want to bind to the variable $a
the double of the person’s age, you must make it an atomic expression by wrapping it in parentheses as shown in the following example:
Person( $a : (age * 2) )
The decision engine does not support bindings to the same declaration, but does support unification of arguments across several properties. While positional arguments are always processed with unification, the unification symbol :=
exists for named arguments.
The following example patterns unify the age
property across two Person
facts:
Example pattern with unification
Person( $age := age ) Person( $age := age )
Unification declares a binding for the first occurrence and constrains to the same value of the bound field for sequence occurrences.
16.8.3. Nested constraints and inline casts
In some cases, you might need to access multiple properties of a nested object, as shown in the following example:
Example pattern to access multiple properties
Person( name == "mark", address.city == "london", address.country == "uk" )
You can group these property accessors to nested objects with the syntax .( <constraints> )
for more readable rules, as shown in the following example:
Example pattern with grouped constraints
Person( name == "mark", address.( city == "london", country == "uk") )
The period prefix .
differentiates the nested object constraints from a method call.
When you work with nested objects in patterns, you can use the syntax <type>#<subtype>
to cast to a subtype and make the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes, as shown in the following examples:
Example patterns with inline casting to a subtype
// Inline casting with subtype name: Person( name == "mark", address#LongAddress.country == "uk" ) // Inline casting with fully qualified class name: Person( name == "mark", address#org.domain.LongAddress.country == "uk" ) // Multiple inline casts: Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 )
These example patterns cast Address
to LongAddress
, and additionally to DetailedCountry
in the last example, making the parent getters available to the subtypes in each case.
You can use the instanceof
operator to infer the results of the specified type in subsequent uses of that field with the pattern, as shown in the following example:
Person( name == "mark", address instanceof LongAddress, address.country == "uk" )
If an inline cast is not possible (for example, if instanceof
returns false
), the evaluation is considered false
.
16.8.4. Date literal in constraints
By default, the decision engine supports the date format dd-mmm-yyyy
. You can customize the date format, including a time format mask if needed, by providing an alternative format mask with the system property drools.dateformat="dd-mmm-yyyy hh:mm"
. You can also customize the date format by changing the language locale with the drools.defaultlanguage
and drools.defaultcountry
system properties (for example, the locale of Thailand is set as drools.defaultlanguage=th
and drools.defaultcountry=TH
).
Example pattern with a date literal restriction
Person( bornBefore < "27-Oct-2009" )
16.8.5. Supported operators in DRL pattern constraints
DRL supports standard Java semantics for operators in pattern constraints, with some exceptions and with some additional operators that are unique in DRL. The following list summarizes the operators that are handled differently in DRL constraints than in standard Java semantics or that are unique in DRL constraints.
.()
,#
Use the
.()
operator to group property accessors to nested objects, and use the#
operator to cast to a subtype in nested objects. Casting to a subtype makes the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes.Example patterns with nested objects
// Ungrouped property accessors: Person( name == "mark", address.city == "london", address.country == "uk" ) // Grouped property accessors: Person( name == "mark", address.( city == "london", country == "uk") )
NoteThe period prefix
.
differentiates the nested object constraints from a method call.Example patterns with inline casting to a subtype
// Inline casting with subtype name: Person( name == "mark", address#LongAddress.country == "uk" ) // Inline casting with fully qualified class name: Person( name == "mark", address#org.domain.LongAddress.country == "uk" ) // Multiple inline casts: Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 )
!.
Use this operator to dereference a property in a null-safe way. The value to the left of the
!.
operator must be not null (interpreted as!= null
) in order to give a positive result for pattern matching.Example constraint with null-safe dereferencing
Person( $streetName : address!.street ) // This is internally rewritten in the following way: Person( address != null, $streetName : address.street )
[]
Use this operator to access a
List
value by index or aMap
value by key.Example constraints with
List
andMap
access// The following format is the same as `childList(0).getAge() == 18`: Person(childList[0].age == 18) // The following format is the same as `credentialMap.get("jdoe").isValid()`: Person(credentialMap["jdoe"].valid)
<
,<=
,>
,>=
Use these operators on properties with natural ordering. For example, for
Date
fields, the<
operator means before, and forString
fields, the operator means alphabetically before. These properties apply only to comparable properties.Example constraints with
before
operatorPerson( birthDate < $otherBirthDate ) Person( firstName < $otherFirstName )
==
,!=
Use these operators as
equals()
and!equals()
methods in constraints, instead of the usualsame
andnot same
semantics.Example constraint with null-safe equality
Person( firstName == "John" ) // This is similar to the following formats: java.util.Objects.equals(person.getFirstName(), "John") "John".equals(person.getFirstName())
Example constraint with null-safe not equality
Person( firstName != "John" ) // This is similar to the following format: !java.util.Objects.equals(person.getFirstName(), "John")
&&
,||
Use these operators to create an abbreviated combined relation condition that adds more than one restriction on a field. You can group constraints with parentheses
()
to create a recursive syntax pattern.Example constraints with abbreviated combined relation
// Simple abbreviated combined relation condition using a single `&&`: Person(age > 30 && < 40) // Complex abbreviated combined relation using groupings: Person(age ((> 30 && < 40) || (> 20 && < 25))) // Mixing abbreviated combined relation with constraint connectives: Person(age > 30 && < 40 || location == "london")
matches
,not matches
Use these operators to indicate that a field matches or does not match a specified Java regular expression. Typically, the regular expression is a
String
literal, but variables that resolve to a valid regular expression are also supported. These operators apply only toString
properties. If you usematches
against anull
value, the resulting evaluation is alwaysfalse
. If you usenot matches
against anull
value, the resulting evaluation is alwaystrue
. As in Java, regular expressions that you write asString
literals must use a double backslash\\
to escape.Example constraint to match or not match a regular expression
Person( country matches "(USA)?\\S*UK" ) Person( country not matches "(USA)?\\S*UK" )
contains
,not contains
Use these operators to verify whether a field that is an
Array
or aCollection
contains or does not contain a specified value. These operators apply toArray
orCollection
properties, but you can also use these operators in place ofString.contains()
and!String.contains()
constraints checks.Example constraints with
contains
andnot contains
for a Collection// Collection with a specified field: FamilyTree( countries contains "UK" ) FamilyTree( countries not contains "UK" ) // Collection with a variable: FamilyTree( countries contains $var ) FamilyTree( countries not contains $var )
Example constraints with
contains
andnot contains
for a String literal// Sting literal with a specified field: Person( fullName contains "Jr" ) Person( fullName not contains "Jr" ) // String literal with a variable: Person( fullName contains $var ) Person( fullName not contains $var )
NoteFor backward compatibility, the
excludes
operator is a supported synonym fornot contains
.memberOf
,not memberOf
Use these operators to verify whether a field is a member of or is not a member of an
Array
or aCollection
that is defined as a variable. TheArray
orCollection
must be a variable.Example constraints with
memberOf
andnot memberOf
with a CollectionFamilyTree( person memberOf $europeanDescendants ) FamilyTree( person not memberOf $europeanDescendants )
soundslike
Use this operator to verify whether a word has almost the same sound, using English pronunciation, as the given value (similar to the
matches
operator). This operator uses the Soundex algorithm.Example constraint with
soundslike
// Match firstName "Jon" or "John": Person( firstName soundslike "John" )
str
Use this operator to verify whether a field that is a
String
starts with or ends with a specified value. You can also use this operator to verify the length of theString
.Example constraints with
str
// Verify what the String starts with: Message( routingValue str[startsWith] "R1" ) // Verify what the String ends with: Message( routingValue str[endsWith] "R2" ) // Verify the length of the String: Message( routingValue str[length] 17 )
in
,notin
Use these operators to specify more than one possible value to match in a constraint (compound value restriction). This functionality of compound value restriction is supported only in the
in
andnot in
operators. The second operand of these operators must be a comma-separated list of values enclosed in parentheses. You can provide values as variables, literals, return values, or qualified identifiers. These operators are internally rewritten as a list of multiple restrictions using the operators==
or!=
.Example constraints with
in
andnotin
Person( $color : favoriteColor ) Color( type in ( "red", "blue", $color ) ) Person( $color : favoriteColor ) Color( type notin ( "red", "blue", $color ) )
16.8.6. Operator precedence in DRL pattern constraints
DRL supports standard Java operator precedence for applicable constraint operators, with some exceptions and with some additional operators that are unique in DRL. The following table lists DRL operator precedence where applicable, from highest to lowest precedence:
Operator type | Operators | Notes |
---|---|---|
Nested or null-safe property access |
| Not standard Java semantics |
|
| Not standard Java semantics |
Constraint binding |
| Not standard Java semantics |
Multiplicative |
| |
Additive |
| |
Shift |
| |
Relational |
| |
Equality |
|
Uses |
Non-short-circuiting |
| |
Non-short-circuiting exclusive |
| |
Non-short-circuiting inclusive |
| |
Logical |
| |
Logical |
| |
Ternary |
| |
Comma-separated |
| Not standard Java semantics |
16.8.7. Supported rule condition elements in DRL (keywords)
DRL supports the following rule condition elements (keywords) that you can use with the patterns that you define in DRL rule conditions:
and
Use this to group conditional components into a logical conjunction. Infix and prefix
and
are supported. You can group patterns explicitly with parentheses()
. By default, all listed patterns are combined withand
when no conjunction is specified.Example patterns with
and
//Infix `and`: Color( colorType : type ) and Person( favoriteColor == colorType ) //Infix `and` with grouping: (Color( colorType : type ) and (Person( favoriteColor == colorType ) or Person( favoriteColor == colorType )) // Prefix `and`: (and Color( colorType : type ) Person( favoriteColor == colorType )) // Default implicit `and`: Color( colorType : type ) Person( favoriteColor == colorType )
NoteDo not use a leading declaration binding with the
and
keyword (as you can withor
, for example). A declaration can only reference a single fact at a time, and if you use a declaration binding withand
, then whenand
is satisfied, it matches both facts and results in an error.Example misuse of
and
// Causes compile error: $person : (Person( name == "Romeo" ) and Person( name == "Juliet"))
or
Use this to group conditional components into a logical disjunction. Infix and prefix
or
are supported. You can group patterns explicitly with parentheses()
. You can also use pattern binding withor
, but each pattern must be bound separately.Example patterns with
or
//Infix `or`: Color( colorType : type ) or Person( favoriteColor == colorType ) //Infix `or` with grouping: (Color( colorType : type ) or (Person( favoriteColor == colorType ) and Person( favoriteColor == colorType )) // Prefix `or`: (or Color( colorType : type ) Person( favoriteColor == colorType ))
Example patterns with
or
and pattern bindingpensioner : (Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 )) (or pensioner : Person( sex == "f", age > 60 ) pensioner : Person( sex == "m", age > 65 ))
The decision engine does not directly interpret the
or
element but uses logical transformations to rewrite a rule withor
as a number of sub-rules. This process ultimately results in a rule that has a singleor
as the root node and one sub-rule for each of its condition elements. Each sub-rule is activated and executed like any normal rule, with no special behavior or interaction between the sub-rules.Therefore, consider the
or
condition element a shortcut for generating two or more similar rules that, in turn, can create multiple activations when two or more terms of the disjunction are true.exists
Use this to specify facts and constraints that must exist. This option is triggered on only the first match, not subsequent matches. If you use this element with multiple patterns, enclose the patterns with parentheses
()
.Example patterns with
exists
exists Person( firstName == "John") exists (Person( firstName == "John", age == 42 )) exists (Person( firstName == "John" ) and Person( lastName == "Doe" ))
not
Use this to specify facts and constraints that must not exist. If you use this element with multiple patterns, enclose the patterns with parentheses
()
.Example patterns with
not
not Person( firstName == "John") not (Person( firstName == "John", age == 42 )) not (Person( firstName == "John" ) and Person( lastName == "Doe" ))
forall
Use this to verify whether all facts that match the first pattern match all the remaining patterns. When a
forall
construct is satisfied, the rule evaluates totrue
. This element is a scope delimiter, so it can use any previously bound variable, but no variable bound inside of it is available for use outside of it.Example rule with
forall
rule "All full-time employees have red ID badges" when forall( $emp : Employee( type == "fulltime" ) Employee( this == $emp, badgeColor = "red" ) ) then // True, all full-time employees have red ID badges. end
In this example, the rule selects all
Employee
objects whose type is"fulltime"
. For each fact that matches this pattern, the rule evaluates the patterns that follow (badge color) and if they match, the rule evaluates totrue
.To state that all facts of a given type in the working memory of the decision engine must match a set of constraints, you can use
forall
with a single pattern for simplicity.Example rule with
forall
and a single patternrule "All full-time employees have red ID badges" when forall( Employee( badgeColor = "red" ) ) then // True, all full-time employees have red ID badges. end
You can use
forall
constructs with multiple patterns or nest them with other condition elements, such as inside anot
element construct.Example rule with
forall
and multiple patternsrule "All employees have health and dental care programs" when forall( $emp : Employee() HealthCare( employee == $emp ) DentalCare( employee == $emp ) ) then // True, all employees have health and dental care. end
Example rule with
forall
andnot
rule "Not all employees have health and dental care" when not ( forall( $emp : Employee() HealthCare( employee == $emp ) DentalCare( employee == $emp ) ) ) then // True, not all employees have health and dental care. end
NoteThe format
forall( p1 p2 p3 …)
is equivalent tonot( p1 and not( and p2 p3 … ) )
.from
Use this to specify a data source for a pattern. This enables the decision engine to reason over data that is not in the working memory. The data source can be a sub-field on a bound variable or the result of a method call. The expression used to define the object source is any expression that follows regular MVEL syntax. Therefore, the
from
element enables you to easily use object property navigation, execute method calls, and access maps and collection elements.Example rule with
from
and pattern bindingrule "Validate zipcode" when Person( $personAddress : address ) Address( zipcode == "23920W" ) from $personAddress then // Zip code is okay. end
Example rule with
from
and a graph notationrule "Validate zipcode" when $p : Person() $a : Address( zipcode == "23920W" ) from $p.address then // Zip code is okay. end
Example rule with
from
to iterate over all objectsrule "Apply 10% discount to all items over US$ 100 in an order" when $order : Order() $item : OrderItem( value > 100 ) from $order.items then // Apply discount to `$item`. end
NoteFor large collections of objects, instead of adding an object with a large graph that the decision engine must iterate over frequently, add the collection directly to the KIE session and then join the collection in the condition, as shown in the following example:
when $order : Order() OrderItem( value > 100, order == $order )
Example rule with
from
andlock-on-active
rule attributerule "Assign people in North Carolina (NC) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person() $a : Address( state == "NC" ) from $p.address then modify ($p) {} // Assign the person to sales region 1. end rule "Apply a discount to people in the city of Raleigh" ruleflow-group "test" lock-on-active true when $p : Person() $a : Address( city == "Raleigh" ) from $p.address then modify ($p) {} // Apply discount to the person. end
ImportantUsing
from
withlock-on-active
rule attribute can result in rules not being executed. You can address this issue in one of the following ways:-
Avoid using the
from
element when you can insert all facts into the working memory of the decision engine or use nested object references in your constraint expressions. -
Place the variable used in the
modify()
block as the last sentence in your rule condition. -
Avoid using the
lock-on-active
rule attribute when you can explicitly manage how rules within the same ruleflow group place activations on one another.
The pattern that contains a
from
clause cannot be followed by another pattern starting with a parenthesis. The reason for this restriction is that the DRL parser reads thefrom
expression as"from $l (String() or Number())"
and it cannot differentiate this expression from a function call. The simplest workaround to this is to wrap thefrom
clause in parentheses, as shown in the following example:Example rules with
from
used incorrectly and correctly// Do not use `from` in this way: rule R when $l : List() String() from $l (String() or Number()) then // Actions end // Use `from` in this way instead: rule R when $l : List() (String() from $l) (String() or Number()) then // Actions end
-
Avoid using the
entry-point
Use this to define an entry point, or event stream, corresponding to a data source for the pattern. This element is typically used with the
from
condition element. You can declare an entry point for events so that the decision engine uses data from only that entry point to evaluate the rules. You can declare an entry point either implicitly by referencing it in DRL rules or explicitly in your Java application.Example rule with
from entry-point
rule "Authorize withdrawal" when WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream" CheckingAccount( accountId == $ai, balance > $am ) then // Authorize withdrawal. end
Example Java application code with EntryPoint object and inserted facts
import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.EntryPoint; // Create your KIE base and KIE session as usual: KieSession session = ... // Create a reference to the entry point: EntryPoint atmStream = session.getEntryPoint("ATM Stream"); // Start inserting your facts into the entry point: atmStream.insert(aWithdrawRequest);
collect
Use this to define a collection of objects that the rule can use as part of the condition. The rule obtains the collection either from a specified source or from the working memory of the decision engine. The result pattern of the
collect
element can be any concrete class that implements thejava.util.Collection
interface and provides a default no-arg public constructor. You can use Java collections likeList
,LinkedList
, andHashSet
, or your own class. If variables are bound before thecollect
element in a condition, you can use the variables to constrain both your source and result patterns. However, any binding made inside thecollect
element is not available for use outside of it.Example rule with
collect
import java.util.List rule "Raise priority when system has more than three pending alarms" when $system : System() $alarms : List( size >= 3 ) from collect( Alarm( system == $system, status == 'pending' ) ) then // Raise priority because `$system` has three or more `$alarms` pending. end
In this example, the rule assesses all pending alarms in the working memory of the decision engine for each given system and groups them in a
List
. If three or more alarms are found for a given system, the rule is executed.You can also use the
collect
element with nestedfrom
elements, as shown in the following example:Example rule with
collect
and nestedfrom
import java.util.LinkedList; rule "Send a message to all parents" when $town : Town( name == 'Paris' ) $mothers : LinkedList() from collect( Person( children > 0 ) from $town.getPeople() ) then // Send a message to all parents. end
accumulate
Use this to iterate over a collection of objects, execute custom actions for each of the elements, and return one or more result objects (if the constraints evaluate to
true
). This element is a more flexible and powerful form of thecollect
condition element. You can use predefined functions in youraccumulate
conditions or implement custom functions as needed. You can also use the abbreviationacc
foraccumulate
in rule conditions.Use the following format to define
accumulate
conditions in rules:Preferred format for
accumulate
accumulate( <source pattern>; <functions> [;<constraints>] )
NoteAlthough the decision engine supports alternate formats for the
accumulate
element for backward compatibility, this format is preferred for optimal performance in rules and applications.The decision engine supports the following predefined
accumulate
functions. These functions accept any expression as input.-
average
-
min
-
max
-
count
-
sum
-
collectList
-
collectSet
In the following example rule,
min
,max
, andaverage
areaccumulate
functions that calculate the minimum, maximum, and average temperature values over all the readings for each sensor:Example rule with
accumulate
to calculate temperature valuesrule "Raise alarm" when $s : Sensor() accumulate( Reading( sensor == $s, $temp : temperature ); $min : min( $temp ), $max : max( $temp ), $avg : average( $temp ); $min < 20, $avg > 70 ) then // Raise the alarm. end
The following example rule uses the
average
function withaccumulate
to calculate the average profit for all items in an order:Example rule with
accumulate
to calculate average profitrule "Average profit" when $order : Order() accumulate( OrderItem( order == $order, $cost : cost, $price : price ); $avgProfit : average( 1 - $cost / $price ) ) then // Average profit for `$order` is `$avgProfit`. end
To use custom, domain-specific functions in
accumulate
conditions, create a Java class that implements theorg.kie.api.runtime.rule.AccumulateFunction
interface. For example, the following Java class defines a custom implementation of anAverageData
function:Example Java class with custom implementation of
average
function// An implementation of an accumulator capable of calculating average values public class AverageAccumulateFunction implements org.kie.api.runtime.rule.AccumulateFunction<AverageAccumulateFunction.AverageData> { public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } public void writeExternal(ObjectOutput out) throws IOException { } public static class AverageData implements Externalizable { public int count = 0; public double total = 0; public AverageData() {} public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { count = in.readInt(); total = in.readDouble(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(count); out.writeDouble(total); } } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#createContext() */ public AverageData createContext() { return new AverageData(); } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#init(java.io.Serializable) */ public void init(AverageData context) { context.count = 0; context.total = 0; } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#accumulate(java.io.Serializable, java.lang.Object) */ public void accumulate(AverageData context, Object value) { context.count++; context.total += ((Number) value).doubleValue(); } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#reverse(java.io.Serializable, java.lang.Object) */ public void reverse(AverageData context, Object value) { context.count--; context.total -= ((Number) value).doubleValue(); } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#getResult(java.io.Serializable) */ public Object getResult(AverageData context) { return new Double( context.count == 0 ? 0 : context.total / context.count ); } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#supportsReverse() */ public boolean supportsReverse() { return true; } /* (non-Javadoc) * @see org.kie.api.runtime.rule.AccumulateFunction#getResultType() */ public Class< ? > getResultType() { return Number.class; } }
To use the custom function in a DRL rule, import the function using the
import accumulate
statement:Format to import a custom function
import accumulate <class_name> <function_name>
Example rule with the imported
average
functionimport accumulate AverageAccumulateFunction.AverageData average rule "Average profit" when $order : Order() accumulate( OrderItem( order == $order, $cost : cost, $price : price ); $avgProfit : average( 1 - $cost / $price ) ) then // Average profit for `$order` is `$avgProfit`. end
-
16.8.8. OOPath syntax with graphs of objects in DRL rule conditions
OOPath is an object-oriented syntax extension of XPath that is designed for browsing graphs of objects in DRL rule condition constraints. OOPath uses the compact notation from XPath for navigating through related elements while handling collections and filtering constraints, and is specifically useful for graphs of objects.
When the field of a fact is a collection, you can use the from
condition element (keyword) to bind and reason over all the items in that collection one by one. If you need to browse a graph of objects in the rule condition constraints, the extensive use of the from
condition element results in a verbose and repetitive syntax, as shown in the following example:
Example rule that browses a graph of objects with from
rule "Find all grades for Big Data exam" when $student: Student( $plan: plan ) $exam: Exam( course == "Big Data" ) from $plan.exams $grade: Grade() from $exam.grades then // Actions end
In this example, the domain model contains a Student
object with a Plan
of study. The Plan
can have zero or more Exam
instances and an Exam
can have zero or more Grade
instances. Only the root object of the graph, the Student
in this case, needs to be in the working memory of the decision engine for this rule setup to function.
As a more efficient alternative to using extensive from
statements, you can use the abbreviated OOPath syntax, as shown in the following example:
Example rule that browses a graph of objects with OOPath syntax
rule "Find all grades for Big Data exam" when Student( $grade: /plan/exams[course == "Big Data"]/grades ) then // Actions end
Formally, the core grammar of an OOPath expression is defined in extended Backus-Naur form (EBNF) notation in the following way:
EBNF notation for OOPath expressions
OOPExpr = [ID ( ":" | ":=" )] ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ; OOPSegment = ID ["#" ID] ["[" ( Number | Constraints ) "]"]
In practice, an OOPath expression has the following features and capabilities:
-
Starts with a forward slash
/
or with a question mark and forward slash?/
if it is a non-reactive OOPath expression (described later in this section). -
Can dereference a single property of an object with the period
.
operator. -
Can dereference multiple properties of an object with the forward slash
/
operator. If a collection is returned, the expression iterates over the values in the collection. Can filter out traversed objects that do not satisfy one or more constraints. The constraints are written as predicate expressions between square brackets, as shown in the following example:
Constraints as a predicate expression
Student( $grade: /plan/exams[ course == "Big Data" ]/grades )
Can downcast a traversed object to a subclass of the class declared in the generic collection. Subsequent constraints can also safely access the properties declared only in that subclass, as shown in the following example. Objects that are not instances of the class specified in this inline cast are automatically filtered out.
Constraints with downcast objects
Student( $grade: /plan/exams#AdvancedExam[ course == "Big Data", level > 3 ]/grades )
Can backreference an object of the graph that was traversed before the currently iterated graph. For example, the following OOPath expression matches only the grades that are above the average for the passed exam:
Constraints with backreferenced object
Student( $grade: /plan/exams/grades[ result > ../averageResult ] )
Can recursively be another OOPath expression, as shown in the following example:
Recursive constraint expression
Student( $exam: /plan/exams[ /grades[ result > 20 ] ] )
Can access objects by their index between square brackets
[]
, as shown in the following example. To adhere to Java convention, OOPath indexes are 0-based, while XPath indexes are 1-based.Constraints with access to objects by index
Student( $grade: /plan/exams[0]/grades )
OOPath expressions can be reactive or non-reactive. The decision engine does not react to updates involving a deeply nested object that is traversed during the evaluation of an OOPath expression.
To make these objects reactive to changes, modify the objects to extend the class org.drools.core.phreak.ReactiveObject
. After you modify an object to extend the ReactiveObject
class, the domain object invokes the inherited method notifyModification
to notify the decision engine when one of the fields has been updated, as shown in the following example:
Example object method to notify the decision engine that an exam has been moved to a different course
public void setCourse(String course) { this.course = course; notifyModification(this); }
With the following corresponding OOPath expression, when an exam is moved to a different course, the rule is re-executed and the list of grades matching the rule is recomputed:
Example OOPath expression from "Big Data" rule
Student( $grade: /plan/exams[ course == "Big Data" ]/grades )
You can also use the ?/
separator instead of the /
separator to disable reactivity in only one sub-portion of an OOPath expression, as shown in the following example:
Example OOPath expression that is partially non-reactive
Student( $grade: /plan/exams[ course == "Big Data" ]?/grades )
With this example, the decision engine reacts to a change made to an exam or if an exam is added to the plan, but not if a new grade is added to an existing exam.
If an OOPath portion is non-reactive, all remaining portions of the OOPath expression also become non-reactive. For example, the following OOPath expression is completely non-reactive:
Example OOPath expression that is completely non-reactive
Student( $grade: ?/plan/exams[ course == "Big Data" ]/grades )
For this reason, you cannot use the ?/
separator more than once in the same OOPath expression. For example, the following expression causes a compilation error:
Example OOPath expression with duplicate non-reactivity markers
Student( $grade: /plan?/exams[ course == "Big Data" ]?/grades )
Another alternative for enabling OOPath expression reactivity is to use the dedicated implementations for List
and Set
interfaces in Red Hat Decision Manager. These implementations are the ReactiveList
and ReactiveSet
classes. A ReactiveCollection
class is also available. The implementations also provide reactive support for performing mutable operations through the Iterator
and ListIterator
classes.
The following example class uses these classes to configure OOPath expression reactivity:
Example Java class to configure OOPath expression reactivity
public class School extends AbstractReactiveObject { private String name; private final List<Child> children = new ReactiveList<Child>(); 1 public void setName(String name) { this.name = name; notifyModification(); 2 } public void addChild(Child child) { children.add(child); 3 // No need to call `notifyModification()` here } }
- 1
- Uses the
ReactiveList
instance for reactive support over the standard JavaList
instance. - 2
- Uses the required
notifyModification()
method for when a field is changed in reactive support. - 3
- The
children
field is aReactiveList
instance, so thenotifyModification()
method call is not required. The notification is handled automatically, like all other mutating operations performed over thechildren
field.
16.9. Rule actions in DRL (THEN)
The then
part of the rule (also known as the Right Hand Side (RHS) of the rule) contains the actions to be performed when the conditional part of the rule has been met. Actions consist of one or more methods that execute consequences based on the rule conditions and on available data objects in the package. For example, if a bank requires loan applicants to be over 21 years of age (with a rule condition Applicant( age < 21 )
) and a loan applicant is under 21 years old, the then
action of an "Underage"
rule would be setApproved( false )
, declining the loan because the applicant is under age.
The main purpose of rule actions is to insert, delete, or modify data in the working memory of the decision engine. Effective rule actions are small, declarative, and readable. If you need to use imperative or conditional code in rule actions, then divide the rule into multiple smaller and more declarative rules.
Example rule for loan application age limit
rule "Underage" when application : LoanApplication() Applicant( age < 21 ) then application.setApproved( false ); application.setExplanation( "Underage" ); end
16.9.1. Supported rule action methods in DRL
DRL supports the following rule action methods that you can use in DRL rule actions. You can use these methods to modify the working memory of the decision engine without having to first reference a working memory instance. These methods act as shortcuts to the methods provided by the RuleContext
class in your Red Hat Decision Manager distribution.
For all rule action methods, download the Red Hat Process Automation Man