Skip to content

Commit

Permalink
Merge pull request #92 from cupofcat/lazy-eval
Browse files Browse the repository at this point in the history
chore(jsonlogic): Modified the logic to evaluate 'and' & 'or' lazily
  • Loading branch information
diegoholiveira authored Dec 16, 2024
2 parents 3d5f2e3 + dc9385d commit 659feac
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 14 deletions.
32 changes: 26 additions & 6 deletions jsonlogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ func unary(operator string, value interface{}) interface{} {
return b
}

func _and(values []interface{}) interface{} {
func _and(values, data interface{}) interface{} {
values = getValuesWithoutParsing(values, data)
var v float64

isBoolExpression := true

for _, value := range values {
for _, value := range values.([]interface{}) {
value = parseValues(value, data)
if isSlice(value) {
return value
}
Expand Down Expand Up @@ -101,9 +103,11 @@ func _and(values []interface{}) interface{} {
return v
}

func _or(values []interface{}) interface{} {
for _, value := range values {
if isTrue(value) {
func _or(values, data interface{}) interface{} {
values = getValuesWithoutParsing(values, data)

for _, value := range values.([]interface{}) {
if isTrue(parseValues(value, data)) {
return value
}
}
Expand Down Expand Up @@ -428,6 +432,22 @@ func parseValues(values, data interface{}) interface{} {
return parsed
}

// If values represents a map (an operation), returns the result. Otherwise returns the
// values without parsing. This means that each of the returned values might be a subtree
// of JSONLogic.
// Used in lazy evaluation of "AND" and "OR" operators
func getValuesWithoutParsing(values, data interface{}) interface{} {
if values == nil || isPrimitive(values) {
return values
}

if isMap(values) {
return apply(values, data)
}

return values.([]interface{})
}

func apply(rules, data interface{}) interface{} {
ruleMap := rules.(map[string]interface{})

Expand Down Expand Up @@ -462,7 +482,7 @@ func apply(rules, data interface{}) interface{} {
return some(values, data)
}

return operation(operator, parseValues(values, data), data)
return operation(operator, values, data)
}

// an empty-map rule should return an empty-map
Expand Down
21 changes: 13 additions & 8 deletions operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ func AddOperator(key string, cb func(values, data interface{}) (result interface
}

func operation(operator string, values, data interface{}) interface{} {
// "AND" evaluates values lazily, so parseValues() is delayed until needed
if operator == "and" {
return _and(values, data)
}

// "OR" evaluates values lazily, so parseValues() is delayed until needed
if operator == "or" {
return _or(values, data)
}

// Parse the entire remaining tree and eval recursively for non-lazy eval operators
values = parseValues(values, data)

// Check against any custom operators
for index, customOperation := range customOperators {
if operator == index {
Expand Down Expand Up @@ -66,14 +79,6 @@ func operation(operator string, values, data interface{}) interface{} {

parsed := values.([]interface{})

if operator == "and" {
return _and(parsed)
}

if operator == "or" {
return _or(parsed)
}

if operator != "in" && len(parsed) == 1 {
return unary(operator, parsed[0])
}
Expand Down

0 comments on commit 659feac

Please sign in to comment.