Main features are
- Complete in-memory rule engine with support for hundreds of thousands of conditions for each 1GB of RAM.
- Support for nested rules, where one of the rule's condition is that another rule has been fulfilled. Loop avoidance included with the feature-not-a-bug of unexpected outcomes.
- Produces a set of labels for each evaluated document(s).
The Dymo engine checks Conditions against Facts in order to add Labels to Documents. Its output is one or more sets of labels (LabelSet) each containing a Fact which contains a list of Labels and the Namespace for that label set.
A document Class is the specification of a certain family of documents and their respective schema.
A Document is a a set of Facts and is the input for the Engine.
A Label is part of the Engine output and is a tupple which contains the metadata of the Rule that has been fulfilled.
A Rule is a set of Conditions and is decomposed by the Engine into Deductions.
A Deduction triggers Actions when one or more Conditions are fulfilled.
An Action can be adding the Fact that a Deduction has been fulfilled and/or adding a Label.
A Session represents the evaluation that starts when a new set of Facts is sent to the rules Engine and ends when all Facts have been checked. The Session then returns the output of the evaluation which is the list of Labels.
Please check the examples
- Example #1: using Dymo as a Service which is a rule storage service.
- Example #2 creating rules with code.
Rules are how the engine is configured. It is outside of dymo's control how rules are stored. Yet a parser subpackage is included for converting rules encoded as JSON into the basic conditions and actions already provided by Dymo.
The format is
{
"namespace": "list",
"id": "R-123",
"description": "Give 20% discount if price >= 10 except apple products or price >= 2000.",
"expressions": [
{"type": "cond", "class": "product", "prop": "brand", "op": "==", "rval": "apple", "negated": true },
{"type": "cond", "class": "product", "prop": "price", "op": ">=", "rval": 10.00, "negated": false },
{"type": "join", "op": "and"},
{"type": "cond", "class": "product", "prop": "price", "op": ">=", "rval": 2000.00, "negated": false },
{"type": "join", "op": "or"}
{"type": "cond", "class": "DYMO_RULE", "prop": "id", "op": "==", "rval": "L-2", "negated": false },
{"type": "join", "op": "or"}
]
}The id must be a unique rule identifier, even between namespaces.
The namespace is used only to split sets of labels. Note that the recommendation is to have a single namespace for each engine and split your rules across multiple engines instead since it is faster to check two unrelated sets of rules instead of combining them into a single engine.
The description is not used by the engine and is just as a human readable identifier for the rule.
Field expressions is the set of sub expressions which represent a rule. These are combinations of condition and join-condition nodes in reverse polish (postfix) notation. The Backus-Naur format for these conditions is
EXPR ::= EXPR EXPR JOIN | COND
COND ::= CLASS PROP OP RVAL NEGATE
JOIN ::= "AND" | "OR"
CLASS ::= WORD
PROP ::= WORD
OP ::= "==" | ">=" | ">" | "<" | "<=" | "C" | "P" | "S" | WORD
NEGATE ::= true | false
WORD ::= "[a-zA-Z][-_a-zA-Z0-9]*"
Note that not all operators can be applied to all rules. The parser, when using the condition builder, will infer the property type and if the rval is identified as a number you can't execute string operations in it. You can override this and construct the Dymo Rule as you see fit.
Type join
{
"type": "join",
"op": "<op>
}Valid operators for type join
ORAND
Type cond
{
"type": "cond",
"op": "<op>",
"class": "<class>",
"propr": "<prop>",
"rval": <rval>,
"negate": true
}RVal can be one of
- string
- numeric
- boolean
- (not yet supported by Dymo) array of strings
- (not yet supported by Dymo) array of numbers
Valid operators for type cond and RVal type number
==>>=<<=
Valid operators for type cond and RVal type string
==: if lval and rval are numeric then applies numeric comparison, otherwise it uses lexicographical comparison>: if lval and rval are numeric then applies numeric comparison, otherwise it uses lexicographical comparison>=: if lval and rval are numeric then applies numeric comparison, otherwise it uses lexicographical comparison<: if lval and rval are numeric then applies numeric comparison, otherwise it uses lexicographical comparison<=: if lval and rval are numeric then applies numeric comparison, otherwise it uses lexicographical comparisonE: lval equals rvalC: lval contains rvalP: lval prefixed by rvalS: lval suffixed by rval
Valid operators for type cond and RVal type bool
==:
Valid operators for type cond and RVal type []number
IN: lval is in rval
Valid operators for type cond and RVal type []string
IN: lval is in rval