operational-decision-tree
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -19,3 +19,17 @@ | ||
var DecisionTree = new DTS() | ||
var conditionOpts = { | ||
percentageSplit: { | ||
hit: function (subjectData, cb) { | ||
console.log("HIT!") | ||
cb(null) | ||
}, | ||
run: function (subjectData, cb) { | ||
console.log("RUN!") | ||
var result = 2 | ||
cb(null, result) | ||
} | ||
} | ||
} | ||
var DecisionTree = new DTS({conditionOpts:conditionOpts}) | ||
DecisionTree.run(treeData, person, function (err, result) { | ||
@@ -22,0 +36,0 @@ if (err) console.error("ERROR", err) |
@@ -5,4 +5,4 @@ | ||
"name": "conditionAge", | ||
"property": "age", | ||
"comparison": { | ||
"property": "age", | ||
"operation": ">", | ||
@@ -15,6 +15,6 @@ "value": 40 | ||
"condition": { | ||
"name": "conditionPercent", | ||
"property": "random", | ||
"name": "percentageSplit", | ||
"comparisons": [ | ||
{ | ||
"property": "random", | ||
"operation": "<", | ||
@@ -24,2 +24,3 @@ "value": 30 | ||
{ | ||
"property": "random", | ||
"operation": "<", | ||
@@ -29,2 +30,3 @@ "value": 50 | ||
{ | ||
"property": "random", | ||
"operation": "<", | ||
@@ -34,2 +36,3 @@ "value": 90 | ||
{ | ||
"property": "random", | ||
"operation": "<", | ||
@@ -36,0 +39,0 @@ "value": 100 |
@@ -33,3 +33,3 @@ | ||
if (node.condition) { | ||
this.testCondition(node.condition, subjectData, function (err, result) { | ||
this.evaluateSubject(node.condition, subjectData, function (err, result) { | ||
if (err) return cb(err) | ||
@@ -39,3 +39,3 @@ self.handleDecision(node, subjectData, result, cb) | ||
} else if (node.conditions) { | ||
this.testConditions(node.conditions, subjectData, function (err, result) { | ||
this.evaluateSubject(node.conditions, subjectData, function (err, result) { | ||
if (err) return cb(err) | ||
@@ -49,36 +49,72 @@ self.handleDecision(node, subjectData, result, cb) | ||
DecisionTree.prototype.testCondition = function (condition, subjectData, cb) { | ||
DecisionTree.prototype.evaluateSubject = function (condition, subjectData, cb) { | ||
var self = this | ||
var priority = {} | ||
var subjectPriority = {} | ||
if (isFunction(subjectData[condition.property])) { | ||
// if a subject property is a function, run that function once per condition | ||
// (so each comparison in the condition uses the same result of the subject function) | ||
subjectData[condition.property](condition, subjectData, function (err, result) { | ||
priority[condition.property] = result | ||
var result = self.runComparisons(condition, subjectData, priority) | ||
return cb(null, result) | ||
subjectPriority[condition.property] = result | ||
self.testCondition(condition, subjectData, subjectPriority, cb) | ||
}) | ||
} else { | ||
var result = self.runComparisons(condition, subjectData, priority) | ||
return cb(null, result) | ||
this.testCondition(condition, subjectData, null, cb) | ||
} | ||
} | ||
DecisionTree.prototype.runComparisons = function (condition, subjectData, priority) { | ||
DecisionTree.prototype.testCondition = function (condition, subjectData, subjectPriority, cb) { | ||
var self = this | ||
this.runComparisons(condition, subjectData, subjectPriority, function (err, result) { | ||
if (err) return cb(err) | ||
if (self.opts.conditionOpts && self.opts.conditionOpts[condition.name] && self.opts.conditionOpts[condition.name].hit) { | ||
self.opts.conditionOpts[condition.name].hit(subjectData, function (err) { | ||
if (err) return cb(err) | ||
return cb(null, result) | ||
}) | ||
} else { | ||
return cb(null, result) | ||
} | ||
}) | ||
} | ||
DecisionTree.prototype.runComparisons = function (condition, subjectData, subjectPriority, cb) { | ||
var run = null | ||
if (this.opts.conditionOpts && this.opts.conditionOpts[condition.name] && this.opts.conditionOpts[condition.name].run) { | ||
run = this.opts.conditionOpts[condition.name].run | ||
} | ||
if (condition.comparison) { | ||
var result = this.compare(condition.property, condition.comparison, subjectData, priority) | ||
return result | ||
if (run) { | ||
run(subjectData, function (err, result) { | ||
if (err) return err | ||
console.log("RES", result) | ||
return cb(null, result) | ||
}) | ||
} else { | ||
var result = this.compare(condition.property, condition.comparison, subjectData, subjectPriority) | ||
} | ||
return cb(null, result) | ||
} else if (condition.comparisons) { | ||
for (var index in condition.comparisons) { | ||
var result = this.compare(condition.property, condition.comparisons[index], subjectData, priority) | ||
if (result) { | ||
return index | ||
for (var i=0; i<condition.comparisons.length; i++) { | ||
if (run) { | ||
run(subjectData, function (err, result) { | ||
if (err) return cb(err) | ||
return cb(null, result) | ||
}) | ||
} else { | ||
var result = this.compare(condition.property, condition.comparisons[i], subjectData, subjectPriority) | ||
if (result) { | ||
return cb(null, i) | ||
} | ||
} | ||
} | ||
return 0 | ||
} else { | ||
throw new Error("no comparisons in condition "+condition.name) | ||
} | ||
} | ||
DecisionTree.prototype.compare = function (property, comparison, subjectData, priority) { | ||
DecisionTree.prototype.compare = function (property, comparison, subjectData, subjectPriority) { | ||
var result = 0; | ||
if (priority[property]) { | ||
var val = priority[property] | ||
if (subjectPriority && subjectPriority[property]) { | ||
var val = subjectPriority[property] | ||
} else { | ||
@@ -128,2 +164,7 @@ var val = subjectData[property] | ||
break; | ||
case "exists": | ||
if (val) { | ||
result = 1; | ||
} | ||
break; | ||
case "in": | ||
@@ -154,3 +195,3 @@ for (var i in comparison.value) { | ||
for (var i in conditions) { | ||
this.testCondition(conditions[i], subjectData, function (err, result) { | ||
this.evaluateSubject(conditions[i], subjectData, function (err, result) { | ||
if (err) return cb(err) | ||
@@ -157,0 +198,0 @@ results.push(result) |
{ | ||
"name": "operational-decision-tree", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "A Decision Tree executor that uses decision modules to decide pathways. Can accept binary or arbitrary decision modules.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -8,6 +8,5 @@ | ||
var sampleTree = require('./test.json') | ||
var conditions = require('./conditions.js') | ||
tape('Test Basic Condition', function (t) { | ||
tape('Test Binary Condition', function (t) { | ||
var DT = new DTS() | ||
@@ -20,3 +19,3 @@ | ||
var condition = { | ||
name: 'conditionBasic', | ||
name: 'conditionBinary', | ||
property: 'value', | ||
@@ -30,6 +29,6 @@ comparison: { | ||
DT.testCondition(condition, subject, function (err, result) { | ||
t.ok(result, 'Basic condition returns true') | ||
t.equal(result, 1, 'Binary condition returns 1') | ||
subject.value = 0 | ||
DT.testCondition(condition, subject, function (err, result) { | ||
t.notOk(result, 'Basic condition returns false') | ||
t.equal(result, 0, 'Binary condition returns 0') | ||
t.end() | ||
@@ -40,2 +39,42 @@ }) | ||
tape('Test Arbitrary Condition', function (t) { | ||
var DT = new DTS() | ||
var subject = { | ||
random: 85 | ||
} | ||
var condition = { | ||
name: 'conditionArbitrary', | ||
property: 'random', | ||
comparisons: [ | ||
{ | ||
operation: '<', | ||
value: 30 | ||
}, | ||
{ | ||
operation: '<', | ||
value: 50 | ||
}, | ||
{ | ||
operation: '<', | ||
value: 100 | ||
} | ||
] | ||
} | ||
DT.testCondition(condition, subject, function (err, result) { | ||
t.equal(result, 2, 'Arbitrary condition returns 2') | ||
subject.random = 35 | ||
DT.testCondition(condition, subject, function (err, result) { | ||
t.equal(result, 1, 'Arbitrary condition returns 1') | ||
subject.random = 5 | ||
DT.testCondition(condition, subject, function (err, result) { | ||
t.equal(result, 0, 'Arbitrary condition returns 0') | ||
t.end() | ||
}) | ||
}) | ||
}) | ||
}) | ||
tape('Test Condition With Opts', function (t) { | ||
@@ -139,1 +178,2 @@ var DT = new DTS() | ||
21374
663
13