diff --git a/JSON.md b/JSON.md index 8db25fa..de3624b 100644 --- a/JSON.md +++ b/JSON.md @@ -9,7 +9,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -17,7 +17,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "true" } ``` @@ -25,7 +25,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : 10 } ``` @@ -33,7 +33,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "\"value\"" } ``` @@ -41,7 +41,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "value" } ``` @@ -51,7 +51,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : true } ``` @@ -59,7 +59,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "true" } ``` @@ -67,7 +67,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 10 } ``` @@ -75,7 +75,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "\"value\"" } ``` @@ -83,7 +83,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "value" } ``` @@ -93,7 +93,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 } ``` @@ -103,7 +103,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -113,7 +113,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -123,7 +123,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -133,7 +133,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"value\"" } ``` @@ -143,7 +143,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"value\"" } ``` @@ -153,7 +153,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"value\"" } ``` @@ -164,11 +164,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -180,11 +180,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -196,11 +196,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -212,29 +212,29 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ], "anyMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ], "noneMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -248,7 +248,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "EQUALS", + "operator" : "equals", "right" : 10 } ``` @@ -256,7 +256,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "EQUALS", + "operator" : "equals", "right" : 0 } ``` @@ -264,7 +264,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 0 } ``` @@ -272,7 +272,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 10 } ``` @@ -280,7 +280,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : true, - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -288,7 +288,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : false, - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -296,7 +296,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : true, - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -304,7 +304,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : false, - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -312,7 +312,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -320,7 +320,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -328,7 +328,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.falseValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -336,7 +336,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.falseValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -344,7 +344,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : "true" } ``` @@ -352,7 +352,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "false" } ``` @@ -360,7 +360,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : false } ``` @@ -368,7 +368,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.field.that.dont.exist", - "operator" : "EQUALS", + "operator" : "equals", "right" : "10", "onFailure" : "TRUE" } @@ -377,7 +377,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.field.that.dont.exist", - "operator" : "EQUALS", + "operator" : "equals", "right" : "10", "onFailure" : "FALSE" } @@ -387,11 +387,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -401,11 +401,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -415,11 +415,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -429,11 +429,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -443,11 +443,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 9 } ] } @@ -457,11 +457,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 9 } ] } @@ -471,11 +471,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 9 } ] } @@ -485,11 +485,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 9 } ] } @@ -499,11 +499,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -513,11 +513,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -527,11 +527,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -541,11 +541,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -555,11 +555,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -569,11 +569,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -583,11 +583,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -597,11 +597,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -611,11 +611,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ] } @@ -625,11 +625,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ] } @@ -639,11 +639,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ] } @@ -653,11 +653,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ] } @@ -666,7 +666,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 0 } ``` @@ -674,7 +674,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 } ``` @@ -682,7 +682,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -690,7 +690,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -698,7 +698,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 30, - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -706,7 +706,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 100 } ``` @@ -714,7 +714,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 1 } ``` @@ -722,7 +722,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -730,7 +730,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 2, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -738,7 +738,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 20, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -746,7 +746,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -754,7 +754,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 100 } ``` @@ -762,7 +762,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -770,7 +770,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 1 } ``` @@ -778,7 +778,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 100 } ``` @@ -786,7 +786,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -794,7 +794,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 1 } ``` @@ -802,7 +802,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 1 } ``` @@ -810,7 +810,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -818,7 +818,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 100 } ``` @@ -826,7 +826,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 1 } ``` @@ -834,7 +834,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -842,7 +842,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 100 } ``` @@ -851,7 +851,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -861,7 +861,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -871,11 +871,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "10000" } ] } @@ -885,11 +885,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -899,11 +899,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -913,11 +913,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -927,12 +927,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -943,12 +943,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -959,12 +959,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -975,12 +975,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -991,12 +991,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1007,12 +1007,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1023,24 +1023,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1051,24 +1051,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1079,24 +1079,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1107,24 +1107,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1135,7 +1135,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1145,7 +1145,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1155,11 +1155,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1169,11 +1169,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1183,11 +1183,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1197,11 +1197,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1211,12 +1211,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1227,12 +1227,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1243,12 +1243,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1259,12 +1259,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1275,12 +1275,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1291,12 +1291,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1307,24 +1307,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1335,24 +1335,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1363,24 +1363,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1391,24 +1391,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1419,7 +1419,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1429,7 +1429,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1439,11 +1439,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1453,11 +1453,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1467,12 +1467,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1483,12 +1483,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1499,12 +1499,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1515,12 +1515,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1531,12 +1531,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1547,12 +1547,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1563,12 +1563,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1579,12 +1579,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1595,12 +1595,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1611,12 +1611,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1627,12 +1627,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1643,12 +1643,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1659,24 +1659,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1687,24 +1687,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1715,24 +1715,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1743,24 +1743,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1771,24 +1771,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1798,7 +1798,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.arrTags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"in_array\"" } ``` @@ -1806,7 +1806,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.arrTags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"not_in_array\"" } ``` @@ -1814,7 +1814,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"product\"" } ``` @@ -1822,7 +1822,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"name\"" } ``` @@ -1830,7 +1830,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"name\"" } ``` @@ -1838,7 +1838,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"product\"" } ``` @@ -1846,7 +1846,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"duct\"" } ``` @@ -1854,7 +1854,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"different value\"" } ``` @@ -1862,7 +1862,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.tags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"test\"" } ``` @@ -1870,7 +1870,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.tags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"different value\"" } ``` diff --git a/README.md b/README.md index 48c76aa..f6aaf2a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ 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 evaluator: Evaluator = ... +val evaluator: Evaluator = ... val result = evaluator.evaluate(rule, input) println(result) // true @@ -130,24 +130,47 @@ val result2 = evaluator.evaluate(rule, Input(item = Item(price = 0.0))) println(result) // true ``` -## Supported operations (expressions) +## Expressions (Rule) -The engine only supports `boolean` evaluations, which means that all operations must results in a boolean value. +In the context of the engine, an expression is a decision table, where many statements can be executed using defined +operators, resulting in a `boolean`, where `true` means that the given input data matches, and `false` when it doesn't +match. All provided operations can be created using the builder: `com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder` +### Operators + +The engine provides many built-in operators, but it also allows adding new ones or event overwriting the existing one. + +#### Built-in operators + | 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. | +| 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. | + +#### Customizing the operators + +It is possible to create custom operators by creating an implementation of the +interface `com.rapatao.projects.ruleset.engine.types.operators.Operators`. + +The function `name()` identifies the operator, which is used when evaluating the expressions. The engine supports a +single Operator per name, which means that it is not possible to have more than one using the same name. + +> Each built-in operator has its own class and all of them are located at the +> package `com.rapatao.projects.ruleset.engine.types.operators`. To override then it is not mandatory to use these base +> classes, it only need to have the same name as the built-in operator. + +There is no validation related to duplicated operator names, since it is required to allow overriding the built-in +operator by one implemented by the user of this library. ### Examples diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt index f26636b..ce476b4 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt @@ -3,11 +3,17 @@ package com.rapatao.projects.ruleset.engine import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.types.Expression import com.rapatao.projects.ruleset.engine.types.OnFailure +import com.rapatao.projects.ruleset.engine.types.errors.UnknownOperator +import com.rapatao.projects.ruleset.engine.types.operators.Operator /** * The Evaluator is a base class used to evaluate a given rule expression against input data. */ -abstract class Evaluator { +abstract class Evaluator( + operators: List, +) { + + private val declaredOperators = operators.associateBy { it.name().lowercase() } /** * Evaluates the given rule expression against the provided input data. @@ -48,6 +54,13 @@ abstract class Evaluator { */ abstract fun name(): String + /** + * Return the operator implementation for the given name. + * + * @return The operator. + */ + fun operator(name: String): Operator? = declaredOperators[name] + private fun List.processNoneMatch(context: EvalContext): Boolean { return this.none { usingFailureWrapper(it.onFailure) { @@ -89,7 +102,11 @@ abstract class Evaluator { private fun Expression.processExpression(context: EvalContext): Boolean { return usingFailureWrapper(this.onFailure) { - context.process(this) + requireNotNull(this.operator) { "expression operator must not be null" } + + val operator = operator(this.operator) ?: throw UnknownOperator(this.operator) + + context.process(this.left, operator, this.right) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt index 598fccb..3e5ad8d 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt @@ -1,6 +1,6 @@ package com.rapatao.projects.ruleset.engine.context -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator /** * Represents an evaluation context for processing expressions. @@ -8,11 +8,9 @@ import com.rapatao.projects.ruleset.engine.types.Expression fun interface EvalContext { /** - * Processes an expression. + * Process the expression using the given operator * - * @param expression the expression to process * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW */ - fun process(expression: Expression): Boolean + fun process(left: Any?, operator: Operator, right: Any?): Boolean } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt index c5148c6..e226421 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt @@ -1,5 +1,7 @@ package com.rapatao.projects.ruleset.engine.types +import com.rapatao.projects.ruleset.engine.Evaluator + /** * Represents an expression used in a logical query. * @@ -19,7 +21,7 @@ data class Expression( val anyMatch: List? = null, val noneMatch: List? = null, val left: Any? = null, - val operator: Operator? = null, + val operator: String? = null, val right: Any? = null, val onFailure: OnFailure = OnFailure.THROW, ) { @@ -32,15 +34,25 @@ data class Expression( * * @return Boolean value indicating whether the object is valid. */ - fun isValid(): Boolean { - val any = anyMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true - val none = noneMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true - val all = allMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true + fun isValid(engine: Evaluator): Boolean { + val any = anyMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + val none = noneMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + val all = allMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + + val something = (any && none && all) || parseable() + + return isValidGroup() && something && isValidOperator(engine) + } + + private fun isValidOperator(engine: Evaluator): Boolean { + val validOperator = operator == null || engine.operator(operator) != null + return validOperator + } + private fun isValidGroup(): Boolean { val has = anyMatch == null && noneMatch == null && allMatch == null && parseable() val group = anyMatch != null || noneMatch != null || allMatch != null - val something = (any && none && all) || parseable() - return (has || group) && something + return (has || group) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt deleted file mode 100644 index ffa52ea..0000000 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.rapatao.projects.ruleset.engine.types - -/** - * This enum class defines a set of operators that can be utilized in different contexts such as string comparison, - * numerical comparison, or array element verification. - */ -enum class Operator { - /** - * Represents the equality operator (==), used to check if two values are equal. - */ - EQUALS, - - /** - * Represents the inequality operator (!=), used to check if two values are not equal. - */ - NOT_EQUALS, - - /** - * Represents the greater than operator (>), used to compare if one value is greater than another. - */ - GREATER_THAN, - - /** - * Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to - * another. - */ - GREATER_OR_EQUAL_THAN, - - /** - * Represents the less than operator (<), used to compare if one value is less than another. - */ - LESS_THAN, - - /** - * Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to - * another. - */ - LESS_OR_EQUAL_THAN, - - /** - * Represents the operation to check if a string starts with a specified sequence of characters. - */ - STARTS_WITH, - - /** - * Represents the operation to check if a string ends with a specified sequence of characters. - */ - ENDS_WITH, - - /** - * Represents the operation to check if a string contains a specified sequence of characters or if an array contains - * a particular element. - */ - CONTAINS, -} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt index be2a9f1..2ce579b 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt @@ -1,7 +1,6 @@ package com.rapatao.projects.ruleset.engine.types.builder import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator /** * A builder class for constructing expressions representing a between condition. @@ -10,7 +9,7 @@ import com.rapatao.projects.ruleset.engine.types.Operator * @property from The starting value of the range. * @property operator The comparison operator for the condition. */ -data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) { +data class BetweenBuilder(val left: Any, val from: Any, val operator: String) { /** * Creates a non-inclusive range expression using the given `to` value. * @@ -19,7 +18,7 @@ data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) */ infix fun to(to: Any): Expression = MatcherBuilder.allMatch( ExpressionBuilder.expression(left = left, operator = operator, right = from), - ExpressionBuilder.expression(left = left, operator = Operator.LESS_THAN, right = to), + ExpressionBuilder.expression(left = left, operator = "less_than", right = to), ) /** @@ -30,7 +29,7 @@ data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) */ infix fun toInclusive(to: Any): Expression = MatcherBuilder.allMatch( ExpressionBuilder.expression(left = left, operator = operator, right = from), - ExpressionBuilder.expression(left = left, operator = Operator.LESS_OR_EQUAL_THAN, right = to), + ExpressionBuilder.expression(left = left, operator = "less_or_equal_than", right = to), ) } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt index 4df5cf8..a721f9c 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt @@ -1,16 +1,6 @@ package com.rapatao.projects.ruleset.engine.types.builder import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator -import com.rapatao.projects.ruleset.engine.types.Operator.CONTAINS -import com.rapatao.projects.ruleset.engine.types.Operator.ENDS_WITH -import com.rapatao.projects.ruleset.engine.types.Operator.EQUALS -import com.rapatao.projects.ruleset.engine.types.Operator.GREATER_OR_EQUAL_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.GREATER_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.LESS_OR_EQUAL_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.LESS_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.NOT_EQUALS -import com.rapatao.projects.ruleset.engine.types.Operator.STARTS_WITH /** * A utility class for building expressions using different operators. @@ -48,7 +38,7 @@ object ExpressionBuilder { * @param right The right operand of the expression. * @return The created expression. */ - fun expression(left: Any?, operator: Operator, right: Any?) = Expression( + fun expression(left: Any?, operator: String, right: Any?) = Expression( left = left, operator = operator, right = right ) @@ -66,7 +56,7 @@ object ExpressionBuilder { * @param right The right operand to compare. * @return The expression representing the "equals to" comparison. */ - infix fun equalsTo(right: Any?) = Expression(left = left, operator = EQUALS, right = right) + infix fun equalsTo(right: Any?) = Expression(left = left, operator = "equals", right = right) /** * Creates an expression representing the inequality comparison between the left-hand side and the right-hand @@ -75,7 +65,7 @@ object ExpressionBuilder { * @param right The right-hand side of the comparison. * @return A new expression representing the inequality comparison. */ - infix fun notEqualsTo(right: Any?) = Expression(left = left, operator = NOT_EQUALS, right = right) + infix fun notEqualsTo(right: Any?) = Expression(left = left, operator = "not_equals", right = right) /** * Creates an Expression object representing the greater-than comparison between the left operand and the right @@ -86,7 +76,7 @@ object ExpressionBuilder { * @return the Expression object representing the greater-than comparison */ infix fun greaterThan(right: Any) = - Expression(left = left, operator = GREATER_THAN, right = right) + Expression(left = left, operator = "greater_than", right = right) /** * Creates an expression that represents the "greater than or equal to" operation @@ -95,7 +85,7 @@ object ExpressionBuilder { * @return an expression representing the "greater than or equal to" operation */ infix fun greaterOrEqualThan(right: Any) = - Expression(left = left, operator = GREATER_OR_EQUAL_THAN, right = right) + Expression(left = left, operator = "greater_or_equal_than", right = right) /** * Creates an 'less than' expression with the specified 'right' value. @@ -103,7 +93,7 @@ object ExpressionBuilder { * @param right The right value of the expression. * @return The created expression. */ - infix fun lessThan(right: Any) = Expression(left = left, operator = LESS_THAN, right = right) + infix fun lessThan(right: Any) = Expression(left = left, operator = "less_than", right = right) /** * Creates an expression that represents the less than or equal to operation. @@ -112,21 +102,21 @@ object ExpressionBuilder { * @return An Expression object representing the less than or equal to operation. */ infix fun lessOrEqualThan(right: Any) = - Expression(left = left, operator = LESS_OR_EQUAL_THAN, right = right) + Expression(left = left, operator = "less_or_equal_than", right = right) /** * Checks if the left operand represents a boolean with value true. * * @return The boolean expression representing if the condition is true. */ - fun isTrue() = Expression(left = left, operator = EQUALS, right = true) + fun isTrue() = Expression(left = left, operator = "equals", right = true) /** * Checks if the left operand represents a boolean with value false. * * @return The boolean expression representing if the condition is false. */ - fun isFalse() = Expression(left = left, operator = EQUALS, right = false) + fun isFalse() = Expression(left = left, operator = "equals", right = false) /** * Creates an expression that checks if the left operand starts with the specified right operand. @@ -135,7 +125,7 @@ object ExpressionBuilder { * @return An Expression object representing the "starts with" condition. */ fun startsWith(right: Any?) = Expression( - left = left, operator = STARTS_WITH, right = right + left = left, operator = "starts_with", right = right ) /** @@ -145,7 +135,7 @@ object ExpressionBuilder { * @return An Expression object representing the "ends with" condition. */ fun endsWith(right: Any?) = Expression( - left = left, operator = ENDS_WITH, right = right + left = left, operator = "ends_with", right = right ) /** @@ -154,10 +144,10 @@ object ExpressionBuilder { * If the left operand is a list, it checks if the list contains the right operand. * * @param right The value to be checked for containment within the left operand. Can be of any type. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ fun contains(right: Any?) = Expression( - left = left, operator = CONTAINS, right = right + left = left, operator = "contains", right = right ) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt index b1c22ee..6744965 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt @@ -1,6 +1,5 @@ package com.rapatao.projects.ruleset.engine.types.builder.extensions -import com.rapatao.projects.ruleset.engine.types.Operator import com.rapatao.projects.ruleset.engine.types.builder.BetweenBuilder /** @@ -10,7 +9,7 @@ import com.rapatao.projects.ruleset.engine.types.builder.BetweenBuilder * @return A [BetweenBuilder] object to build the between condition. */ infix fun Any.from(from: Any): BetweenBuilder = BetweenBuilder( - left = this, from = from, operator = Operator.GREATER_THAN, + left = this, from = from, operator = "greater_than", ) /** @@ -20,5 +19,5 @@ infix fun Any.from(from: Any): BetweenBuilder = BetweenBuilder( * @return The [BetweenBuilder] instance for further chaining */ infix fun Any.fromInclusive(from: Any): BetweenBuilder = BetweenBuilder( - left = this, from = from, operator = Operator.GREATER_OR_EQUAL_THAN, + left = this, from = from, operator = "greater_or_equal_than", ) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt index 37e007b..de7b271 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt @@ -8,7 +8,7 @@ import com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder * * @receiver The string in which to check for the presence of the substring. * @param right The substring to search for within the current string. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ infix fun String.expContains(right: String): Expression = ExpressionBuilder.left(this).contains(right) @@ -17,6 +17,6 @@ infix fun String.expContains(right: String): Expression = ExpressionBuilder.left * * @receiver The list in which to check for the presence of the element. * @param right The element to search for within the current list. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ infix fun List.expContains(right: Any): Expression = ExpressionBuilder.left(this).contains(right) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt index 3233b9c..f581026 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt @@ -1,14 +1,13 @@ package com.rapatao.projects.ruleset.engine.types.builder.extensions import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator import com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder /** * Creates an [Expression] object that represents a "starts with" operation. * * @param right The value to compare with the left string to check if it starts with it. - * @return An [Expression] object with the left string, the [Operator.STARTS_WITH] operator, and the right value. + * @return An [Expression] object with the left string, the "starts_with" operator, and the right value. */ infix fun String.startsWith(right: Any): Expression = ExpressionBuilder.left(this).startsWith(right) @@ -16,6 +15,6 @@ infix fun String.startsWith(right: Any): Expression = ExpressionBuilder.left(thi * Creates an [Expression] object that represents a "ends with" operation. * * @param right The value to compare with the left string to check if it ends with it. - * @return An [Expression] object with the left string, the [Operator.ENDS_WITH] operator, and the right value. + * @return An [Expression] object with the left string, the "ends_with" operator, and the right value. */ infix fun String.endsWith(right: Any): Expression = ExpressionBuilder.left(this).endsWith(right) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt new file mode 100644 index 0000000..0c25777 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.errors + +/** + * Throw when an expression is using an unrecognized operator + */ +class UnknownOperator(name: String) : RuntimeException( + "Unknown operator: $name" +) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt new file mode 100644 index 0000000..54a5d21 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt @@ -0,0 +1,13 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +object BuiltInOperators { + const val EQUALS = "equals" + const val NOT_EQUALS = "not_equals" + const val GREATER_THAN = "greater_than" + const val GREATER_OR_EQUAL_THAN = "greater_or_equal_than" + const val LESS_THAN = "less_than" + const val LESS_OR_EQUAL_THAN = "less_or_equal_than" + const val STARTS_WITH = "starts_with" + const val ENDS_WITH = "ends_with" + const val CONTAINS = "contains" +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt new file mode 100644 index 0000000..ffbf04b --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string contains a specified sequence of characters or if an array contains + * a particular element. + */ +abstract class ContainsOperator : Operator { + override fun name(): String = BuiltInOperators.CONTAINS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt new file mode 100644 index 0000000..2fc6867 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string ends with a specified sequence of characters. + */ +abstract class EndsWithOperator : Operator { + override fun name(): String = BuiltInOperators.ENDS_WITH +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt new file mode 100644 index 0000000..5acbf77 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the equality operator (==), used to check if two values are equal. + */ +abstract class EqualsOperator : Operator { + override fun name(): String = BuiltInOperators.EQUALS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt new file mode 100644 index 0000000..c8e0904 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to + * another. + */ +abstract class GreaterOrEqualThanOperator : Operator { + override fun name(): String = BuiltInOperators.GREATER_OR_EQUAL_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt new file mode 100644 index 0000000..912545d --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the greater than operator (>), used to compare if one value is greater than another. + */ +abstract class GreaterThanOperator : Operator { + override fun name(): String = BuiltInOperators.GREATER_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt new file mode 100644 index 0000000..e38cda1 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to + * another. + */ +abstract class LessOrEqualThanOperator : Operator { + override fun name(): String = BuiltInOperators.LESS_OR_EQUAL_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt new file mode 100644 index 0000000..ba686e8 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the less than operator (<), used to compare if one value is less than another. + */ +abstract class LessThanOperator : Operator { + override fun name(): String = BuiltInOperators.LESS_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt new file mode 100644 index 0000000..f793560 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the inequality operator (!=), used to check if two values are not equal. + */ +abstract class NotEqualsOperator : Operator { + override fun name(): String = BuiltInOperators.NOT_EQUALS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt new file mode 100644 index 0000000..5e7062d --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt @@ -0,0 +1,23 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +import com.rapatao.projects.ruleset.engine.context.EvalContext + +/** + * This interface defines how the Engines must validate the expression for a given Operator + */ +interface Operator { + + /** + * Compare the given operands + * + * @return true when equal, otherwise false + */ + fun process(context: EvalContext, left: Any?, right: Any?): Boolean + + /** + * Returns the operator representation name, such as "equals", "not_equals", etc. + * + * @return The operator name + */ + fun name(): String +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt new file mode 100644 index 0000000..a493e56 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string starts with a specified sequence of characters. + */ +abstract class StartsWithOperator : Operator { + override fun name(): String = BuiltInOperators.STARTS_WITH +} diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt index a009659..4e47b6e 100644 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt @@ -1,9 +1,8 @@ package com.rapatao.projects.ruleset.engine.evaluator.graaljs import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.graalvm.polyglot.Context -import org.graalvm.polyglot.Source /** * GraalJSContext is a class that implements the EvalContext interface. @@ -15,15 +14,8 @@ class GraalJSContext( private val context: Context, ) : EvalContext { - /** - * Processes an expression. - * - * @param expression the expression to process - * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW - */ - override fun process(expression: Expression): Boolean { - return context.eval(expression.asScript()).asBoolean() + override fun process(left: Any?, operator: Operator, right: Any?): Boolean { + return operator.process(this, left, right) } /** @@ -32,14 +24,4 @@ class GraalJSContext( * @return the Graal JS context. */ fun context() = context - - private fun Expression.asScript(): Source { - val script = Parser.parse(this) - - return Source.newBuilder( - "js", - "true == ($script)", - script - ).buildLiteral() - } } diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt index f12b44c..ee07362 100644 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt @@ -4,6 +4,7 @@ import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.evaluator.graaljs.parameters.MapInjector import com.rapatao.projects.ruleset.engine.evaluator.graaljs.parameters.TypedInjector +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.graalvm.polyglot.Context import org.graalvm.polyglot.Engine import org.graalvm.polyglot.HostAccess @@ -26,8 +27,21 @@ open class GraalJSEvaluator( .engine(engine) .option("js.ecmascript-version", "2023") .allowHostAccess(HostAccess.ALL).allowHostClassLookup { true } - .option("js.nashorn-compat", "true").allowExperimentalOptions(true) -) : Evaluator() { + .option("js.nashorn-compat", "true").allowExperimentalOptions(true), + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { override fun call(inputData: Any, block: EvalContext.() -> T): T = createContext().let { diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt new file mode 100644 index 0000000..ee7f279 --- /dev/null +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt @@ -0,0 +1,80 @@ +package com.rapatao.projects.ruleset.engine.evaluator.graaljs + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator +import org.graalvm.polyglot.Source + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) == ($right)") +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) != ($right)") +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) > ($right)") +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) >= ($right)") +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) < ($right)") +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) <= ($right)") +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).startsWith(($right))") +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).endsWith(($right))") +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate( + """ + (function() { + if (Array.isArray(${left})) { + return ${left}.includes(${right}) + } else { + return ${left}.indexOf(${right}) !== -1 + } + })() + """.trimIndent() + ) +} + +private fun EvalContext.evaluate(content: String): Boolean { + val graalJSContext = this as GraalJSContext + + return graalJSContext.context().eval( + Source.newBuilder( + "js", + "true == ($content)", + content, + ).buildLiteral() + ).asBoolean() +} diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt deleted file mode 100644 index a3df66a..0000000 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.graaljs - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - - fun parse(expression: Expression): String { - return when (expression.operator) { - Operator.EQUALS -> "==".formatComparison(expression) - Operator.NOT_EQUALS -> "!=".formatComparison(expression) - Operator.GREATER_THAN -> ">".formatComparison(expression) - Operator.GREATER_OR_EQUAL_THAN -> ">=".formatComparison(expression) - Operator.LESS_THAN -> "<".formatComparison(expression) - Operator.LESS_OR_EQUAL_THAN -> "<=".formatComparison(expression) - Operator.STARTS_WITH -> "startsWith".formatWithOperation(expression) - Operator.ENDS_WITH -> "endsWith".formatWithOperation(expression) - Operator.CONTAINS -> formatContainsOperation(expression) - null -> error("when evaluation an expression, the operator cannot be null") - } - } - - private fun String.formatComparison(expression: Expression) = - "(${expression.left}) $this (${expression.right})" - - private fun String.formatWithOperation(expression: Expression) = - "${expression.left}.${this}(${expression.right})" - - private fun formatContainsOperation(expression: Expression) = - """ - (function() { - if (Array.isArray(${expression.left})) { - return ${expression.left}.includes(${expression.right}) - } else { - return ${expression.left}.indexOf(${expression.right}) !== -1 - } - })() - """.trimIndent() -} diff --git a/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt b/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt index 98e3bdb..9532fd2 100644 --- a/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt +++ b/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonInclude import com.rapatao.projects.ruleset.engine.types.OnFailure -import com.rapatao.projects.ruleset.engine.types.Operator /** * This interface represents a mixin for JSON serialization and deserialization of expressions. @@ -23,14 +22,6 @@ interface ExpressionMixin { ) fun onFailure(): OnFailure - @JsonFormat( - with = [ - JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES, - JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES - ], - ) - fun operator(): Operator - @JsonIgnore fun isValid(): Boolean } diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt index ae0c0be..4110289 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt @@ -3,6 +3,7 @@ package com.rapatao.projects.ruleset.jackson import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.rapatao.projects.ruleset.engine.cases.TestData +import com.rapatao.projects.ruleset.engine.evaluator.kotlin.KotlinEvaluator import com.rapatao.projects.ruleset.engine.types.Expression import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.allMatch import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.anyMatch @@ -31,6 +32,8 @@ internal class SerializationExamplesBuilder { .setSerializationInclusion(JsonInclude.Include.NON_NULL) .addMixIn(Expression::class.java, ExpressionMixin::class.java) + private val engine = KotlinEvaluator() + private val cases = listOf( "* equalsTo: ", "field" equalsTo true, @@ -107,7 +110,7 @@ internal class SerializationExamplesBuilder { .filterIsInstance() .filter { // remove from serialization example rules that one of the operands is null - it.isValid() && + it.isValid(engine) && ( (it.left != null && it.right != null) || !it.noneMatch.isNullOrEmpty() || !it.allMatch.isNullOrEmpty() || !it.anyMatch.isNullOrEmpty() diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt index b5da54b..a0ad110 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt @@ -44,7 +44,7 @@ class SerializationTest { val json = """ { "left": "field", - "operator": "EQUALS", + "operator": "equals", "right": 10 } """.trimIndent() diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt index 9a6991b..a72f3c6 100644 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt @@ -1,8 +1,7 @@ package com.rapatao.projects.ruleset.engine.evaluator.kotlin import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.evaluator.kotlin.Parser.parse -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import java.math.BigDecimal /** @@ -15,12 +14,8 @@ class KotlinContext( private val inputData: Map ) : EvalContext { - override fun process(expression: Expression): Boolean { - return parse( - expression, - expression.left.asValue(), - expression.right.asValue() - ) + override fun process(left: Any?, operator: Operator, right: Any?): Boolean { + return operator.process(this, left.asValue(), right.asValue()) } private fun Any?.asValue(): Any? { diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt index 257abd4..80d89eb 100644 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt @@ -2,6 +2,7 @@ package com.rapatao.projects.ruleset.engine.evaluator.kotlin import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.Operator import kotlin.reflect.full.memberProperties /** @@ -9,7 +10,22 @@ import kotlin.reflect.full.memberProperties * * Supported types: Java primitive types, boolean, string, number types, maps, lists and arrays. */ -open class KotlinEvaluator : Evaluator() { +open class KotlinEvaluator( + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { + override fun call(inputData: Any, block: (context: EvalContext) -> T): T { return block(KotlinContext( mutableMapOf().apply { diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt new file mode 100644 index 0000000..1e5f94d --- /dev/null +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt @@ -0,0 +1,69 @@ +package com.rapatao.projects.ruleset.engine.evaluator.kotlin + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left == right +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left != right +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() > right +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() >= right +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() < right +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() <= right +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.toString().startsWith(right.toString()) +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.toString().endsWith(right.toString()) +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.checkContains(right) +} + +@Suppress("UNCHECKED_CAST") +private fun T.comparable() = this as Comparable + +private fun Any?.checkContains(value: Any?): Boolean { + return when { + this is String && value is String -> this.contains(value) + this is Collection<*> -> this.contains(value) + this is Array<*> -> this.contains(value) + else -> throw UnsupportedOperationException("contains doesn't support ${this?.javaClass} type") + } +} diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt deleted file mode 100644 index 7095428..0000000 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.kotlin - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - fun parse(expression: Expression, left: Any?, right: Any?) = - when (expression.operator) { - Operator.EQUALS -> left == right - Operator.NOT_EQUALS -> left != right - Operator.GREATER_THAN -> left.comparable() > right - Operator.GREATER_OR_EQUAL_THAN -> left.comparable() >= right - Operator.LESS_THAN -> left.comparable() < right - Operator.LESS_OR_EQUAL_THAN -> left.comparable() <= right - Operator.STARTS_WITH -> left.toString().startsWith(right.toString()) - Operator.ENDS_WITH -> left.toString().endsWith(right.toString()) - Operator.CONTAINS -> left.checkContains(right) - null -> error("when evaluation an expression, the operator cannot be null") - } - - @Suppress("UNCHECKED_CAST") - private fun T.comparable() = this as Comparable - - private fun Any?.checkContains(value: Any?): Boolean { - return when { - this is String && value is String -> this.contains(value) - this is Collection<*> -> this.contains(value) - this is Array<*> -> this.contains(value) - else -> throw UnsupportedOperationException("contains doesn't support ${this?.javaClass} type") - } - } -} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt new file mode 100644 index 0000000..fe5c90d --- /dev/null +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt @@ -0,0 +1,82 @@ +package com.rapatao.projects.ruleset.engine.evaluator.rhino + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) == ($right)") +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) != ($right)") +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) > ($right)") +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) >= ($right)") +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) < ($right)") +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) <= ($right)") +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).startsWith(($right))") +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).endsWith(($right))") +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate( + """ + (function() { + if (Array.isArray(${left})) { + return ${left}.includes(${right}) + } else { + return ${left}.indexOf(${right}) !== -1 + } + })() + """.trimIndent() + ) +} + +private fun EvalContext.evaluate(content: String): Boolean { + val rhinoContext = this as RhinoContext + + return true == rhinoContext.context() + .compileString( + "true == ($content)", + content, + 0, + null, + ).exec( + rhinoContext.context(), + rhinoContext.scope(), + ) +} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt deleted file mode 100644 index 3fa02ea..0000000 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.rhino - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - fun parse(expression: Expression): String { - return when (expression.operator) { - Operator.EQUALS -> "==".formatComparison(expression) - Operator.NOT_EQUALS -> "!=".formatComparison(expression) - Operator.GREATER_THAN -> ">".formatComparison(expression) - Operator.GREATER_OR_EQUAL_THAN -> ">=".formatComparison(expression) - Operator.LESS_THAN -> "<".formatComparison(expression) - Operator.LESS_OR_EQUAL_THAN -> "<=".formatComparison(expression) - Operator.STARTS_WITH -> "startsWith".formatWithOperation(expression) - Operator.ENDS_WITH -> "endsWith".formatWithOperation(expression) - Operator.CONTAINS -> formatContainsOperation(expression) - null -> error("when evaluation an expression, the operator cannot be null") - } - } - - private fun String.formatComparison(expression: Expression) = - "(${expression.left}) $this (${expression.right})" - - private fun String.formatWithOperation(expression: Expression) = - "${expression.left}.${this}(${expression.right})" - - private fun formatContainsOperation(expression: Expression) = - """ - (function() { - if (Array.isArray(${expression.left})) { - return ${expression.left}.includes(${expression.right}) - } else { - return ${expression.left}.indexOf(${expression.right}) !== -1 - } - })() - """.trimIndent() -} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt index 4c5c953..6a524a9 100644 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt @@ -1,9 +1,8 @@ package com.rapatao.projects.ruleset.engine.evaluator.rhino import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.mozilla.javascript.Context -import org.mozilla.javascript.Script import org.mozilla.javascript.ScriptableObject /** @@ -18,17 +17,8 @@ class RhinoContext( private val scope: ScriptableObject, ) : EvalContext { - /** - * Processes an expression. - * - * @param expression the expression to process - * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW - */ - override fun process(expression: Expression): Boolean { - return true == expression.asScript(context) - .exec(context, scope) - } + override fun process(left: Any?, operator: Operator, right: Any?): Boolean = + operator.process(this, left, right) /** * Returns the Rhino context. @@ -43,15 +33,4 @@ class RhinoContext( * @return The Rhino scope. */ fun scope() = scope - - private fun Expression.asScript(context: Context): Script { - val script = Parser.parse(this) - - return context.compileString( - "true == ($script)", - script, - 0, - null, - ) - } } diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt index 55c8d11..f126257 100644 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt @@ -4,6 +4,7 @@ import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.evaluator.rhino.parameters.MapInjector import com.rapatao.projects.ruleset.engine.evaluator.rhino.parameters.TypedInjector +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.mozilla.javascript.Context import org.mozilla.javascript.ScriptableObject @@ -12,7 +13,20 @@ import org.mozilla.javascript.ScriptableObject */ open class RhinoEvaluator( private val contextFactory: RhinoContextFactory = RhinoContextFactory(), -) : Evaluator() { + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { override fun call( inputData: Any, diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt index 8cef73d..0e8cedf 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt @@ -7,9 +7,11 @@ import com.rapatao.projects.ruleset.engine.types.OnFailure import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.allMatch import com.rapatao.projects.ruleset.engine.types.builder.extensions.equalsTo import com.rapatao.projects.ruleset.engine.types.builder.extensions.ifFail +import com.rapatao.projects.ruleset.engine.types.operators.BuiltInOperators import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.emptyString import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasItem import org.hamcrest.Matchers.not import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -17,6 +19,7 @@ import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import kotlin.reflect.full.memberProperties abstract class BaseEvaluatorTest( private val evaluator: Evaluator @@ -127,4 +130,17 @@ abstract class BaseEvaluatorTest( fun assertEvaluatorMustHaveName() { assertThat(evaluator.name(), not(emptyString())) } + + @Test + @DisplayName("all built-in operators should have at least one test case") + fun assertBuiltInImplementations() { + val operatorsTested: Set = TestData.cases().flatMap { it.get().toList() } + .filterIsInstance() + .mapNotNull { it.operator } + .toSet() + + BuiltInOperators::class.memberProperties.forEach { + assertThat(operatorsTested, hasItem(it.call())) + } + } } diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt index 64575e2..3181ebb 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt @@ -1,5 +1,8 @@ package com.rapatao.projects.ruleset.engine.types +import com.rapatao.projects.ruleset.engine.Evaluator +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.DisplayName @@ -7,63 +10,78 @@ import org.junit.jupiter.api.Test class ExpressionTest { + private val dummyEval: Evaluator = object : Evaluator( + listOf( + object : Operator { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = true + override fun name(): String = "equals" + } + ) + ) { + override fun call(inputData: Any, block: (context: EvalContext) -> T): T { + TODO("Not yet implemented") + } + + override fun name(): String = "test" + } + @Test @DisplayName("empty expression must be invalid") fun assertEmptyExpression() { val exp = Expression() - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("expression with operator is valid") fun assertExpressionValid() { val exp = Expression( - operator = Operator.EQUALS, + operator = "equals", ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with noneMatch is valid") fun assertNoOperatorByNoneMatch() { val exp = Expression( - noneMatch = listOf(Expression(operator = Operator.EQUALS)), + noneMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with anyMatch is valid") fun assertNoOperatorButWithAnyMatch() { val exp = Expression( - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with allMatch is valid") fun assertNoOperatorButWithAllMatch() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("is valid if group and operator is set and valid") fun assertIsValidIfGroupAndOperatorIsSetAndValid() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), + noneMatch = listOf(Expression(operator = "equals")), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @@ -71,34 +89,34 @@ class ExpressionTest { fun assertIsInvalidIfAllMatchContainsAnInvalidExpression() { val exp = Expression( allMatch = listOf(Expression()), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + noneMatch = listOf(Expression(operator = "equals")), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("is invalid if noneMatch contains an invalid expression") fun assertIsInvalidIfNoneMatchContainsInvalidExpression() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), noneMatch = listOf(Expression()), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("is invalid if anyMatch contains an invalid expression") fun assertIsInvalidIfAnyMatchContainsInvalidExpression() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), + noneMatch = listOf(Expression(operator = "equals")), anyMatch = listOf(Expression()), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } }