Rule engine implementation in Golang

Overview

Gopheer Holds The Rule

Build Status Build Status Go Report Card License

"Gopher Holds The Rules"

Grule-Rule-Engine

import "github.com/hyperjumptech/grule-rule-engine"

Rule Engine for Go

Grule is a Rule Engine library for the Go (Golang) programming language. Inspired by the acclaimed JBOSS Drools, and done in a much simpler manner.

Like Drools, Grule has its own DSL or Domain-Specific Language.

Below is an example of Drools's DRL or Drools Rule Language:

rule "SpeedUp"
    salience 10
    when
        $TestCar : TestCarClass( speedUp == true && speed < maxSpeed )
        $DistanceRecord : DistanceRecordClass()
    then
        $TestCar.setSpeed($TestCar.Speed + $TestCar.SpeedIncrement);
        update($TestCar);
        $DistanceRecord.setTotalDistance($DistanceRecord.getTotalDistance() + $TestCar.Speed);
        update($DistanceRecord);
end

Grule's GRL is as follows:

rule SpeedUp "When testcar is speeding up we keep increase the speed." salience 10  {
    when
        TestCar.SpeedUp == true && TestCar.Speed < TestCar.MaxSpeed
    then
        TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
        DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

What is a Rule Engine

There isn't a better explanation than the article authored by Martin Fowler. You can read the article here (RulesEngine by Martin Fowler).

Taken from TutorialsPoint website (with slight modifications),

The Grule Rule Engine is a Production Rule System that uses the rule-based approach to implement an Expert System. Expert Systems are knowledge-based systems that use knowledge representations to process acquired knowledge into a knowledgebase that can be used for reasoning.

A Production Rule System is Turing complete with a focus on knowledge representation to express propositional and first-order logic in a concise, non-ambiguous and declarative manner.

The brain of a Production Rules System is an Inference Engine that can scale to a large number of rules and facts. The Inference Engine matches facts and data against Production Rules – also called Productions or just Rules – to infer conclusions which result in actions.

A Production Rule is a two-part structure that uses first-order logic for reasoning over knowledge representation. A business rule engine is a software system that executes one or more business rules in a runtime production environment.

A Rule Engine allows you to define “What to Do” and not “How to do it.”

What is a Rule

(also taken from TutorialsPoint)

Rules are pieces of knowledge often expressed as, "When some conditions occur, then do some tasks."

When
   <Condition is true>
Then
   <Take desired Action>

The most important part of a Rule is its when part. If the when part is satisfied, the then part is triggered.

rule  <rule_name> <rule_description>
   <attribute> <value> {
   when
      <conditions>

   then
      <actions>
}

Advantages of a Rule Engine

Declarative Programming

Rules make it easy to express solutions to difficult problems and get the verifications as well. Unlike code, Rules are written with less complex language; Business Analysts can easily read and verify a set of rules.

Logic and Data Separation

The data resides in the Domain Objects and the business logic resides in the Rules. Depending upon the kind of project, this kind of separation can be very advantageous.

Centralization of Knowledge

By using Rules, you create a repository of knowledge (a knowledge base) which is executable. It is a single point of truth for business policy. Ideally, Rules are so readable that they can also serve as documentation.

Agility To Change

Since business rules are actually treated as data. Adjusting the rule according to business's dynamic nature becomes trivial. No need to re-build code or deploy as normal software development does - you only need to roll out sets of rules and apply them to knowledge repository.

Use Cases

The following cases are better solved with a rule-engine:

  1. An expert system that must evaluate facts to provide some sort of real-world conclusion. If not using a RETE-style rule engine, one would code up a cascading set of if/else statements, and the permutations of the combinations of how those might be evaluated would quickly become impossible to manage. A table-based rule engine might suffice, but it is still more brittle against change, and is not terribly easy to code. A system like Grule allows you to describe the rules and facts of your system, releasing you from the need to describe how the rules are evaluated against those facts, and hiding the bulk of that complexity from you.

  2. A rating system. For example, a bank system may want to create a "score" for each customer based on the customer's transaction records (facts). We could see their score change based on how often they interact with the bank, how much money they transfer in and out, how quickly they pay their bills, how much interest they accrue, how much they earn for themselves or for the bank, and so on. A rule engine could be provided by a developer, and the specification of the facts and rules can then be supplied by subject matter experts within the bank's customer analytics department. Decoupling these different teams puts the responsibilities where they should be.

  3. Computer games. Player status, rewards, penalties, damage, scores, and probability systems are many different examples of where rules play a significant part in most computer games. These rules can interact in very complex ways, often times in ways that the developer didn't foresee. Coding these dynamic situations through the use of a scripting language (e.g. Lua) can get quite complex, and a rule engine can help simplify the work tremendously.

  4. Classification systems. This is actually a generalization of the rating system described above. Using a rule engine, we can classify things such as credit eligibility, biochemical identification, risk assessment for insurance products, potential security threats, and many more.

  5. Advice/suggestion system. A "rule" is simply another kind of data, which makes it a prime candidate for definition by another program. This program can be another expert system or artificial intelligence. Rules can be manipulated by other systems in order to deal with new types of facts or newly discovered information about the domain which the ruleset is intending to model.

There are many other use-cases that would benefit from the use of a Rule-Engine. The above cases represent only a small number of the potential ones.

However, it is important to remember that a Rule-Engine not a silver bullet, of course. Many alternatives exist to solve "knowledge" problems in software, and those should be employed where they are most appropriate. One would not employ a rule engine where a simple if / else branch would suffice, for instance.

Theres's something else to note: some rule engine implementations are extremely expensive, yet many businesses gain so much value from them that the cost of running them is easily offset by that value. For even moderately complex use cases, the benefit of a strong rule engine that can decouple teams and tame business complexity seems to be quite clear.

Docs

Documentation page here

To dive into the Tutorial, see the Wiki Docs here on Github.

Benchmark

Loading rules into KnowledgeBase:

  • To load 100 rules into knowledgeBase it took 99342047 ns/op (took the highest value) that is equal to ~99.342047ms and (49295906 B/op) ~49.295906MB memory per operation

  • To load 1000 rules into knowledgeBase it took 933617752 ns/op (took the highest value) that is equal to ~933.617752ms and (488126636 B/op) ~488.126636 memory per operation

Executing rules against a fact:

  • To execute a fact against 100 rules, Grule Engine took ~9697 ns/op (took the highest value as base) that is hardly ~0.009697ms and 3957 B/op which is pretty fast.

  • To execute a fact against 1000 rules, Grule Engine took ~568959 ns/op (took the highest value as base) that is hardly ~0.568959ms and 293710 B/op which is also pretty fast.

You can read the detail report here

Our Contributors

Tasks and Help Wanted

Yes. We need contributors to make Grule even better and useful to the Open Source Community.

  • Need to do more and more and more tests.
  • Better code coverage test.
  • Better commenting for go doc best practice.
  • Improve function argument handling to be more fluid and intuitive.

If you really want to help us, simply Fork the project and apply for Pull Request. Please read our Contribution Manual and Code of Conduct

Issues
  • Panic on workingMemory when remove a rule

    Panic on workingMemory when remove a rule

    Describe the bug Hey guys, I'm working with the grule engine and I came across the following situation.

    I created a microservice that uses grule to analyze data over rules. In this microservice I have an API that allows adding and deleting a rule. But the service is crashing when I remove an existing rule and add a new rule. So, I created a test application only using grule to analyze the problem and I came across a panic: DEBU[0000] Cloning working memory POCRules:0.0.1 lib=grule-rule-engine package=ast DEBU[0000] Cloning 7 expressionSnapshotMap entries lib=grule-rule-engine package=ast panic: expression BMF.Value1 is not on the clone table

    Apparently the problem is when removing the rule just on the ruleEntries but the workingMemory remains the same.

    I will index the sample code that reproduce this error.

    So my question is, Am I using rule removal correctly? Or is this a bug in the removal?

    To Reproduce Code: https://gist.github.com/vitbaq/35f52f30bdfb442eb7a7a892ddb1d5d3 Log: https://gist.github.com/vitbaq/88a6f00edcadbb8724218a17551d113f

    Testing environment:

    • Operating System/Platform: Ubuntu 18.04.5 LTS (Bionic Beaver)
    • Go version: go1.13.5 linux/amd64
    • Grule-rule-engine version: 1.9.0
    opened by vitbaq 18
  • Retracted rule is not restored when the the engine is executed again

    Retracted rule is not restored when the the engine is executed again

    Describe the bug Retracted rules are not restored when the engine is executed again. From the Retract method's documentation, I'm assuming that when we call the engine.Execute(dataCtx, knowledgeBase) the retracted rule should be restored. However, this is not happening.

    To Reproduce Steps to reproduce the behavior: 1.Run the following code

    package examples
    
    import (
    	"fmt"
    	"strconv"
    	"testing"
    
    	"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"
    	"github.com/stretchr/testify/assert"
    )
    
    type ComputedInputs struct {
    	Name string
    	Bid  float64
    	Rate float64
    }
    
    const Grl = `
    	rule SampleRule "Hero/Honda" {
    		When
    			ComputedInputs.Name == "Hero" || ComputedInputs.Name == "Honda"
    		Then
    			Log("SampleRule");
    			ComputedInputs.Bid = ComputedInputs.Rate;
    			Retract("SampleRule");
    	}
    `
    
    func TestItems(t *testing.T) {
    	testData := []*struct {
    		CI      *ComputedInputs
    		WantBid float64
    	}{
    		{
    			CI: &ComputedInputs{
    				Name: "Hero",
    				Rate: 10,
    			},
    			WantBid: 10,
    		},
    		{
    			CI: &ComputedInputs{
    				Name: "Honda",
    				Rate: 10,
    			},
    			WantBid: 10,
    		},
    	}
    
    	// Prepare knowledgebase library and load it with our rule.
    	lib := ast.NewKnowledgeLibrary()
    	rb := builder.NewRuleBuilder(lib)
    	byteArr := pkg.NewBytesResource([]byte(Grl))
    	err := rb.BuildRuleFromResource("Tutorial", "0.0.1", byteArr)
    	assert.NoError(t, err)
    
    	engine := &engine.GruleEngine{
    		MaxCycle: 10,
    	}
    	knowledgeBase := lib.NewKnowledgeBaseInstance("Tutorial", "0.0.1")
    
    	for i, td := range testData {
    		t.Run(strconv.Itoa(i), func(t *testing.T) {
    			dataCtx := ast.NewDataContext()
    			err := dataCtx.Add("ComputedInputs", td.CI)
    			assert.NoError(t, err)
    
    			rules, _ := engine.FetchMatchingRules(dataCtx, knowledgeBase)
    			fmt.Printf("\nNo. of matching rules: %d\n", len(rules))
    
    			knowledgeBase := lib.NewKnowledgeBaseInstance("Tutorial", "0.0.1")
    			err = engine.Execute(dataCtx, knowledgeBase)
    			assert.NoError(t, err)
    			assert.Equal(t, td.WantBid, td.CI.Bid)
    		})
    	}
    }
    
    

    Expected behavior All the tests should pass

    Additional context The tests are passing if we create a new instance of knowledgeBase on every execution. Do we have to create a new instance of knowledge base whenever a rule is retracted?

    opened by vamshi-tg 10
  • decouple rules and evaluations

    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

    architecture 
    opened by rglyons 10
  • Question: How to execute all the selected rules' actions instead of highest salience?

    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?

    opened by sourcesoft 10
  • Getting Max cycle reached with 1.6.0

    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

    opened by sourcesoft 8
  • [Antlr4] Lexer and parser generation using Antlr4.9 and upgraded antlr4  runtime for to improve performance

    [Antlr4] Lexer and parser generation using Antlr4.9 and upgraded antlr4 runtime for to improve performance

    Description:

    • Current GruleV3 lexer and parser are generated using Antlt4 4.8, so upgraded to Antlr4 4.9.2
    • There was a go runtime performance issue has been fixed in Anltr4 Library. so I have upgraded Antlr4 library to the recent commit

    Reference: https://github.com/antlr/antlr4/pull/3243

    The core problem in Golang runtime: it wastes too much time on hash code computing because of the incorrect Murmur3 and Set and hasher implementation. Compare the Java and the original Go runtime, Go computes murmur3 ~80,000,000 times, but the Java runtime only ~600,000 times.
    
    Here's my benchmark in my MBP '16 2019, the g4 files are copied from https://github.com/antlr/grammars-v4/tree/master/sql/mysql/Positive-Technologies, the original Go runtime cost 4.8s, and this PR costs 171ms.
    

    Changes to go.mod:

    	github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210928164016-886d244c73ef
    
    architecture 
    opened by jinagamvasubabu 7
  • [Proposal] Is there any way to add a rule/s to existing knowledgebase ?

    [Proposal] Is there any way to add a rule/s to existing knowledgebase ?

    Is your feature request related to a problem? Please describe. Currently my application is using grule-rule-engine and its working perfectly fine. Thanks @newm4n this is a great work !!!! Coming to the implementation, i am saving all my rule DSL's in DB (postgres) and pulling them when the container boot up and create a new knowledgeBase and keep it in Memory. so far all fine. let's suppose if someone creates a new rule or rules then i need to pull all the rules again and recreate the knowledgeBase and this is a heavy process even though i know that there are no changes to the existing knowledgeBase except new rules i have to add to the existing knowledgeBase.

    Describe the solution you'd like What i am proposing is, when a new rule or rules are created i should be having a way to update the existing knowledgeBase with new rule instead of reloading all the N rules instead of creating a new knowledgeBase instance everytime with all the rules.

    I can contribute on this, just that i need some ideas or pointers from you to implement this feature :)

    Additional context Let me know if you need more info

    opened by jinagamvasubabu 7
  • Support logic NOT operator

    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
            ...
    
    enhancement 
    opened by liouxiao 7
  • Condition is not re-evaluated while the fact changed

    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.

    opened by irfanhanif 7
  • Are Maps and Slices Not supported?

    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

    enhancement 
    opened by BitShepherd 7
  • How to apply default value if no rule matched

    How to apply default value if no rule matched

    How to apply default value if no rule matched ? add a default rule like ?

    rule is_girl "set girl" salience 0 { when mf.sex == "famale" then mf.result = "girl" }

    rule is_boy "set boy" salience 0 { when mf.sex == "male" then mf.result = "boy" }

    rule default "set default value if no rule matched" salience 0 { when true then mf.result = "unknown"; }

    Thanks

    opened by kennir 6
  • how to get current rule name?

    how to get current rule name?

    Is your feature request related to a problem? Please describe. I want to get the name of the current RULE in the context

    Describe the solution you'd like

    rule nameHere "a sample rule" salience 0 {
        when
            ctx.HasResult("ok") == false
        then
            ctx.SetReuslt("ok") 
    }
    

    I want the SetResult can retrieve the name of the rule, which should be "nameHere" here

    Describe alternatives you've considered

    Additional context

    opened by kennir 0
  • Implement evaluation logic for pointer fact fields other than struct

    Implement evaluation logic for pointer fact fields other than struct

    Issue raised as result of the following inquiry...

    Is there any workaround (or plan to support) for pointers to primitive types in the Fact struct

    Something like this

    type Fact struct {
    	Foo *string
    	Bar *bool
    }
    

    The nature of my data requires value distinction after unmarshalling the following two sample json docs

    1. '{"Foo": "", "Bar": false}'
    2. '{}'
    With a Fact struct with no pointers
    type Fact struct {
    	Foo string
    	Bar bool
    }
    

    the resulting struct would be equivalent to this for both case 1 and 2 Fact{Foo: "", Bar: false}

    If we go with the struct with pointer we would get the following equivalents 1)

    Foo := ""
    Bar := false
    Fact{Foo: &Foo, Bar: &Bar}
    

    Fact{Foo: nil, Bar: nil}

    Problem I'm facing is that I'm not able to pull (and evaluate) the values pointed by the pointers

    Keep in mind I'm starting with go, so it might be a silly question Thank you

    Originally posted by @mledezma-r7 in https://github.com/hyperjumptech/grule-rule-engine/discussions/263

    enhancement 
    opened by mledezma-r7 0
  • How to export and import a single rule from the engine

    How to export and import a single rule from the engine

    There is a web service that has too many rules to load all of them at startup. A knowledgeLibrary instance is created back for each request. Since loading rules from strings is slow, we want to cache the loaded rules. Now I have a problem, how to export and import a rule separately?

    opened by mzs0207 2
  • Support of DMN Notation or/and Decision Tables

    Support of DMN Notation or/and Decision Tables

    Hi All,

    do you plan a kind of an "import/mapping" based on decision tables? Commonly, a decision table provides a compact and "user understandable" view on a set of rules and their actions.

    It would be great, to have a kind of converter from a decision table into the GRL representation of it. Additionally, it exists an OMG Standard, called DMN, to represent complex decision tables or rather models. By doing so, GRULE could be used as a headless fast execution engine, while the definition tables or models can be done in user friendly way somewhere else.

    best regards, Markus

    help wanted 
    opened by markuspatrick 12
Releases(v1.10.6)
  • v1.10.6(May 31, 2022)

    • Fixing issue #303 and #304 caused by the AST tree logic bug that misses adding ArrayMapSelector snapshot into the ExpressionAtom.
    • Bump up ANTLR4 to use the most recent version
    Source code(tar.gz)
    Source code(zip)
  • v1.10.5(Feb 25, 2022)

    • Adding new built in function MatchString for matching a string to regular expression. fixes issue #281
    • A way to track the cause of panic when importing GRB rule binary file. Right now, when the process raises a panic, a panic log were emitted but without showing panic message and the error message were not returned. Now the panic message were displayed in the log and the error returned.
    Source code(tar.gz)
    Source code(zip)
  • v1.10.4(Dec 2, 2021)

    • Minor fix where when using JSON as data into the context, when evaluation is evaluating JSON path beyond the JSON data it self, the evaluation yield a panic. Now evaluating JSON data in context will not panic but instead the rule entry will simply failed the evaluation and rule execution proceed normaly.
    • Minor broken link fix in the documentation
    Source code(tar.gz)
    Source code(zip)
  • v1.10.3(Nov 25, 2021)

    • Add option to error on failed grule engine execution
    • Fixed #268 : Where everytime engine execute using the same knowledgebase instance, the rule entries are not resetted as intended.
    Source code(tar.gz)
    Source code(zip)
  • v1.10.2(Oct 8, 2021)

    What's Changed

    • [Antlr4] Lexer and parser generation using Antlr4.9 and upgraded antlr4 runtime for to improve performance by @jinagamvasubabu in https://github.com/hyperjumptech/grule-rule-engine/pull/249
    • Remove rule entry PR by @jinagamvasubabu in https://github.com/hyperjumptech/grule-rule-engine/pull/252

    Full Changelog: https://github.com/hyperjumptech/grule-rule-engine/compare/v1.10.1...v1.10.2

    Source code(tar.gz)
    Source code(zip)
  • v1.10.1(Aug 16, 2021)

  • v1.10.0(Jul 20, 2021)

    Release v1.10.0

    • Added Go Embed feature for loading rules from embedded directory for those who uses Go 1.16
    • Parser Hardening. which fixes issue #214
    Source code(tar.gz)
    Source code(zip)
  • v1.9.1(Jul 11, 2021)

  • v1.9.0(May 19, 2021)

    • Ability to store and load KnowledgeBase inside KnowledgeLibrary into binary file (GRB). This makes the loading of rule sets from GRB to be much faster. Almost 10x faster.
    • Added In function for the string receiver data.
    • Engine Listener to know what rule is currently evaluated or executed
    • Feature for apps to see GRL script syntax errors.
    Source code(tar.gz)
    Source code(zip)
  • v1.8.5(Apr 14, 2021)

    Small quick path to fix possible bug when loading RuleSet from binary file, where io.EOF is not detected causing loading to fail. Although, by design, this should not be possible.

    Source code(tar.gz)
    Source code(zip)
  • v1.8.4(Apr 14, 2021)

    • Added feature to write all rule-sets inside KnowledgeBase into writer (eg. saved into file etc) in binary format (not GRL).
    • Read a knowledgebase (previously written as binary) from file directly into KnowledgeLibrary.
    Source code(tar.gz)
    Source code(zip)
  • v1.8.3(Mar 15, 2021)

    This patch includes support for UTF-8 character in SIMPLENAME within the grammar. With this change, from within the GRL rule name, variable name, function name can use non-ascii unicode characters except symbols, etc. Thus, golang struct, variables that has unicode in their name should be accessible from the rule script.

    The example/UnicodeRule_test.go file tested this new patch.

    Source code(tar.gz)
    Source code(zip)
  • v1.8.2(Jan 15, 2021)

  • v1.8.1(Dec 30, 2020)

  • v1.8.0(Dec 19, 2020)

    • Support for JSON as Fact, add your JSON string into DataContext straight away.
    • Support for Native type Fact, add int, string, bool, etc straight into DataContext.
    • Support for traversing JSON Fact data as Object or as Map, as well as writing value into it.
    • Support for accessing JSON Array element as well as replacing it value.
    • Support for appending values into JSON Array fact.
    Source code(tar.gz)
    Source code(zip)
  • v1.7.2(Dec 9, 2020)

    • Fixes the cloning problem where Expression do not clone the negation attribute
    • Added mutex for unique.NewID() to make sure that this function is thread/concurrent safe.
    Source code(tar.gz)
    Source code(zip)
  • v1.7.1(Dec 3, 2020)

    • Fixed ANTLR4 grammar to enable function chaining in the THEN scope
    • Fixed ANTLR4 grammar error that makes array/slice/map cannot be chained with function
    • Built-in function Changed is renamed to Forget to clearly tell the engine to forget about variable values or function invocation to make sure the engine look again into the underlying data context on the next cycle.
    Source code(tar.gz)
    Source code(zip)
  • v1.7.0(Nov 6, 2020)

    • Change the Grule ANTLR4 grammar for better structure, tested with ANTLR4 hierarchy and AST Tree.
    • FunctionCall AST graph is now under ExpressionAtom instead of Variable
    • Proper Integer and Float literals both support exponent format
    • Integer literal support Octal and Hexadecimal, Float literal support Hexadecimal.
    • Support negation.
    • Added more documentation about the new numbering literals and also re-arrange the menu in the documentation.
    Source code(tar.gz)
    Source code(zip)
  • v1.6.3(Nov 5, 2020)

    • Added way for user to change Logrus log level for Grule
    • Fix for crash when compiling Grule for ARM target.
    • Add logical OR and AND short circuit evaluation.
    Source code(tar.gz)
    Source code(zip)
  • v1.6.2(Oct 27, 2020)

    This patch release fixes few minor issues

    • Fixes panic when obtaining KnowledgeBase instance from KnowledgeLibrary if any of the rule don't have description.
    • Fixes panic when creating Rules using JSON with one of the rule have no description
    • Fixed goroutine leak due to possibly non closed context by caller of ExecuteWithContext.
    Source code(tar.gz)
    Source code(zip)
  • v1.6.1(Sep 3, 2020)

    This v1.6.1 patch

    • Remove crashing bug that caused by unaddressable value operation (on SetCap() and SetLen()) in array.Clear() and map.Clear().
    • Fix array.Append() operation that causes array operation fail because once the append is executed, the function does not properly return.
    Source code(tar.gz)
    Source code(zip)
  • v1.6.0(Sep 1, 2020)

    This release contains quite substantial new features into Grule. Some of them are :

    1. Better expression atom parsing, this replaces variable tracing with proper struct-field value traversal in golang reflect package.
    2. ANTLR4 grammar update to support array and map.
    3. The grammar update also enables function chaining.
    4. Enable constant's function. Such as "string".ToUpper(), as well as few functions are introduced.
    5. Removed Grule Event bus. As this is just too complicated and seems not usable by anyone.
    6. And many more.

    I have made all previous test runs okay in by this new changes. Thus, I hope its 99% backward compatible.

    Source code(tar.gz)
    Source code(zip)
  • v1.5.1(Aug 31, 2020)

Owner
Hyperjump
Open-source first. Cloud native. DevOps excellence.
Hyperjump
Weaviate is a cloud-native, modular, real-time vector search engine

Weaviate is a cloud-native, real-time vector search engine (aka neural search engine or deep search engine). There are modules for specific use cases such as semantic search, plugins to integrate Weaviate in any application of your choice, and a console to visualize your data.

SeMI Technologies 2.5k Jun 24, 2022
Self hosted search engine for data leaks and password dumps

Self hosted search engine for data leaks and password dumps. Upload and parse multiple files, then quickly search through all stored items with the power of Elasticsearch.

Davide Pataracchia 22 Aug 2, 2021
IBus Engine for GoVarnam. An easy way to type Indian languages on GNU/Linux systems.

IBus Engine For GoVarnam An easy way to type Indian languages on GNU/Linux systems. goibus - golang implementation of libibus Thanks to sarim and haun

Varnamproject 10 Feb 10, 2022
A BPMN engine, meant to be embedded in Go applications with minim hurdles, and a pleasant developer experience using it.

A BPMN engine, meant to be embedded in Go applications with minim hurdles, and a pleasant developer experience using it. This approach can increase transparency for non-developers.

Martin W. Kirst 32 Jun 26, 2022
Program to generate ruins using the Numenera Ruin Mapping Engine

Ruin Generator This is my attempt to build a program to generate ruins for Numenera using the rules from the Jade Colossus splatbook. The output only

Sean Hagen 0 Nov 7, 2021
An experimental vulkan 3d engine for linux (raspberry 4)

protomatter an experimental vulkan 3d engine for linux (raspberry 4).

Torben Schinke 0 Nov 14, 2021
A search engine for XKCD

xkcd_searchtool a search engine for XKCD What is it? This tool can crawling the comic transcripts from XKCD.com Users can search a comic using key wor

null 1 Sep 29, 2021
Nune - High-performance numerical engine based on generic tensors

Nune (v0.1) Numerical engine is a library for performing numerical computation i

Lord Larker 61 May 31, 2022
Nune-go - High-performance numerical engine based on generic tensors

Nune (v0.1) Numerical engine is a library for performing numerical computation i

Lord Larker 61 May 31, 2022
Zinc Search engine. A lightweight alternative to elasticsearch that requires minimal resources, written in Go.

Zinc Search Engine Zinc is a search engine that does full text indexing. It is a lightweight alternative to Elasticsearch and runs using a fraction of

null 9.2k Jun 26, 2022
a quick golang implementation of google pubsub subscriber for testing with the emulator.

gosub a quick golang implementation of google pubsub subscriber for testing with the emulator. it does one thing which is subscribing to a topic and r

Nam Pham 1 Oct 23, 2021
A simple and sussy project is an implementation of SOMMIP Lab 1 written in Golang

SOMMIP Lab 1 Isac Arthur Table of Contents About The Project Getting Started Prerequisites Installation Supported commands About The Project This very

Arthur 1 Nov 10, 2021
The official golang implementation for Project Anatha.

Project Anatha The official golang implementation for Project Anatha. For instructions on setting up a validator on the Anatha network, view the guide

潘毅 0 Nov 25, 2021
An ease to use finit state machine golang implementation.Turn any struct to a fsm with graphviz visualization supported.

go-fsm An ease to use finit state machine golang implementation.Turn any struct to a fsm with graphviz visualization supported. usage import github.co

FingerLiu 5 Dec 26, 2021
klaytnBreak - Official golang implementation of the Klaytn protocol

Klaytn Official golang implementation of the Klaytn protocol. Please visit KlaytnDocs for more details on Klaytn design, node operation guides and app

Zer0Luck 0 Jan 13, 2022
Go-wordle - Wordle implementation in GoLang

go-wordle A golang implementation of the popular New York Times game Wordle. Usa

Naveen Mahalingam 3 Mar 18, 2022
Go-sudoku - Sudoku generator and solver implementation in GoLang

go-sudoku An implementation of Sudoku generators and solvers in GoLang. Usage He

Naveen Mahalingam 3 Jun 19, 2022
Swagger 2.0 implementation for go

Swagger 2.0 This package contains a golang implementation of Swagger 2.0 (aka OpenAPI 2.0): it knows how to serialize and deserialize swagger specific

Go Swagger 7.7k Jun 21, 2022
Go implementation of the Rust `dbg` macro

godbg ?? godbg is an implementation of the Rust2018 builtin debugging macro dbg. The purpose of this package is to provide a better and more effective

Tyler Wince 184 Mar 31, 2022