cachecontrol / json-rules-engine Goto Github PK
View Code? Open in Web Editor NEWA rules engine expressed in JSON
License: ISC License
A rules engine expressed in JSON
License: ISC License
Is there a way to determine, on failure
, which rule/condition failed?
After installing json-rules-engine from npm, attempted to install the --dev dependencies in order to run tests (i.e. npm test). Receiving these errord from npm install --dev
INITIAL ERROR
>
babel --stage 1 -d dist/ src/
src/ doesn't exist
npm ERR! Windows_NT 10.0.10586
npm ERR! argv "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "run" "compile"
npm ERR! node v6.2.1
npm ERR! npm v3.9.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] compile: babel --stage 1 -d dist/ src/
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the [email protected] compile script 'babel --stage 1 -d dist/ src/'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the json-rules-engine package,
npm ERR! not with npm itself.
FOLLOWED BY ERROR
npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: [email protected]
npm WARN [email protected] requires a peer of webpack@1 || ^2.1.0-beta but none was installed.
npm ERR! Windows_NT 10.0.10586
npm ERR! argv "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "--only=dev"
npm ERR! node v6.2.1
npm ERR! npm v3.9.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] prepublish: npm run compile
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] prepublish script 'npm run compile'.
LOG
npm-debug.txt
The README says that "// run() returns events with truthy conditions" in this example:
// Run the engine to evaluate
engine
.run(facts)
.then(events => { // run() returns events with truthy conditions
events.map(event => console.log(event.params.message))
})
What about when the result is false?
What I am trying to achieve is something like:
// Run the engine to evaluate
engine
.run(facts)
.then(events => {
return true // return false
})
I want my function to return true if the conditions are met, false if they are not.
What's the way to achieve this?
@CacheControl So consider we need to have a rule which needs to check whether the volume of an item is bigger than say 100 and it takes three arguments:
How can we write this kind of rule that needs to multiply the three arguments to check the condition?
It would be nice to add custom operators to engine that can be used by the rules. For instance I need to check if my fact value as date is with in some range.
I am trying to do the following. Please let me know if its possible in any other way.
{
conditions:{
...
..
...
}
event: {
type: sendEmail,
params: {
from_email: $fact.from_email,
subject: "Test subject $fact.subject"
}
I want to place the values from the fact to the rules's event params dynamically.
I am ok with the solution having placeholders too.
Thanks in advance.
I've looked through the tree here and don't see a license for this. My legal department is pretty strict and I'd love to use this in my next project.
Hi! First of all, thank you for this library! But I'm stuck at a very basic step of rules implementation.
let's say we have the following rule that is the equivalent of if (fact1.value > fact2.value)
(fact comparison as seen in exemples)
let facts = {
"fact1" : {
"value": 3
},
"fact2" : {
"value": 2
},
"fact3" : {
"value": 2
}
}
let rule = {
conditions: {
all: [{
fact: 'fact1',
path: '.value',
operator: 'greaterThan',
value: {
fact: 'fact2',
path: '.value',
}
}]
},
event: { type: 'fact1-greaterthan-fact2' }
}
But how to include fact3.value to write the following condition: (fact.fact1.value > (fact.fact2.value + fact.fact3.value))
?
If it is supported it would be great to add an exemple to the fact-comparison exemple file.
Thank you for your support
Hi there... I tried and loved your json rules engine in node.js, so I tried installing it into the node-red environment using:
functionGlobalContext: {
rulesEngine:require("json-rules-engine")
}
Using a function node, I got the engine processing rules immediately... but strange things happen after the event has finished processing. Although it evaluates all the conditions accurately and fires a success or failure event, ruleResult.conditions received by the callback function is a strangely coded string (as though it's been double-stringified) and incomplete (it carries no evaluation status info, just the original conditions). Additionally, the almanac object is empty.
This is what it looks like:
conditions: "{"priority":1,"all":["{\"operator\":\"greaterThanInclusive\",\"value\":43,\"fact\":\"brian\"}","{\"operator\":\"greaterThanInclusive\",\"value\":23,\"fact\":\"sally\"}"]}"
Obviously this lack of information makes it hard to debug rules. Any ideas?
Let us say, we have the following JSON object. It is like looping through the nested JSON payload and looking for a value match. Here in the below example, indexes being hard-coded, which I wanted to avoid.
JSON Fact Payload :
{
"ObjectA": {
"ObjectA1": {
"dt": "2017-01-10T16:45:20.071Z",
"ObjectA1Array": [
{
"var1": "value1",
"ObjectA1ArrayObject1": {"var2": "value2"}
"ObjectA1ArrayObject2": {
"ObjectA1ArrayObject2Array": [
{
"var3": "value3",
"ObjectA1ArrayObject2ArrayObject1": {"var4": "value4"}
},
{
"var4": "value4",
"ObjectA1ArrayObject2ArrayObject2": {"var5": "value5"}
}
]
}
},
{
"var6": "value6",
"ObjectA1ArrayObject3": {"var7": "value"}
}
]
}
}
}
let exampleRule = {
conditions: {
all: [{
fact: 'JSONPayLoad',
operator: 'equal',
value: 'value1',
path: '.ObjectA.ObjectA1.ObjectA1Array[0].var1'
}, {
fact: 'JSONPayLoad',
operator: 'in',
value: ['value5', 'value10'],
path: '.ObjectA.ObjectA1.ObjectA1Array[1].var5'
}, {
fact: 'JSONPayLoad',
operator: 'greaterThanInclusive',
value: 'value5',
path: '.ObjectA.ObjectA1.ObjectA1Array[0].ObjectA1ArrayObject2.ObjectA1ArrayObject2Array[1].ObjectA1ArrayObject2ArrayObject2.var5'
},
event: {
type: 'something',
params: {
message: 'Some message'
}
},
priority: 100
}
Say
{
'a.b': {
'c': 2
}
}
path: 'a.b.c'
won't work?
Can we make it accept a list? path: ['a.b', 'c']
?
Or is it already there?
@CacheControl I was wondering if this rule engine can support decision tables.
What do I mean by that:
So a decision table is composed of many rules that differ usually by one or more conditions. So, for example, I will create a decision table to implement this logic:
Colour - Price
Blue - 10
Red - 11
Green - 13
Yellow - 14
Pink - 8
White - 13
Black - 20
So, you will read it like this, if a color is "Blue" then the price is $10. So instead of creating 7 separate rules, I will only create one rule, a decision table rule. Let me know what do you think.
We have large number of rule set say 150-200, All of them contains priority for execution.
We only want to retrieve only highest priority rule event, Thats why we want to run the entire rule engine and want to have functionality like findFirst.
Is there a way to achieve this?
Is there any issue here?
The path property in param, but it is not work.
https://github.com/CacheControl/json-rules-engine/blob/master/docs/rules.md#condition-helpers-path
And I found the correct method is put it under the root of condition. like example:
https://github.com/CacheControl/json-rules-engine/blob/master/examples/08-fact-comparison.js#L31
Call it a "score" or "weight" or both, but would it be interesting to others using json-rules-engine to obtain a score on the number of success/failed events based on a score?
That is if Rule A, and B pass but Rule C fails a score can be calculated in the Events definition or better the Conditions?
I've worked around this issue to some extent, but I'd be more motivated if others see this need and are willing to participate and advance a sort of weight/score based solution where we can push this module towards features for machine learning. Yet I understand that json-rules-engine is a rules engine, I guess I'm asking if our community/contributors see a layer above or features in json-rules-engine. See this as a request for features and/or input please.
Hey there,
Thanks for this great lib.
Is there anyway to have details about condition failure.
I would like to know precisely which fact is failing in a large set.
Hi
we have the number of a rule set for single questions like below
`conditions:
{
any: [{
all: [{
fact: 'gameDuration',
operator: 'equal',
value: '1',
factResult: [
{
"action": "do-something for gameDuration show ",
// i can add my custom params
}]
},
{
fact: 'personalFoulCount',
operator: 'equal',
value: '2',
factResult: [
{
"action": "do-something for personalFoulCount show",
}]
}],
},
{
all: [{
fact: 'gameDuration',
operator: 'Equal',
value: '2',
factResult: [
{
"action": "do-something for gameDuration hide",
}]
},
{
fact: 'personalFoulCount',
operator: 'equal',
value: '4',
factResult: [
{
"action": "do-something for personalFoulCount hide",
}]
}]
},
{
all: [{
fact: 'gameDuration',
operator: 'Equal',
value: '8',
factResult: [
{
"action": "do-something for gameDuration hide show",
}]
},
{
fact: 'personalFoulCount',
operator: 'equal',
value: '6',
factResult: [
{
"action": "do-something for gameDuration show hide",
}]
}]
},
]
},
event: {
type: 'json rule',
params: {
message: 'json rule example'
}
}`
and
let facts = { gameDuration: '2', personalFoulCount: '4' }
the rule will success for given facts
on success, i want the success "factResult" separately. is there any way we can access the succeed custom param.we tried with below example
https://github.com/CacheControl/json-rules-engine/blob/master/examples/09-rule-results.js
but could not able to succeed.
I get this error "SyntaxErrorL: Unexpected token import" in node.js v7.7.4 and v6.10.1 while running. How do you run this? Would be useful to add this information the readme
Attempting to run basic example of Rule Engine in a simple express server app, but unable to use json-rules-engine after npm install.
At first require
const Engine = require('json-rules-engine');
Getting error
Error: Cannot find module 'babel-core/register'
at Function.Module._resolveFilename (module.js:440:15)
Looking at stacktrace, requesting babel-core/register from json-rules-engine index.js
To work-around error, went into json-rules-engine module directory and did an npm install --dev. But don't believe this is intended resolution.
Currently I'm getting the complete rule object back as part of rule failurecallback but for rule successCallback, I only get the event object. Is it possible to get the full rule object back as part of the successCallback.
Ex: Say the facts are
{ "avg_def_density_tolerance_hit": "yes", "tot_developers": 900, "tot_defects": 1003, "open_stories": 12 }
And I want to write a rule that will pass if tot_defects > open_stories
Suggestion: If this is not easy to implement or you'd rather not support it, please consider enhancing the addOperator
method to pass in the complete rule and all the facts: that way others can add the logic necessary.
I have integrated json-rules-engine with a project I am working on and the performance seems much slower than I would expect. With 37 rules, it takes about 49ms to execute. The rules are very simple - simple regular expression checks. We are expecting to load 100s of rules and were hoping to have it execute more in the range of 10ms or so but we can't even hit that with a small number of rules. I haven't looked at the code yet, but I wanted to check and see what kind of performance is expected and if there are known performance issues in the code that could be improved. If there are known performance issues, I might take a stab at making some improvements. Thanks
@CacheControl @slmmm
As part of my project, I have to save rules and their dynamic facts in the database. I am able to save and retrieve the rules OK from the database (as they are JSON), wondering how can I do this when my fact is a javascript function like this:
let factDimensionsCheck = new Fact('fact-dimensions-check', function (params, almanac) { return Promise.all([almanac.factValue(params.fact1), almanac.factValue(params.fact2)]) .then(factValues => { let fact1Value = factValues[0]; let fact2Value = factValues[1]; let operator = params.operator; return engine.operators.get(operator).evaluate(fact1Value, fact2Value); }) });
Hello,
thanks for this great lib! 👍
Is there a reason why the rule can't return multiple events when it succeed ?
Hello,
I have some operators that doesn't require a condition value (EXISTS, NOT EXISTS)
Could it be made optional ? I can pass value: null
but would be better to just not pass it at all. 👍
Can you please add a synchronous run function? I am not making any I/O calls and everything is in memory in node so your parallel processing provides no benefit and it greatly complicates implementation.
Or provide me with access your github and I will do it.
Hey Cache,
Can't find any other way to DM you so I'm logging an issue ¯_(ツ)_/¯. I'm building my own version of a json-rules-engine (https://github.com/dlombardi/rules_engine) that's very much inspired by yours. It's mainly for an educational purpose but I'm hoping to find a niche for it through dynamic forms based on rules. I was hoping to pick your brain a bit on the design philosophy behind the json-rules-engine especially related to the almanac, but also other things. Sorry again to do this through an issue. If theres anytime we can have a convo It would certainly help me grow as a developer and would be very much appreciated. I've learned a lot by looking at your code already.
I want to be able to do math in a fact. Adding to the trickiness the expression variables have different path. eg: i have object obj that has 2 sub objects a and b and i want to do a fact: obj.a.x * obj.b.y.
Is this possible with custom fact?
Hi @CacheControl ,
I want to implement bpmn module of Npm package with json-rules-engine. Could you please help me how to do it ?
I was wondering if there was a way to pass the results of a listener on engine.run? So if I did something like this:
engine.on('fouledOut', (params) => {
// Great!
const fouledOutDist = 20;
return { fouledOut: fouledOutDist }
});
return engine
.run(facts)
.then((events) => {
// I would want to get { fouledOut: 20 } to be accessed and pushed through the promise
return events.map(event => console.log(event.params.message))
});
Any way to do this idiomatically within the framework?
Steve
We are looking into using your library for a product hierarchy. We have the need to run a rule based on a dynamic quantity value, where the quantity of a child product cannot exceed the existing quantity of a parent product. I'm not seeing anything in your examples where value is dynamic. Is this possible?
Thanks!
Just saying, this is awesome! Thank you!
So consider I have a rule like this:
let factDimensionsCheck = new Fact('fact-dimensions-check', function (params, almanac) {
return Promise.all([almanac.factValue(params.fact1), almanac.factValue(params.fact2)])
.then(factValues => {
let fact1Value = factValues[0];
let fact2Value = factValues[1];
let operator = params.operator; // "greaterThan"
// each engine stores a registry of operators, which can be called to test one value against another
// just make sure to stick to the official operator names inside "params"; "equal", "notEqual", etc
return new Engine().operators.get(operator).evaluate(fact1Value, fact2Value);// returns true
})
});
let ruleDimensionsCheck = new Rule({
conditions: {
all: [{
fact: 'fact-dimensions-check',
operator: 'equal',
value: true,
params: {
fact1: '/data/consitem/length',
fact2: '/data/consitem/width',
operator: 'greaterThan'
}
}]
},
event: { }
...
And I am passing only one fact say "/data/consitem/length". I get an error like this on the console that the other fact is missing:
(node:14804) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Undefined fact: /data/consitem/width
This is possibly coming from this statement for fact2 which was not passed:
almanac.factValue(params.fact2)
Expected Results:
I am expecting that rule should not fire without throwing any warnings or error for facts being missing.
Current behavior causes redraw on every key type, even though not schema, nor uiSchema changes, this is costly behavior, which should be prevented
Hi,
I noticed that promises were not being observed on an onSuccess of rule callback. Seems this way in the code as well. Is there a workaround to this?
Also, what I may be struggling with is the approach to using rules-engines in general and wanting the truthy state of a rule to fire off an action and update the event parameters with the results. Is there a better approach to this?
{
conditions: {
all: [
{
fact: 'result',
path: '.parameters.email',
operator: 'notEqual',
value: ''
}
]
},
event: { },
onSuccess: ((event, almanac) => {
return almanac.factValue('result')
.then((result) => {
// Do something async. Rule engine completes run before this action completes.
})
})
}
Thanks,
Steve
I have spent a few hours coming to understand your engine and I really like what I've seen so far. However, I do not understand if and how rule chaining works. That is, how do I trigger rules based on the results from other rules?
I have an issue when serializing the rule result, namely the conditions inside this result.
2.0.0
let engine = new Engine();
engine.addRule({
"priority": 4,
"event": {
"type": "something"
},
"conditions": {
"any": [{
"fact": "data",
"path": ".age",
"operator": "lessThan",
"value": 18
}]
}
});
engine.on("success", (event: any, almanac: any, result: any) => {
console.log(JSON.stringify(result))
})
engine.run({
data: {
age: 16
}
});
The console will show:
{
"conditions": "{\"priority\":1,\"any\":[\"{\\\"operator\\\":\\\"lessThan\\\",\\\"value\\\":18,\\\"fact\\\":\\\"data\\\",\\\"path\\\":\\\".age\\\"}\"]}",
"event": {
"type": "something"
},
"priority": 4,
"result": true
}
If I look into the debugger it looks fine:
I did not really dig into this, but based solely on the debugger contents, I guess you "reused" the Condition
class for the rule results (at least of the conditions inside it). The custom serialization of this class is most likely causing the issue. IMO, you should make a different "class" for the rule results (condition) object.
It would be great if we can check facts by looping through inner array.
Sample use case, where student should score greaterThanInclusive of 48 in all subjects.
{
conditions: {
any: [{
all: [{
fact: 'student.age',
operator: 'equal',
value: 40
}, {
fact: 'student.name',
operator: 'greaterThanInclusive',
value: 'john'
}
]
}, {
forEach: {
array: 'student.subjects',
as: 'subject'
all: [{
fact: 'subject.mark',
operator: 'greaterThanInclusive',
value: 48
}, {
fact: 'subject.name',
operator: 'equal',
value: 'maths'
}
]
}
}
]
}
}
it's just a thought ...
@CacheControl Is there a way we can give names to our rules? So for example:
let volumeRule = new Rule({
**name: myVolumeRule //----- Note the name here**
conditions: {
all: [{
fact: 'volume',
operator: 'greaterThanInclusive',
value: 100
}]
},
event: { // define the event to fire when the conditions evaluate truthy
type: 'fouledOut',
params: {
message: 'Player has fouled out!'
}
}
,
priority: 100
});
So the reason for this is that when you are writing 100s of rules, you may want to give them name to identify which rule you are talking about. Also, if we are saving them into a database, we can save them in rows and identified by their names. Its helpful when you are managing a large number of rules.
I'm evaluating rules engine to include in my application. What software license does your project fall under?
Hi! I'm currently using the engine and it looks really good so far, congratulations!
I have a couple of questions related to the design, is this using Promises for any specific reason ? I did a quick look through the code but I couldn't find any I/O call/nextTick/timer usage,
I also saw you response on #14, and be glad to help in case you want to move forward to do a sync approach but adding a Promised interface for run
method.
Thanks!
Hi, this is a good library. But we need nested fact values, o use objects as facts, can be done?
Example:
var facts = {
test: {
a: "a",
b: "true"
},
displayMessage: true
}
rule.setConditions({
any: [{
fact: 'test.b',
operator: 'equal',
value: true
}]
})
Thank you for the great solution!
Is it possible to support IE8?
The issue is
getters & setters can not be defined on this javascript engine
I'm farirly new to node world but I'm facing troubles running the basic.js example. I'm sure I'm just looking in the wrong direction.
I've followed the script from travis (https://travis-ci.org/CacheControl/json-rules-engine) in order to copy-cat the setup of the json-rules-engine. I've successfully ran into unit test with success.
however, how I could run basic.js ? if I go in ./examples folder and run node basic.js. I'm getting following message
any idea or direction to look for ?
many thanks.
The object-hash version included your package has a known memory leak issue with new Node versions.
puleos/object-hash#39
We have seen performance drops and possible root cause of our app crashing using your rules engine crash our app. Any possibility to update the dependency version for object-hash?
I've come across an issue in which I'm using a single Engine instance to run multiple times with each run using different facts and expecting each run to be using a clean Almanac, but it appears the Almanac is reused or caching previous run() facts.
Reading the documentation for the Almanac I expected that facts added in the first run would not exist ("DNE") in subsequent runs.
Every time engine.run() is invoked, a new almanac is instantiated.
To recreate the issue I'm seeing, I modified the test for verifying independent runs in json-rules-engine (engine-run.test.js), From documentation, I expected that the 'age' fact would have activated only 1 time across the 6 runs, since the last 3 runs are for fact 'shoesize' and don't include an 'age' fact. That being the second proof of the Almanac caching across runs, because this test fails to throw exceptions for Undefined fact 'age' over the last 3 runs.
describe('SLMMM: independent runs', () => {
it('treats each run() independently', async () => {
await Promise.all([0, 0, 0, 30].map((age) => engine.run({age})))
await Promise.all([7, 9, 5].map((shoesize) => engine.run({shoesize})))
expect(eventSpy).to.have.been.calledOnce
})
})
Instead, the test fails because there are 4 activations instead of 1.
1 failing
Hi:
I would have liked the rule engine to ignore the fact condition if it is in "ANY" when the fact is not passed in, e.g.,
If my facts are these:
let facts = {
gameDuration: 40
};
And my rule is:
engine.addRule({
conditions: {
any: [{
fact: 'personalFoulCount',
operator: 'greaterThanInclusive',
value: 5
},
{
fact: 'gameDuration',
operator: 'equal',
value: 40
}]
},
event: { // define the event to fire when the conditions evaluate truthy
type: 'fouledOut',
params: {
message: 'Player has fouled out!'
}
}
});
I would have preferred the rule engine would not have errored out and ignore the fact that personalFoulCount is not there as it was in ANY condition clause.
does this use the rete algorithm?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.