diff --git a/CHANGELOG.md b/CHANGELOG.md index 25fe25bb..9ee3ccdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,4 +103,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Removed -- Grule Event Bus is removed from Grule as it seems too complicated and no one use them. They just expect grule to just works. \ No newline at end of file +- Grule Event Bus is removed from Grule as it seems too complicated and no one use them. They just expect grule to just works. + +### [1.7.0] - 2020-11-06 + +#### Changes + +- Change the Grule ANTLR4 grammar for better structure, tested with ANTLR4 hierarchy and AST Tree. +- FunctionCall AST graph is now under ExpressionAtom instead of Variable + +#### Fix + +- Proper Integer and Float literals both support exponent format + +#### Added + +- Integer literal support Octal and Hexadecimal, Float literal support Hexadecimal. +- Added more documentation about the new numbering literals and also re-arrange the menu in the documentation. +- Support negation. + +### [1.7.1] - 2020-12-02 + +##### Fix + +- Fixed ANTLR4 grammar to enable function chaining in the THEN scope +- Fixed ANTLR4 grammar error that makes array/slice/map cannot be chained with function + +#### Change + +- Built-in function `Changed` is renamed to `Forget` to clearly tell the engine to forget about variable values or function invocation to make sure the engine look again into the underlying data context on the next cycle. + +### [1.7.2] - 2020-12-09 + +##### Fix + +- Fixes the cloning problem where Expression do not clone the negation attribute +- Added mutex for unique.NewID() to make sure that this function is thread/concurrent safe. + +### [1.8.0] - 2020-12-19 + +#### Added + +- Support for JSON as Fact +- Support native type variable to be added straight into `DataContext` not only `struct` \ No newline at end of file diff --git a/docs/JSON_Fact_en.md b/docs/JSON_Fact_en.md new file mode 100755 index 00000000..1de33de4 --- /dev/null +++ b/docs/JSON_Fact_en.md @@ -0,0 +1,270 @@ +# JSON Fact + +[Tutorial](Tutorial_en.md) | [Rule Engine](RuleEngine_en.md) | [GRL](GRL_en.md) | [GRL JSON](GRL_JSON_en.md) | [RETE Algorithm](RETE_en.md) | [Functions](Function_en.md) | [FAQ](FAQ_en.md) | [Benchmark](Benchmarking_en.md) + +--- + +Using JSON straight away as fact in Grule is available starting on version 1.8.0. It enable user to treat JSON string as fact +and add it into `DataContext` just like how you previously add fact data into it. The loaded JSON fact are now "visible" the +the Grule scripts (the GRLs). + +## Adding JSON as fact + +Assuming you have a JSON as follow: + +```json +{ + "name" : "John Doe", + "age" : 24, + "gender" : "M", + "height" : 74.8, + "married" : false, + "address" : { + "street" : "9886 2nd St.", + "city" : "Carpentersville", + "state" : "Illinois", + "postal" : 60110 + }, + "friends" : [ "Roth", "Jane", "Jake" ] +} +``` + +You put your JSON into a byte array. + +```go +myJSON := []byte (...your JSON here...) +``` + +You simply add you JSON variable into `DataContext` + +```go +// create new instance of DataContext +dataContext := ast.NewDataContext() + +// add your JSON Fact into data cotnxt using AddJSON() function. +err := dataContext.AddJSON("MyJSON", myJSON) +``` + +Yes, you can add as many _facts_ as you wish into the context and you can mix between JSON facts +(using `AddJSON`) and normal Go fact (using `Add`) + + ## Evaluating (Reading) JSON Fact Values in GRL + + Inside GRL script, the fact is always visible through their label as you provide them + when adding to the `DataContext`. For example, the code bellow add your JSON and it will be + using label `MyJSON`. + + ```go +err := dataContext.AddJSON("MyJSON", myJSON) +``` + + Yes, you can use any label as long as its a single word. + + ### Traversing member variable like a normal object + + Now. Using the JSON shown at the beginning, your GRL `when` scope can evaluate your json + like the following. + + ```text +when + MyJSON.name == "John Doe" +``` + +or + +```text +when + MyJSON.address.city.StrContains("ville") +``` + +or + +```text +when + MyJSON.age > 30 && MyJSON.height < 60 +``` + +### Traversing member variable like a map + +You can access JSON object's fields using `Map` like selector or like normal object. + + ```text +when + MyJSON["name"] == "John Doe" +``` + +or + +```text +when + MyJSON["address"].city.StrContains("ville") +``` + +or + +```text +when + MyJSON.age > 30 && MyJSON["HEIGHT".ToLower()] < 60 +``` + +### Traversing array member variable + +You can inspect JSON Array element just like a normal array + + ```text +when + MyJSON.friends[3] == "Jake" +``` + +## Writing values into JSON Facts in GRL + +Yes, you can write new values into you JSON facts in the `then` scope of your rules. Changing those values will +certainly evaluated on the following rule evaluation cycles. BUT, there are some caveat (read "Things you should know" bellow.) + +### Writing member variable like a normal object + +Now. Using the JSON shown at the beginning, your GRL `then` scope can modify your json +**fact** like the following. + + ```text +then + MyJSON.name = "Robert Woo"; +``` + +or + +```text +then + MyJSON.address.city = "Corruscant"; +``` + +or + +```text +then + MyJSON.age = 30; +``` + +That's pretty straight forward. But there are some twist to this. + +1. You can modify not only the value of member variable of your JSON object, you can also change the `type`. + Assuming your rule can handle the next evaluation chain for the new type you can do this, otherwise we very strongly not recommended this. + + Example: + + You modify the `MyJSON.age` into string. + + ```text + then + MyJSON.age = "Thirty"; + ``` + + This make the engine to panic when evaluating rule like. + + ```text + when + myJSON.age > 25 + ``` + +2. You can assign a value to non-existent member variable + + Example: + + ```text + then + MyJSON.category = "FAT"; + ``` + + Where the `category` member is not existed in the original JSON. + +### Writing member variable like a normal map + +Now. Using the JSON shown at the beginning, your GRL `then` scope can modify your json +**fact** like the following. + + ```text +then + MyJSON["name"] = "Robert Woo"; +``` + +or + +```text +then + MyJSON["address"]["city"] = "Corruscant"; +``` + +or + +```text +then + MyJSON["age"] = 30; +``` + +Like the object style, there are same exact twist to this. + +1. You can modify not only the value of member variable of your JSON map, you can also change the `type`. + Assuming your rule can handle the next evaluation chain for the new type you can do this, otherwise we very strongly not recommended this. + + Example: + + You modify the `MyJSON.age` into string. + + ```text + then + MyJSON["age"] = "Thirty"; + ``` + + This make the engine to panic when evaluating rule like. + + ```text + when + myJSON.age > 25 + ``` + +2. You can assign a value to non-existent member variable + + Example: + + ```text + then + MyJSON["category"] = "FAT"; + ``` + + Where the `category` member is not existed in the original JSON. + +### Writing member array + +In array you can simple replace array element by it's indices. + +```text +then + MyJSON.friends[3] == "Jake"; +``` + +As long as that indice is a valid one. Grule will panic if the indices is out of bound. +Just like normal JSON, you can replace the value of any element with different type. +You can always inspect the array length. Like ... + +```text +when + MyJSON.friends.Length() > 4; +``` + +Yes, you can always append into Array using `Append` function. Append list of argument value of different types. + +```text +then + MyJSON.friends.Append("Rubby", "Anderson", "Smith", 12.3); +``` + +**Known Issue** + +As of now, there are no built-in function to help user to inspect array element easily, such as `Contains(value) bool` + +## Things you should know + +1. After you add JSON fact into `DataContext`, the change to this string variable will not reflect the facts already in the `DataContext`. This is also + applied in vice-versa, where changes in the fact within `DataContext` will not change the JSON string. +2. You can modify your JSON fact in the `then` scope, but unlike normal `Go` facts, these changes will not reflect to your original JSON string. If you want this to happen, + you should parse your JSON into a `struct` before hand, and add your `struct` into `DataContext` normally. \ No newline at end of file diff --git a/docs/Tutorial_en.md b/docs/Tutorial_en.md index 5c2b1d8a..7185ba1c 100644 --- a/docs/Tutorial_en.md +++ b/docs/Tutorial_en.md @@ -88,6 +88,11 @@ if err != nil { } ``` +### Add JSON as Fact + +JSON string as a fact in Grule is a feature introduced at version 1.8.0. +You can read [JSON as a Fact](JSON_Fact_en.md) to know how to add JSON into `DataContext`. + ## Creating KnowledgeLibrary and Add Rules Into It A `KnowledgeLibrary` is basically collection of `KnowledgeBase` blue prints.