Skip to content

Commit

Permalink
Documentation for new JSON as Fact feature (#151)
Browse files Browse the repository at this point in the history
* Added documentation on JSON as Fact feature
  • Loading branch information
newm4n authored Dec 19, 2020
1 parent 7497e4b commit fab4b2b
Show file tree
Hide file tree
Showing 3 changed files with 318 additions and 1 deletion.
44 changes: 43 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- 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`
270 changes: 270 additions & 0 deletions docs/JSON_Fact_en.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions docs/Tutorial_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit fab4b2b

Please sign in to comment.