Simple yet powerful rules engine that offers the flexibility of using the built-in engine and creating a custom one.
Below are the available engines that can be used to evaluate expressions:
This engine uses only Kotlin code to support all Operator functions, offering expressive performance. Although it doesn't support Kotlin expressions inside the expression operands, it can be a suitable choice for simpler rule sets or projects where you prefer using a statically-typed language like Kotlin.
Supported types:
- primitive Java types, boolean, string, number (extends)
- custom objects (reflection)
- maps
- lists
- arrays
val engine = KotlinEvalEngine()
implementation "com.rapatao.ruleset:kotlin-evaluator:$rulesetVersion"
<dependency>
<groupId>com.rapatao.ruleset</groupId>
<artifactId>kotlin-evaluator</artifactId>
<version>$rulesetVersion</version>
</dependency>
Mozilla Rhino is an open-source, embeddable JavaScript interpreter from Mozilla. This engine implementation supports using JavaScript expressions inside the rule operands and is particularly useful when rules contain complex logic or when you want to leverage JavaScript's extensive library of functions.
val engine = RhinoEvalEngine()
implementation "com.rapatao.ruleset:rhino-evaluator:$rulesetVersion"
<dependency>
<groupId>com.rapatao.ruleset</groupId>
<artifactId>rhino-evaluator</artifactId>
<version>$rulesetVersion</version>
</dependency>
GraalJS is a high-performance JavaScript engine. This engine implementation supports using JavaScript expressions inside the rule operands and is particularly useful when rules contain complex logic or when you want to leverage JavaScript's extensive library of functions.
val engine = GraalJSEvalEngine()
implementation "com.rapatao.ruleset:graaljs-evaluator:$rulesetVersion"
<dependency>
<groupId>com.rapatao.ruleset</groupId>
<artifactId>graaljs-evaluator</artifactId>
<version>$rulesetVersion</version>
</dependency>
After adding the desired engine as the application dependency, copy and past the following code, replacing
the val engine: EvalEngine = ...
by the desired engine initialization instruction.
The following example initializes an Evaluator
, and check if the given rule
is valid to the given input
data,
printing the result
in the default output.
import com.rapatao.projects.ruleset.engine.Evaluator
import com.rapatao.projects.ruleset.engine.context.EvalEngine
import com.rapatao.projects.ruleset.engine.types.builder.equalsTo
val rule = "item.price" equalsTo 0
val input = mapOf("item" to mapOf("price" to 0))
val engine: EvalEngine = ...
val evaluator = Evaluator(engine = engine)
val result = evaluator.evaluate(rule, input)
println(result) // true
data class Item(val price: Double)
data class Input(val item: Item)
val result2 = evaluator.evaluate(rule, Input(item = Item(price = 0.0)))
println(result) // true
The engine only supports boolean
evaluations, which means that all operations must results in a boolean value.
All provided operations can be created using the
builder: com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder
To create custom operations, just extends the interface com.rapatao.projects.ruleset.engine.types.Expression
operator | description |
---|---|
EQUALS | Represents the equality operator (==), used to check if two values are equal. |
NOT_EQUALS | Represents the inequality operator (!=), used to check if two values are not equal. |
GREATER_THAN | Represents the greater than operator (>), used to compare if one value is greater than another. |
GREATER_OR_EQUAL_THAN | Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to another. |
LESS_THAN | Represents the less than operator (<), used to compare if one value is less than another. |
LESS_OR_EQUAL_THAN | Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to another. |
STARTS_WITH | Represents the operation to check if a string starts with a specified sequence of characters. |
ENDS_WITH | Represents the operation to check if a string ends with a specified sequence of characters. |
CONTAINS | Represents the operation to check if a string contains a specified sequence of characters or if an array contains a particular element. |
"field".isTrue()
"field".isFalse()
"field" equalsTo 10
"field" equalsTo "\"value\""
"field" equalsTo "value"
"field" notEqualsTo 10
"field" notEqualsTo "\"value\""
"field" notEqualsTo "value"
"field" greaterThan 10
"field" greaterOrEqualThan 10
"field" lessThan 10
"field" lessOrEqualThan 10
"field" startsWith "\"value\""
"field" endsWith "\"value\""
"field" expContains "\"value\""
A grouped operation is true
when all inner operations result in:
anyMatch
: any operation must be evaluated astrue
allMatch
: all operations must be evaluated astrue
noneMatch
: all operations must be evaluated asfalse
allMatch(
"field".isTrue(),
"price" lessThan 10.0,
),
anyMatch(
"field".isTrue(),
"price" lessThan 10.0,
),
noneMatch(
"field".isTrue(),
"price" lessThan 10.0,
),
Expression(
allMatch = listOf(
"field".isTrue(),
"price" lessThan 10.0,
),
anyMatch = listOf(
"field".isTrue(),
"price" lessThan 10.0,
),
noneMatch = listOf(
"field".isTrue(),
"price" lessThan 10.0,
)
)
All provided operations supports serialization using Jackson with the definition of a Mixin.
Mixin interface: com.rapatao.projects.ruleset.jackson.ExpressionMixin
Example of usage:
val mapper = jacksonObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.addMixIn(Expression::class.java, ExpressionMixin::class.java)
val json = "{ definition as json }"
val asMatcher: Expression = mapper.readValue(json)
Serialized examples can be checked here
Although the example only uses
JSON
as reference, by using the givenMix-in
class, it should support any serialization format provided by the Jackson library, likeYAML
andXML
.