GithubHelp home page GithubHelp logo

hyperjumptech / grule-rule-engine Goto Github PK

View Code? Open in Web Editor NEW
2.1K 59.0 326.0 10.87 MB

Rule engine implementation in Golang

License: Other

Makefile 0.08% Go 91.17% ANTLR 1.21% CSS 0.03% HTML 0.82% JavaScript 6.69%
golang rule-engine rule inference-engine rule-based rule-based-engine hacktoberfest hacktoberfest2021

grule-rule-engine's People

Contributors

63isok avatar anjali0508 avatar ariya avatar aviadhahami avatar davidsonff avatar dependabot[bot] avatar derekwyatt avatar fgbravo avatar fivezh avatar garychristianto avatar gbellmann avatar hjblom avatar inhuman avatar jinagamvasubabu avatar jtr860830 avatar justenwalker avatar kbakdev avatar leonardtan13 avatar liouxiao avatar matsuyoshi30 avatar mrkish avatar mvberg avatar newm4n avatar niallnsec avatar plasticmacaroni avatar sapiderman avatar shanhuhai5739 avatar sourcesoft avatar trancee avatar troush avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grule-rule-engine's Issues

it does not support short-circuit evaluation

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

REAL Literal is poorly defined.

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:

  1. Create rule containing real literal according to golang's floating point literal.
  2. Build the rule.
  3. Builder will result in error.

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.

Save condition result for use in then clause?

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

Working on quite major updates. Please hold your horse guys.

Im currently working on major changes in the Grule Rule Engine.
It involves changes in

  1. ANTLR4 grammar changes which leads to ...
  2. AST graph generation which leads to ...
  3. Opportunity to enhance Golang value graph to be more predictable, testable yet simpler.

This updates will fix lots of issues, features and stability related issue.
Im aiming,

  1. Introduce array and map traversal in rule.
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";
}
  1. Function chaining and Constant's functions
    when
       person.GetFriendByName("lucas").IsFemale() &&
       person.GetAddress().Contains("Hill Rd") &&
       person.GetCoupon().StartWith("hill".toUpper())
   then
       ...
  1. JSON fact support
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.

Support comparison string values

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).

Support to jump to other rule

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.

Nested method calls

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.

panic in NewKnowledgeBaseInstance if rule description is missing

Describe the bug
If at least 1 rule doesn't contain description call to NewKnowledgeBaseInstance() will panic.

To Reproduce

  1. Add rule to GRL file
rule WillPanic {
    when Num == 1
    then
        Num = 2;
        Log("this should be logged");
}
  1. load it with NewFileResource()
  2. execute lib.NewKnowledgeBaseInstance(kbName, kbVersion)
  3. app will crash
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.

Supporting concurrent threads executing rules

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

  • Splitting 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...

Integer overflow when building for ARM7

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

[Info]: After the shortcircuit evaluation the benchmarks looks even better

@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

import and build fails while using go.mod

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.

How to use Regexp string in rule

        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

builtin.Changed does not affect a function in v1.7.0

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:

  1. I create code for this and that
    I have these rules
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

ERRO[0000] Failed execution rule : CheckValues. Got error invalid argument types for function CalcAbc(). argument #0, require int but int64 lib=grule struct=GruleEngineV2 panic: invalid argument types for function CalcAbc(). argument #0, require int but int64

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)

}

Set value of embeded struct in the rule will cause panic

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.

Condition is not re-evaluated while the fact changed

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.

rule Execute()?

hi, thanks.
How do I know which specific rule was successfully or failed executed?
Can it be returned by the execute function?

Are Maps and Slices Not supported?

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:

  1. Use the following code:
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"])
}
  1. compile and run.

Expected behavior
Expected the rule to run and produce the output testResult

rule applied to structs not struct keys

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

Big numbers conversion issue in Grule JSON format

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:

  1. Create a new Grule in JSON format and run ParseJSONRuleset() function
[{
    "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.

Question: Find required facts for a goal

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:

  1. if object.has("wheels") and object.has("engine") then object.classification = "car"
  2. if object.classification == "car" and object.colour == "red" then car.make = "ferrari"
  3. If car.make == "ferrari" and driver.age < 30 then insurance.risk = "high"

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)

  • does the object have wheels?
  • does the object have an engine
  • what is the object colour ?
  • is the object a car ?
  • what make is the car ?

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 ?

benchmark params error

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.

Need recommendation on usage

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.

Questions for MaxCycle

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) + "---");
	}

decouple rules and evaluations

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:

  1. cloning still takes CPU cycles, and for our system, which sees potentially millions of events per day, this can add up to quite a bit of CPU usage.
  2. cloning takes up quite a bit of memory, and may actually have a memory leak, unless the leak was actually caused by our own code, which is also possible (I didn't profile deep enough to make a strong claim). The point is, cloning takes a non-zero amount of memory per event, and at millions of events per day, the memory allocations are significant.

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 lot cheaper memory-wise, since rule definitions aren't being copied in memory per execution, and
  • it would save any CPU usage that's lost to cloning a KnowledgeBase.

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

Suggest that Execute start and finish logs be at debug level

These following logs are noisy and can be at debug level instead.

log.Infof("Starting rule execution using knowledge '%s' version %s. Contains %d rule entries", knowledge.Name, knowledge.Version, len(knowledge.RuleEntries))

and

log.Infof("Finished Rules execution. With knowledge base '%s' version %s. Total #%d cycles. Duration %d microsec.", knowledge.Name, knowledge.Version, cycle, time.Now().Sub(startTime))

Support logic NOT operator

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
        ...

Getting Max cycle reached with 1.6.0

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:

  1. Use v1.6.0
  2. Add one or multiple rule(s) without having Retract at the end for that/those rule(s)
  3. See error 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

Can't execute engine without error

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)
}

Question: ANTLR listener or visitor?

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.

Is there any way i can pull all the rules matching a particular fact

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

How can I close the grule debug log

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

Multi-instance

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

Question: How to execute all the selected rules' actions instead of highest salience?

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?

Question: is it possible (or making sense) to do some custom operations in <actions> part other than manipulate fact itself?

Question

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>
}

Example

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.
}

Possible solutions

  • Built-in functions can't handle this.
  • I see Custom Functions section, but it seems not the right way to the answer.

So any idea?

Got error GruleEngine

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
}

Can not load rules in Windows with pkg.NewFileResourceBundle

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:

  1. create rule file at "C:\rules\1.grl"
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");
}
  1. create code like:
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)
        }
    }
}
  1. Run the code and the result is:
resources = []

Expected behavior
I would like to see the result should be:

resources = [File resource at C:\\rules\\1.grl]

Version up

Could you up version to v1.0.1 please

Wrong Default Salience in Documentation.

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).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.