hyperjumptech / grule-rule-engine Goto Github PK
View Code? Open in Web Editor NEWRule engine implementation in Golang
License: Other
Rule engine implementation in Golang
License: Other
Could you up version to v1.0.1 please
Hello,
This is just a question which might be obvious to more experienced rules developers so apologies if this is basic.
I have a few (contrived) rules:
And I am trying to answer the question "What is my risk rating" ?
Assuming I don't currently have any facts available, in order to answer my question, I need to ask the following questions (in order to populate the facts)
If my goal is to answer the question "what is my risk rating" is there any way I can ask grule which facts are missing answers in order of which way i need to ask them ?
Obviously happy to contribute if there isn't anything existing but I would like to get an understanding as to how I would go about doing this ?
hi, thanks.
How do I know which specific rule was successfully or failed executed?
Can it be returned by the execute function?
I run the project (by following the tutorial at https://github.com/hyperjumptech/grule-rule-engine/blob/master/docs/Tutorial_en.md ) on a Windows 10 desktop, all rule files are put in a folder, and I want to load those rules using pkg.NewFileResourceBundle(basePath string, pathPattern ...string)
. However, the returned resource from bundle.MustLoad()
is always empty.
My environment
OS: Windows 10 x86_64
Go version: go version go1.14.6 windows/amd64
To Reproduce
Steps to reproduce the behavior:
rule CheckValues "Check the default values" salience 10 {
when
MF.IntAttribute == 123 && MF.StringAttribute == "Some string value"
then
MF.WhatToSay = MF.GetWhatToSay("Hello Grule");
Retract("CheckValues");
}
package main
import (
"fmt"
"github.com/hyperjumptech/grule-rule-engine/ast"
"github.com/hyperjumptech/grule-rule-engine/builder"
"github.com/hyperjumptech/grule-rule-engine/pkg"
)
func main() {
knowledgeLibrary := ast.NewKnowledgeLibrary()
ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary)
bundle := pkg.NewFileResourceBundle("C:\\rules", "C:\\rules\\*.grl")
resources := bundle.MustLoad()
fmt.Printf("resources = %v", resources)
for _, res := range resources {
err := ruleBuilder.BuildRuleFromResource("TutorialRules", "0.0.1", res)
if err != nil {
panic(err)
}
}
}
resources = []
Expected behavior
I would like to see the result should be:
resources = [File resource at C:\\rules\\1.grl]
This is a great tool, and will be better with cep support.
in my case got an error
2020/06/04 21:33:15 Got error GruleEngine successfully selected rule candidate for execution after 1 cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries "When" and "Then" scope. You can adjust the maximum cycle using GruleEngine.MaxCycle variable.
this is my code, Can someone tell me why? thx~
package main
import (
"log"
"time"
"github.com/hyperjumptech/grule-rule-engine/ast"
"github.com/hyperjumptech/grule-rule-engine/builder"
"github.com/hyperjumptech/grule-rule-engine/engine"
"github.com/hyperjumptech/grule-rule-engine/pkg"
)
// AbstractModel 对账抽象模型
type AbstractModel struct {
Amount int64
OrderID string
IsCloseRecord bool
FinishTime time.Time
}
func (ab *AbstractModel) Sum(in1, in2, in3 int64) int64 {
sum := in1 + in2 + in3
log.Println("this is Handler Sum, sum=", sum)
return sum
}
func main() {
ab1 := &AbstractModel{
Amount: 100,
OrderID: "testorder",
IsCloseRecord: false,
FinishTime: time.Now(),
}
ab2 := &AbstractModel{
Amount: 200,
OrderID: "testorder",
IsCloseRecord: false,
FinishTime: time.Now(),
}
dataContext := ast.NewDataContext()
err := dataContext.Add("Ab1", ab1)
if err != nil {
log.Fatal(err)
}
err = dataContext.Add("Ab2", ab2)
if err != nil {
log.Fatal(err)
}
memory := ast.NewWorkingMemory()
knowledgeBase := ast.NewKnowledgeBase("Test", "0.1")
ruleBuilder := builder.NewRuleBuilder(knowledgeBase, memory)
err = ruleBuilder.BuildRuleFromResource(pkg.NewBytesResource([]byte(`
rule OrderAmountCheck2 "testrule2" salience 1 {
when
Ab1.Amount == 100
then
Ab2.Amount = Ab1.Sum(Ab1.Amount, Ab2.Amount, Ab2.Amount);
}
`)))
if err != nil {
log.Fatal(err)
} else {
eng := &engine.GruleEngine{MaxCycle: 1}
err := eng.Execute(dataContext, knowledgeBase, memory)
if err != nil {
log.Fatalf("Got error %v", err)
} else {
log.Println("exec success")
}
}
return
}
Describe the bug
If we're using a big constant number in Grule JSON Format, there will be a conversion issue. for example, if a constant value in the JSON is 10000000, it will be converted into 1e+07 instead of 10000000
To Reproduce
Steps to reproduce the behavior:
[{
"name": "SpeedUp",
"desc": "When testcar is speeding up we keep increase the \"speed\".",
"salience": 10,
"when": {
"and": [
{"eq": [{"obj": "TestCar.SpeedUp"}, {"const": true}]},
{"lt": [{"obj": "TestCar.Speed"}, {"const": 10000000}]}
]
},
"then": [
{"set": [{"obj": "TestCar.Speed"}, {"plus": [{"obj": "TestCar.Speed"}, {"obj": "TestCar.SpeedIncrement"}]}]},
{"set": [{"obj": "DistanceRecord.TotalDistance"}, {"plus": [{"obj": "DistanceRecord.TotalDistance"}, {"obj": "TestCar.Speed"}]}]},
{"call": ["Log", {"const": "\"Speed\" increased\n"}]}
]
}]
the JSON will be converted into
rule SpeedUp "When testcar is speeding up we keep increase the \"speed\"." salience 10 {
when
TestCar.SpeedUp == true && TestCar.Speed < 1e+07
then
TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
Log("\"Speed\" increased\n");
}
instead of (notice the difference between 1e+07 and 10000000)
rule SpeedUp "When testcar is speeding up we keep increase the \"speed\"." salience 10 {
when
TestCar.SpeedUp == true && TestCar.Speed < 10000000
then
TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
Log("\"Speed\" increased\n");
}
this will cause an error during rule engine execution
line 11:52 missing THEN at 'e'
time="2020-09-21T12:11:35+07:00" level=error msg="GRL error, after '[true && TestCar.Speed < 1]' and then unexpected '<missing >'" lib=grule struct=GruleParserV2Listener
time="2020-09-21T12:11:35+07:00" level=error msg="GRL error, after '[true && TestCar.Speed < 1]' and then unexpected 'e'" lib=grule struct=GruleParserV2Listener
time="2020-09-21T12:11:35+07:00" level=error msg="GRL error, after '[true && TestCar.Speed < 1]' and then unexpected '+'" lib=grule struct=GruleParserV2Listener
time="2020-09-21T12:11:35+07:00" level=error msg="GRL error, after '[true && TestCar.Speed < 1]' and then unexpected '07'" lib=grule struct=GruleParserV2Listener
time="2020-09-21T12:11:35+07:00" level=error msg="GRL error, after '[true && TestCar.Speed < 1]' and then unexpected 'then'" lib=grule struct=GruleParserV2Listener
time="2020-09-21T12:11:35+07:00" level=error msg="Loading rule resource : JSON Resource, underlying resource: Reader resource. Source unknown. failed. Got GRL error, after '[true && TestCar.Speed < 1]' and then unexpected 'then'. Time take 3 ms"
Expected behavior
We need to format floating-point numbers to avoid exponent form for large exponents.
Additional context
Add any other context about the problem here.
hi ,I have a questions with MaxCycle
// MaxCycle is set to 50
// eng := &engine.GruleEngine{MaxCycle: 50}
rule OrderAmountCheck1 "testrule1" salience 100 {
when
Ab1.Amount == 100
then
Ab2.Amount = Ab1.Sum(Ab1.Amount, Ab2.Amount, Ab2.Amount);
Retract("OrderAmountCheck1");
}
rule OrderAmountCheck2 "testrule2" salience 50 {
when
Ab2.Amount == 500
then
Ab2.Amount = Ab1.Sum(Ab1.Amount, Ab2.Amount, 0);
Retract("OrderAmountCheck2");
}
// why OrderAmountCheck3 and OrderAmountCheck4 not run MaxCycle?
// I did not implement Retract
// OrderAmountCheck3 and OrderAmountCheck4 functions are only called once
rule OrderAmountCheck3 "testrule3" salience 0 {
when
Ab2.Amount > 0
then
Ab2.Sprintf(Ab2.Amount);
}
rule OrderAmountCheck4 "testrule3" salience 0 {
when
Ab2.Amount > 0
then
Log(Ab2.Sprintf(Ab2.Amount) + "---");
}
type MyFact struct {
Int64Attribute int64
IntAttribute int
Abc int
StringAttribute string
BooleanAttribute bool
FloatAttribute float64
TimeAttribute time.Time
WhatToSay string
}
func (mf *MyFact) GetWhatToSay(sentence string) string {
abc := fmt.Sprintf("Let say "%s" %d", sentence,mf.IntAttribute)
fmt.Println("--------------",abc)
return abc
}
func (mf *MyFact) CalcAbc( num int) int {
mf.Abc = int(num)
return mf.Abc
}
func (mf *MyFact) CalcInt64( num int64) int64 {
mf.Int64Attribute = num
return mf.Int64Attribute
}
func main() {
myFact := &MyFact{
Int64Attribute: int64(123454545452342423),
IntAttribute: 123,
StringAttribute: "Some string value",
BooleanAttribute: true,
FloatAttribute: 1.234,
TimeAttribute: time.Now(),
}
dataCtx := ast.NewDataContext()
err := dataCtx.Add("MF", myFact)
if err != nil {
panic(err)
}
workingMemory := ast.NewWorkingMemory()
knowledgeBase := ast.NewKnowledgeBase("Tutorial", "0.0.1")
ruleBuilder := builder.NewRuleBuilder(knowledgeBase, workingMemory)
drls := `
rule CheckValues "Check the default values" salience 10 {
when
MF.IntAttribute == 123 && MF.StringAttribute == "Some string value"
then
MF.WhatToSay = MF.GetWhatToSay("Hello Grule");
MF.CalcAbc(100);
Retract("CheckValues");
}
`
byteArr := pkg.NewBytesResource([]byte(drls))
err = ruleBuilder.BuildRuleFromResource(byteArr)
if err != nil {
panic(err)
}
engine := engine.NewGruleEngine()
for i := 0; i < 100000000; i++ {
err = engine.Execute(dataCtx, knowledgeBase, workingMemory)
if err != nil {
panic(err)
}
if i%100000 == 0 {
fmt.Println(myFact.WhatToSay)
fmt.Println(i,myFact.WhatToSay,myFact.Int64Attribute,myFact.Abc)
}
}
fmt.Println(myFact.WhatToSay)
}
Im currently working on major changes in the Grule Rule Engine.
It involves changes in
This updates will fix lots of issues, features and stability related issue.
Im aiming,
rule X "X" {
when
fact.someArray[23].stringVal == "abc" &&
fact.someMap["56"].obj.IntValue > 100
then
fact.otherArray[12] = 23;
fact.otherArray.append(45);
fact.otherMap["abc"] = "def";
}
when
person.GetFriendByName("lucas").IsFemale() &&
person.GetAddress().Contains("Hill Rd") &&
person.GetCoupon().StartWith("hill".toUpper())
then
...
Fact := `{
"s": "abc",
"i": 123,
"o": {
"c", "xyz"
} ,
"a" [
"abc", "def"
]
}`
Access the json dom from the rule.
when
Fact.s == "abc" &&
Fact.o.c == "XYZ" &&
Fact.a.len() > 0 && Fact.a[0] == Fact.s
then
Fact.a.append("ghi");
All these changes will have 100% compatibility with previous version, rule grammar and engine usage. You dont have to change your side of code. I try my best and I hope ;p
So, if you guys found some bug or nice feature to add in the current version, please don't hesitate to post them here. I will incorporate them into this new builds. I am doing this in my spare time out side my regular working hour and try my best to have it done ASAP. So, while Im doing this, please be patient, your issue are looked after, so hold your horses.
@newm4n
Just want to highlight after short-circuit changes, benchmarks are looking even better, please find the detailed report below
Before ShortCircuit Changes
:goos: darwin
goarch: amd64
pkg: github.com/hyperjumptech/grule-rule-engine/examples/benchmark
Benchmark_Grule_Execution_Engine/100_rules-12 35340 33921 ns/op 4391 B/op 78 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#01-12 29650 34346 ns/op 4446 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#02-12 29587 34380 ns/op 4429 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#03-12 31029 34342 ns/op 4423 B/op 78 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#04-12 29646 35943 ns/op 4451 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#05-12 35835 33039 ns/op 4402 B/op 78 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#06-12 29305 34495 ns/op 4390 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#07-12 28704 34857 ns/op 4397 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#08-12 34936 34349 ns/op 4448 B/op 78 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#09-12 36352 33935 ns/op 4332 B/op 77 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#10-12 30698 39917 ns/op 4377 B/op 79 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules-12 3478 317176 ns/op 43912 B/op 690 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#01-12 3434 319312 ns/op 44239 B/op 698 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#02-12 3565 321366 ns/op 43288 B/op 675 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#03-12 3385 330214 ns/op 44611 B/op 707 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#04-12 2544 454601 ns/op 53267 B/op 916 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#05-12 744 1420603 ns/op 137563 B/op 2953 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#06-12 1472 824068 ns/op 78644 B/op 1529 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#07-12 2671 408376 ns/op 51611 B/op 876 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#08-12 3524 359907 ns/op 43579 B/op 682 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#09-12 2970 357360 ns/op 48268 B/op 795 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#10-12 3511 367940 ns/op 43670 B/op 684 allocs/op
Benchmark_Grule_Load_Rules/100_rules-12 44 23445211 ns/op 8871465 B/op 216553 allocs/op
Benchmark_Grule_Load_Rules/100_rules#01-12 51 24634072 ns/op 8871551 B/op 216554 allocs/op
Benchmark_Grule_Load_Rules/100_rules#02-12 46 25222948 ns/op 8871361 B/op 216553 allocs/op
Benchmark_Grule_Load_Rules/100_rules#03-12 44 28146387 ns/op 8871486 B/op 216553 allocs/op
Benchmark_Grule_Load_Rules/100_rules#04-12 45 30634115 ns/op 8871631 B/op 216554 allocs/op
Benchmark_Grule_Load_Rules/100_rules#05-12 49 22554323 ns/op 8871228 B/op 216552 allocs/op
Benchmark_Grule_Load_Rules/100_rules#06-12 46 22489783 ns/op 8871493 B/op 216554 allocs/op
Benchmark_Grule_Load_Rules/100_rules#07-12 54 25210478 ns/op 8871489 B/op 216554 allocs/op
Benchmark_Grule_Load_Rules/100_rules#08-12 54 23163462 ns/op 8871450 B/op 216553 allocs/op
Benchmark_Grule_Load_Rules/100_rules#09-12 51 37621286 ns/op 8871515 B/op 216554 allocs/op
Benchmark_Grule_Load_Rules/100_rules#10-12 33 37549279 ns/op 8871417 B/op 216553 allocs/op
Benchmark_Grule_Load_Rules/1000_rules-12 4 345123608 ns/op 88642916 B/op 2141300 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#01-12 3 358250329 ns/op 88638602 B/op 2141279 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#02-12 3 347296778 ns/op 88646288 B/op 2141318 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#03-12 5 283385379 ns/op 88643715 B/op 2141304 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#04-12 3 372053420 ns/op 88643888 B/op 2141303 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#05-12 5 213746759 ns/op 88643011 B/op 2141299 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#06-12 6 230968014 ns/op 88641652 B/op 2141293 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#07-12 5 216604105 ns/op 88645020 B/op 2141310 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#08-12 5 213267279 ns/op 88640585 B/op 2141289 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#09-12 5 214347871 ns/op 88641289 B/op 2141292 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#10-12 5 211954473 ns/op 88642294 B/op 2141297 allocs/op
After ShortCircuit Changes
:XXXXX@Vasu-Macbook ~/g/s/g/e/benchmark> go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hyperjumptech/grule-rule-engine/examples/benchmark
Benchmark_Grule_Execution_Engine/100_rules-12 2055945 574 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#01-12 2048078 570 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#02-12 2086953 572 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#03-12 2094231 571 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#04-12 2078065 576 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#05-12 2028356 642 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#06-12 2002248 628 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#07-12 1850121 703 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#08-12 1761343 585 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#09-12 2080953 594 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/100_rules#10-12 2082880 573 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules-12 2082183 575 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#01-12 2098585 568 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#02-12 2090640 570 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#03-12 2109938 587 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#04-12 2045216 576 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#05-12 2092534 575 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#06-12 1994415 579 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#07-12 2098788 599 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#08-12 2092808 573 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#09-12 2085716 609 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Execution_Engine/1000_rules#10-12 1864302 576 ns/op 512 B/op 9 allocs/op
Benchmark_Grule_Load_Rules/100_rules-12 67137 17387 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#01-12 67485 17447 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#02-12 67332 17408 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#03-12 67992 17436 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#04-12 68170 17420 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#05-12 67777 17645 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#06-12 65100 17431 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#07-12 67396 17396 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#08-12 68132 17458 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#09-12 67881 17399 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/100_rules#10-12 67216 17523 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules-12 66828 17823 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#01-12 69122 17581 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#02-12 67815 17425 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#03-12 67405 19681 ns/op 5070 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#04-12 48511 21222 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#05-12 67779 18999 ns/op 5070 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#06-12 56694 17691 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#07-12 69086 17641 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#08-12 51638 19401 ns/op 5070 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#09-12 58940 20498 ns/op 5071 B/op 118 allocs/op
Benchmark_Grule_Load_Rules/1000_rules#10-12 67411 19487 ns/op 5071 B/op 118 allocs/op
Describe the bug
when i use the operate &&
and ||
, just like golang or java, i excepted short-circuit evaluation, but it doesn't.
To Reproduce
e.g.:
type TestCar struct {
SpeedUp bool
Speed int
MaxSpeed int
SpeedIncrement int
}
func (t *TestCar) IsTrue() bool {
log.Println("isTrue expression")
return true
}
func (t *TestCar) IsFalse() bool {
log.Println("isFalse expression")
return false
}
const (
rules = `
rule SetTime "When Distance Recorder time not set, set it." {
when
TestCar.IsFalse() && TestCar.IsTrue()
then
Log("Set the test time");
}
`
)
func TestGrule_Execute(t *testing.T) {
tc := &TestCar{
SpeedUp: true,
Speed: 0,
MaxSpeed: 100,
SpeedIncrement: 2,
}
dctx := ast.NewDataContext()
err := dctx.Add("TestCar", tc)
assert.NoError(t, err)
lib := ast.NewKnowledgeLibrary()
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(rules)))
assert.NoError(t, err)
engine := NewGruleEngine()
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
start := time.Now()
err = engine.Execute(dctx, kb)
assert.NoError(t, err)
}
i got the result like that:
time="2020-10-28T13:30:12+08:00" level=info msg="Working memory indexing takes 0 ms" lib=grule package=AST
time="2020-10-28T13:30:12+08:00" level=info msg="isFalse expression" lib=grule struct=GruleEngineV2
time="2020-10-28T13:30:12+08:00" level=info msg="isTrue expression" lib=grule struct=GruleEngineV2
GruleEngine_test.go:99: 0
GruleEngine_test.go:100: Duration 0 ms
--- PASS: TestGrule_Execute (0.00s)
PASS
so, grule just run all function, TestCar.IsFalse()
and TestCar.IsTrue()
Expected behavior
please add the feature short-circuit evaluation
Additional context
Short-circuit_evaluation
I try to create two simple rules similar with the tutorial stated in the docs. These are my rules:
// Initial fact
// MF.IntAttr = 1200000
rule Rule1 "Desc of Rule1" salience 0 {
when
MF.IntAttr >= 100000
then
Log("Rule1 called");
Retract("Rule1");
}
rule Rule2 "Desc of Rule2" salience -1 {
when
MF.IntAttr / 500000 > 0.0
then
Log("Rule2 called");
MF.IntAttr = MF.IntAttr - 500000;
}
I expect the engine to re-evaluate the condition in the Rule2
whenever a new cycle begins. And at some point, the condition will be false and the cycle done. But instead, I got an error like this:
panic: GruleEngine successfully selected rule candidate for execution after 5000 cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries "When" and "Then" scope. You can adjust the maximum cycle using GruleEngine.MaxCycle variable.
It seems the engine is not re-evaluating the condition in Rule2
and remember it as true causing the cycle to keep repeating again and again. Please advice if I am doing it wrong. Thanks.
Is your feature request related to a problem? Please describe.
It's not very convenient to handle some sort of decision problem, like:
Condition A --y--> Rule A
\--n--> Rule B --y--> Rule C
\--n--> Rule D
Describe the solution you'd like
Add a built-in function like Jump(ruleName). It is used in Then clause to jump to other rule.
Hello, I follow the steps in tutorial to try this package. It can work but error always return
GruleEngine successfully selected rule candidate for execution after 1 cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries "When" and "Then" scope. You can adjust the maximum cycle using GruleEngine.MaxCycle variable.
I've tried to modify MaxCycle but the error still return. Maybe someone can tell me how to fix this. Thx.
package main
import (
"fmt"
ast "github.com/hyperjumptech/grule-rule-engine/ast"
builder "github.com/hyperjumptech/grule-rule-engine/builder"
engine "github.com/hyperjumptech/grule-rule-engine/engine"
pkg "github.com/hyperjumptech/grule-rule-engine/pkg"
"time"
)
type MyFact struct {
IntAttribute int64
StringAttribute string
BooleanAttribute bool
FloatAttribute float64
TimeAttribute time.Time
WhatToSay string
}
func (mf *MyFact) GetWhatToSay(sentence string) string {
return fmt.Sprintf("Let say \"%s\"", sentence)
}
func main() {
myFact := &MyFact{
IntAttribute: 123,
StringAttribute: "Some string value",
BooleanAttribute: true,
FloatAttribute: 1.234,
TimeAttribute: time.Now(),
}
dataCtx := ast.NewDataContext()
err := dataCtx.Add("MF", myFact)
if err != nil {
panic(err)
}
workingMemory := ast.NewWorkingMemory()
knowledgeBase := ast.NewKnowledgeBase("test", "1")
ruleBuilder := builder.NewRuleBuilder(knowledgeBase, workingMemory)
drls := pkg.NewFileResource("./test.grl")
err = ruleBuilder.BuildRuleFromResource(drls)
if err != nil {
panic(err)
}
engine := engine.NewGruleEngine()
err = engine.Execute(dataCtx, knowledgeBase, workingMemory)
fmt.Println(err)
fmt.Println(myFact.WhatToSay)
}
Is your feature request related to a problem? Please describe.
This is more like an enhancement
Describe the solution you'd like
I wanted to fetch all the rules matching a particular condition, this helps me to write logic to delete duplicate rules if there is a match in existing rules.
Eg:
rule 1 when (A > 10 || B < 4) then .....
rule2 when (B < 5) then ....
rule 3 when (C > 4) then ....
Fact (input) is B < 6 then it should give me rule1 and rule2
Hi,
Is it possible to save a function result in the condition part and pass it to 'then' statement)?
for example, the condition is if the file is found, then I could have its path in then statement rather than calling the same function one more time in 'then' statement.
Thanks in advance
hi, @newm4n , current Grule takes listener-pattern, I'd like to know is there any consideration for that?
e.g. https://stackoverflow.com/questions/20714492/antlr4-listeners-and-visitors-which-to-implement mentioned:
There's another important difference between these two patterns: a visitor uses the call stack to manage tree traversals, whereas the listener uses an explicit stack allocated on the heap, managed by a walker. This means that large inputs to a visitor could blow out the stack, while a listener would have no trouble.
hi:
Is this library run with multiple instances?
I ran it with the latest version of the attempt, but it does not seem to support multiple instances
Is your feature request related to a problem? Please describe.
When using grules in a service, being able to serve concurrent requests with the same KnowledgeBase
-- without having to parse source rules in each request -- would increase performance.
Describe the solution you'd like
Just having a method to clone a KnowledgeBase
object would be enough. When having to serve a request, just clone the KnowledgeBase
singleton instance and execute against a new WorkingMemory
and DataCotext
. GruleEngine
can be shared among threads with no issue right now.
Describe alternatives you've considered
KnowledgeBase
into KnowledgeBase
and WorkingKnowledgeBase
is another solution, where rules are only retracted on the later. WorkingKnowledgeBase
is a clone of KnowledgeBase
used only internally in GruleEngine.Execute(...)
.Additional context
Nothing else comes to my mind...
Hi there!
Is your feature request related to a problem? Please describe.
With the release of 1.6.0, we saw the ability to use the same KnowledgeBase for multiple concurrent threads. This was great, since it allowed us to only parse the rule library once, which was a CPU-intensive operation that was bogging down our system previously. After upgrading grule we saw CPU usage reduce significantly. I think it can go further, though.
In my opinion, there are a couple of issues with the current implementation - namely KnowledgeBase cloning:
As far as I understand grule, I don't think cloning KnowledgeBases is necessary at all. A KnowledgeBase can be created and parsed once, say at the time of bootstrapping an application, and then traversed and evaluated as read only. This would require the evaluations - the results of the traversal - to be stored separately from the rule definitions in the KnowledgeBase.
Describe the solution you'd like
Looking at the current code, I see the value of a rule being stored within that rule's type. This ties the rule instance to the evaluation/result instance, and since these are tied, the rule instance must be instantiated (cloned) per evaluation (hence KnowledgeBase.Clone()
).
For example, here's the ExpressionAtom
type, which contains the definition of a rule (or part of a rule), as well as the evaluation result:
// ExpressionAtom AST node graph
type ExpressionAtom struct {
AstID string
GrlText string
Variable *Variable
FunctionCall *FunctionCall
Value reflect.Value // <-- evaluation result
Evaluated bool // <-- evaluation result
}
If the evaluation results were kept in a different type, then that type could be instantiated per evaluation. This would be
A couple of ways to maintain results in a separate structure:
Option 1
Utilize the unique AstID
on each rule definition type, and maintain a map of evaluation results that can be instantiated each time a KnowledgeBase is evaluated, e.g.
type evalResults struct {
Evaluated bool
Value reflect.Value
}
// keep some map[string]*evalResults, and when a rule or partial rule is evaluated, write to this map using AstID as the key
One concern here is that this solution may require a sync.Map
to enable concurrent writes of evaluation results, which could be less performant.
Option 2
More of an alternative approach to writes in Option 1, but a chan *evalResult
could be used in place of a sync.Map
, like this
type evalResults struct {
AstID string // <-- add the AstID and pass it along on the channel
evaluated bool
evalValueResult reflect.Value
evalErrorResult error
}
// when rule is evaluated
evalResultsChannel <- &evalResult{ AstID: myAstID, ... }
// in some result collection routine
for _, evalResult := range evalResultsChannel {
resultsMap[evalRules.AstID] = evalResult
}
This avoids any slowdown from a sync.Map
, but result writes become async, so it's hard to know when a rule's evaluation result is formally stored in the resultsMap
.
These are just a couple of ideas off the top of my head; there may be better solutions!
Is this decoupling of rule definition and evaluation result possible? Thanks in advance!
Describe alternatives you've considered
none
Additional context
none
Describe the bug
I just upgraded from 1.5.0 to the latest 1.6.0 and started getting Max cycle reached
, none of my rules had Retract
. I'm also using JSON to build the rules, and it works as expected if I add Retract
to the rules.
To Reproduce
Steps to reproduce the behavior:
Max cycle reached
Expected behavior
Previous versions Retract wasn't mandatory
UPDATE: also getting warning while adding rule entry : <rulename>. got rule entry <rulename> already exist, possibly already added by antlr listener
which wasn't happening before the upgrade
UPDATE 2: It's also running the THEN statements for all rules that passed instead of just one (highest priority), kinda like what i was looking for here
These following logs are noisy and can be at debug level instead.
grule-rule-engine/engine/GruleEngine.go
Line 49 in 1a23c03
and
grule-rule-engine/engine/GruleEngine.go
Line 140 in 1a23c03
Hi, I'm a bit confused or lost with this scenario, wonder if you could guide me: Imagine I have 20 rules. And with a given fact, 3 of them pass. The conflict set chooses the highest priority out of those 3. However I need all 3 to be executed (to run the action or then clause).
Use case: I have a triggers/automations system that one trigger (an event like page created
) could dispatch multiple actions (if the rules or conditions are met). Suppose that the user have created 2 automations (each automation consists of a trigger + GRL that has the action as a fact method) that their associated rules both pass for a given page creation event/trigger. One trigger/automation will send an email (one action or fact method), another will send a notification (another).
Couldn't find a way to achieve this. Is there a better way to do this or am I missing something?
in LoadRules_benchmark_test.go file, i find in code file the .grl file name is "100_complicated_rules.grl", but in folder the file name is "100_rules.grl". so the benchmark data is not right.
Is your feature request related to a problem? Please describe.
Lets assume we have this rule
rule SpeedUp "When testcar is speeding up we keep increase the speed." {
when
TestCar.SpeedUp == true && TestCar.Speed < TestCar.MaxSpeed
then
TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
DistanceRecorder.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}
and we have those structs
tc := &TestCar{
SpeedUp: true,
Speed: 0,
MaxSpeed: 100,
SpeedIncrement: 2,
}
dr := &DistanceRecorder{
TotalDistance: 0,
}
tc2 := &TestCar{
SpeedUp: true,
Speed: 0,
MaxSpeed: 100,
SpeedIncrement: 2,
}
How do i insert all the facts in dataContext and be able to run them with the same rule?
dataContext := context.NewDataContext()
dataContext.Add("TestCar", tc)
dataContext.Add("DistanceRecorder", dr)
dataContext.Add("TestCar", tc2)
This does not work for me. Am I doing something wrong?
The problem is that i want to have both the facts on DataContext because i may want to later add a new rule that will be applied to both the facts.
As i see it right now the rules apply on the keys and not on the structs
We see a rule description pattern like
rule <rule_name> <rule_description>
<attribute> <value> {
when
<conditions>
then
<actions>
}
What I want is do some extra operations in <actions>
part other than the basic functions as posted in docs. It looks like
rule <rule_name> <rule_description>
<attribute> <value> {
when
<conditions>
then
<some actions>;
// Create some new structs in Golang
// Store (i.e. insert) those new data strcuts into database (e.g. PostgreSQL)
<some actions>
}
Let's go with the this example
rule TestOverspeed "Check if the car overspeed." {
when
TestCar.Speed > TestCar.MaxSpeed
then
// 1. Create a new data struct in Golang, like
// type CarOverspeed struct {
// Speed int64
// Location string
// PlateNumber string
// }
// 2. Store the details of the car: speed, location, plate number,
// 3. and then insert the struct to PostgreSQL use orm or other skill
//
// Above steps mean we may do some operations with `TestCar` as an
// argument rather manipulate `TestCar` itself.
}
So any idea?
Describe the bug
when using a map in the data context and attempting to reference it in a rule GRL errors occur.
For example the rule:
rule testRule "set internet revenue" salience 10 {
when
ORDER["IntAttribute"] == 123
then
OUTPUT["result"] = "testResult";
Retract("testRule");
}
Fails with the following errors:
line 4:9 token recognition error at: '['
line 4:24 token recognition error at: ']'
line 4:10 mismatched input '"IntAttribute"' expecting THEN
line 6:10 token recognition error at: '['
line 6:19 token recognition error at: ']'
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '"IntAttribute"' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '==' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '123' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected 'then' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected 'OUTPUT' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '"result"' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '=' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '"testResult"' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected ';' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected 'Retract' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '(' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected '"testRule"' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected ')' lib=grule struct=GruleParserV2Listener
ERRO[0000] GRL error, after '[salience 10 { when ORDER]' and then unexpected ';' lib=grule struct=GruleParserV2Listener
ERRO[0000] Loading rule resource : Byte array resources 170 bytes failed. Got GRL error, after '[salience 10 { when ORDER]' and then unexpected ';'. Time take 2 ms
panic: error were found before builder bailing out. Got GRL error, after '[salience 10 { when ORDER]' and then unexpected ';'
To Reproduce
Steps to reproduce the behavior:
package main
import (
json "encoding/json"
"fmt"
ast "github.com/hyperjumptech/grule-rule-engine/ast"
builder "github.com/hyperjumptech/grule-rule-engine/builder"
engine "github.com/hyperjumptech/grule-rule-engine/engine"
pkg "github.com/hyperjumptech/grule-rule-engine/pkg"
)
func main() {
// Create example JSON
inputjson := `{
"IntAttribute": 123,
"StringAttribute": "Some string value",
"BooleanAttribute": true,
"FloatAttribute": 1.234,
"TimeAttribute": "2020-03-11T10:00:00Z",
}`
// unmarshal the json into a map
var input map[string]interface{}
json.Unmarshal([]byte(inputjson), &input)
// create the new Data Context
dataCtx := ast.NewDataContext()
// Add the input
err := dataCtx.Add("INPUT", &input)
if err != nil {
panic(err)
}
// add an output map
var output map[string]interface{}
err = dataCtx.Add("OUTPUT", &output)
if err != nil {
panic(err)
}
workingMemory := ast.NewWorkingMemory()
knowledgeBase := ast.NewKnowledgeBase("test", "1.0.0")
ruleBuilder := builder.NewRuleBuilder(knowledgeBase, workingMemory)
// example rules
drls := `
rule testRule "set internet revenue" salience 10 {
when
ORDER["IntAttribute"] == 123
then
OUTPUT["result"] = "testResult";
Retract("testRule");
}
`
byteArr := pkg.NewBytesResource([]byte(drls))
err = ruleBuilder.BuildRuleFromResource(byteArr)
if err != nil {
panic(err)
}
engine := engine.NewGruleEngine()
err = engine.Execute(dataCtx, knowledgeBase, workingMemory)
if err != nil {
panic(err)
}
fmt.Println(output["result"])
}
Expected behavior
Expected the rule to run and produce the output testResult
I try to run go build main.go
and get
build rule_engine: cannot load github.com/hyperjumptech/grule-rule-engine: module github.com/hyperjumptech/grule-rule-engine@latest found (v1.2.4), but does not contain package github.com/hyperjumptech/grule-rule-engine
My main.go looks like this
package main
import (
"fmt"
grule "github.com/hyperjumptech/grule-rule-engine"
"time"
)
type MyFact struct {
IntAttribute int64
StringAttribute string
BooleanAttribute bool
FloatAttribute float64
TimeAttribute time.Time
WhatToSay string
}
func main() {
myFact := MyFact{
IntAttribute: 123,
StringAttribute: "Some string value",
BooleanAttribute: true,
FloatAttribute: 1.234,
TimeAttribute: time.Now(),
}
println(myFact.GetWhatToSay("hello world"))
dataCtx := grule.ast.NewDataContext()
err := dataCtx.Add("MF", myFact)
if err != nil {
panic(err)
}
}
func (mf *MyFact) GetWhatToSay(sentence string) string {
return fmt.Sprintf("Let say \"%s\"", sentence)
}
my go.mod looks like this
module rule_engine
go 1.13
require (
github.com/hyperjumptech/grule-rule-engine v1.2.4
)
complains saying Build constraints exclude all Go files in github.com/hyperjumptech/grule-rule-engine
when hovered over import.
rule checkPersonalUserLoginPassword "check password whether robust."{
when
Fact.CorpId<=0&&Fact.CheckPassWord("^1([38][0-9]|14[57]|5[^4])\d{8}$")
then
...
}
execute with error
time="2020-07-14T11:26:30+08:00" level=error msg="Loading rule resource : Byte array resources 256 bytes failed. Got error parsing quoted string (\"^1([38][0-9]|14[57]|5[^4])\\d{8}$\"): invalid syntax. Time take 3 ms"
panic: error were found before builder bailing out. Got error parsing quoted string ("^1([38][0-9]|14[57]|5[^4])\d{8}$"): invalid syntax
In the documentation, it was mentioned that the default Salience is 10.
But actually, when DRL not mention a salience on a rule, the Salience is 0.
Proposed solution is to correct the documentation, and mention that the Salience default is 0 (zero).
Is your feature request related to a problem? Please describe.
I can't find a way to close the grule debug log, its a little noisy
Describe the solution you'd like
supply a api to set the log level or shut down the log
I'm curious why not support comparison string values.
Here's an example rule:
rule CompareString "Comparison two string values" salience 10 {
when
"A" < "B"
then
Retract("CompareString");
}
The error message is can not use data type of string in LT comparison
.
Similar errors happened on <=
(LTE), >
(GT) and >=
(GTE).
Describe the bug
I upgraded grule-rule-engine from v1.6.2 to v1.7.0 and my old rule does not work as expected.
More concrete, I called builtin.Changed via go
and it does not remove the F1.GetProgress() out of working as expected thus my execution lead to Max cycle reached
error (it worked well in version 1.6.2). I copied my code below
To Reproduce
Steps to reproduce the behavior:
rule Reviewing "reviewing" salience 1 {
when
F1.GetProgress() == 10
then
F1.Result = "good job";
Complete();
}
rule Task1 "task 1" salience 0 {
when
F1.GetProgress() > 5 && F1.GetProgress() < 10
then
F1.DoJob();
}
rule Task2 "task 2" salience 0 {
when
F1.GetProgress() <= 5
then
F1.DoJob();
}
rule TakeABreak "break" salience -1 {
when
F1.GetProgress() == 5
then
Retract("Task2");
Retract("TakeABreak");
}
and the struct
type PF1 struct {
Progress int64
Result string
builtin ast.BuiltInFunctions
}
func (p *PF1) DoJob() {
fmt.Println("[Pipeline Rule]", "DoJob")
p.Progress += 1
p.builtin.Changed("F1.GetProgress()")
}
func (p *PF1) WhichTask(task string) bool {
fmt.Println("[Pipeline Rule]", task)
return true
}
func (p *PF1) GetProgress() int64 {
fmt.Println("[Pipeline Rule]", "GetProgress")
return p.Progress
}
driven function
func pipeline_rule() {
var (
err error
ngin *engine.GruleEngine
)
pf1 := &PF1{}
dataCtx := ast.NewDataContext()
err = dataCtx.Add("F1", pf1)
knowledgeLibrary := ast.NewKnowledgeLibrary()
ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary)
fileRes := pkg.NewJSONResourceFromResource(pkg.NewFileResource("./rules/json_rule2.json"))
err = ruleBuilder.BuildRuleFromResource("PipelineRules", "0.0.1", fileRes)
knowledgeBase := knowledgeLibrary.NewKnowledgeBaseInstance("PipelineRules", "0.0.1")
if err != nil {
panic(err)
}
pf1.builtin = ast.BuiltInFunctions{
Knowledge: knowledgeBase,
WorkingMemory: knowledgeBase.WorkingMemory,
DataContext: knowledgeBase.DataContext,
}
ngin = engine.NewGruleEngine()
err = ngin.Execute(dataCtx, knowledgeBase)
if err != nil {
panic(err)
}
fmt.Println("[Pipeline Rule]", "PF1:", pf1.Result)
}
Expected behavior
As expected, F1.GetProgress() must be removed from the working memory by F1.DoJob() but in fact, nothing happened thus F1.GetProgress() will never be updated, it leads to Reviewing
rule will never be triggered then an endless execution
In a Many-Rule-Many-Fact rule engine, rule selection algorithm that is fast is very important. Thus, algorithm like Rete Algorithm is a must to implement instead of the current naive rule selection.
Reference to read:
Describe the bug
Similar to issue #117 , Using RealLiteral format in grl is faulty.
The current grammar for handling Real Literal is poor.
Grule should be able to handle formats as defined in Floating point literal here
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Builder should compile no problem and it works for handling real value constants
Additional context
Need to modify ANTLR4 grammar with regard to real literal.
1+2+3*4 = 24 in grule ?
I am currently beginning to evaluate grools for usage in a decision engine work at my organisation.
In a nutshell we are looking to have some http endpoints to receive data that I am thinking of converting to facts and saving to a database(pgsql).
Thereon we want to load our rules (.grl) and process them against the facts.
Now as an outcome of the inference I want a result to be saved in another table in the same database.
I am trying to understand what part of my flow can integrate grools.
Is there a way to specify the actions in code ?
Any advice will be helpful.
hi,
i can not find GRL keywords。can you give some help?
thanks very much!
func (ab *AbstractModel) SumN(in ...int64) int64 {
var sum int64
for _, v := range in {
sum += v
}
return sum
}
Hi, set value of embeded struct in the rule will cause panic
On the tutorial example, add a sub struct on struct MyFact
type Inner struct {
Age int
Name string
}
type MyFact struct {
IntAttribute int64
StringAttribute string
BooleanAttribute bool
FloatAttribute float64
TimeAttribute time.Time
WhatToSay string
Inn Inner
}
Then on the rule, set age field in then clause
MF.Inn.Age = 65;
Run the test , A panic will rise:
Failed execution rule : CheckValues. Got error attribute named Age not exist in struct
I tried to modify func traceSetValue in Datacontext.go as following
// return traceSetValue(objVal, path[1:], newValue)
return traceSetValue(objVal.Addr().Interface(), path[1:], newValue)
It works.
Is Rule versioning supported or can it be achieved?
For instance - A case from the past can be re-assessed using the rules that were applicable in the period that the case deals with.
Describe the bug
If at least 1 rule doesn't contain description call to NewKnowledgeBaseInstance()
will panic.
To Reproduce
rule WillPanic {
when Num == 1
then
Num = 2;
Log("this should be logged");
}
NewFileResource()
lib.NewKnowledgeBaseInstance(kbName, kbVersion)
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xcec79c]
Expected behavior
Maybe description should be optional. Grule should print clear error message including the problematic rule name.
Go 1.14 is not supported, make error!
I think it should be easy to support logic NOT (!) operator.
The rule would looks like:
rule RuleOne "Some rule description." salience 10 {
when
!(someContext.attributeA.Match("123") &&
someContext.attributeA.Contains("1"))
then
...
Without NOT operator, I have to write the rule like:
rule RuleOne "Some rule description." salience 10 {
when
someContext.attributeA.Match("123")==false ||
someContext.attributeA.Contains("1")==false
then
...
Is your feature request related to a problem? Please describe.
When using helper functions I would like to be able to access data returned from the method call.
Describe the solution you'd like
I would like to be able to write a rule such as this:
rule NestedFunctionCall "Use a nested function call" : 10 {
when
Order.User().Name == "bob"
then
....
}
where in Go
type OrderInfo struct {
uid string
Product string
...
}
type UserInfo struct {
UserID string
Name string
}
func (o *OrderInfo) User() (*UserInfo) {
return someDB.GetUser(o.uid)
}
This is a trivial example but in more complex cases this is useful for accessing data when necessary that may be slow to access and in most cases is unnecessary.
Describe alternatives you've considered
I have considered using multiple helper functions to do this, nesting them as necessary but this results in a very clunky rule and obfuscates the meaning of the rule in my opinion.
Additional context
I have not got any prior experience using Antlr so I am not sure if this is possible but I imagine it should be.
Building a target for ARM7 (go build env GOOS=linux GOARCH=arm GOARM=7 ...
) with a project that depends on grule-rule-engine
v1.6.2 fails with the following error:
env GOOS=linux GOARCH=arm GOARM=7 go build -o dist/development/arm7 ./...
# github.com/hyperjumptech/grule-rule-engine/antlr/parser/grulev2
../../../go/pkg/mod/github.com/hyperjumptech/[email protected]/antlr/parser/grulev2/grulev2_parser.go:1255: constant 4026532015 overflows int
../../../go/pkg/mod/github.com/hyperjumptech/[email protected]/antlr/parser/grulev2/grulev2_parser.go:2835: constant 4034920623 overflows int
Do we have any benchmarks available for Grule rule engine ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.