Skip to content

Commit

Permalink
Add in_sorted_list operator (#21)
Browse files Browse the repository at this point in the history
- Adds in_sorted_list operator to make it faster when searching in huge lists. (Using binary search algorithm)
  • Loading branch information
lucianomagrao authored and diegoholiveira committed Oct 25, 2019
1 parent b9fcc37 commit c755fde
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
39 changes: 39 additions & 0 deletions jsonlogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"math"
"reflect"
"sort"
"strings"

"github.com/mitchellh/copystructure"
Expand Down Expand Up @@ -114,6 +115,40 @@ func _inRange(value interface{}, values interface{}) bool {
return toString(value) >= toString(i) && toString(j) >= toString(value)
}

// Expect values to be in alphabetical ascending order
func _inSorted(value interface{}, values interface{}) bool {
valuesSlice := values.([]interface{})

findElement := func(i int) bool {
element := valuesSlice[i]

if isSlice(valuesSlice[i]) {
sliceElement := valuesSlice[i].([]interface{})
start := sliceElement[0]
end := sliceElement[1]

return (toString(start) <= toString(value) && toString(end) >= toString(value)) || toString(end) > toString(value)
}

return toString(element) >= toString(value)
}

i := sort.Search(len(valuesSlice), findElement)
if i >= len(valuesSlice) {
return false
}

if isSlice(valuesSlice[i]) {
sliceElement := valuesSlice[i].([]interface{})
start := sliceElement[0]
end := sliceElement[1]

return toString(start) <= toString(value) && toString(end) >= toString(value)
}

return toString(valuesSlice[i]) == toString(value)
}

func _in(value interface{}, values interface{}) bool {
if isString(values) {
return strings.Contains(values.(string), value.(string))
Expand Down Expand Up @@ -580,6 +615,10 @@ func operation(operator string, values, data interface{}) interface{} {
return _in(parsed[0], parsed[1])
}

if operator == "in_sorted" {
return _inSorted(parsed[0], parsed[1])
}

if operator == "%" {
return mod(parsed[0], parsed[1])
}
Expand Down
53 changes: 53 additions & 0 deletions jsonlogic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,59 @@ func TestListOfRanges(t *testing.T) {
assert.JSONEq(t, expected, result.String())
}

func TestInSortedOperator(t *testing.T) {
rule := strings.NewReader(`{
"filter": [
{"var": "people"},
{"in_sorted": [
{"var": ".age"},
[
11.00,
[12, 14],
[13, 18],
2,
"20",
[32, 38],
"a",
["b", "d"]
]
]}
]
}`)

data := strings.NewReader(`{
"people": [
{"age":"18", "name":"John"},
{"age":20, "name":"Luke"},
{"age":18, "name":"Mark"},
{"age":40, "name":"Donald"},
{"age":11, "name":"Mickey"},
{"age":"1", "name":"Minnie"},
{"age":2, "name":"Mario"},
{"age":"a", "name":"Mario"},
{"age":"c", "name":"Princess"}
]
}`)

var result bytes.Buffer
err := Apply(rule, data, &result)
if err != nil {
t.Fatal(err)
}

expected := `[
{"age":"18", "name": "John"},
{"age":20, "name":"Luke"},
{"age":18, "name": "Mark"},
{"age":11, "name":"Mickey"},
{"age":2, "name":"Mario"},
{"age":"a", "name":"Mario"},
{"age":"c", "name":"Princess"}
]`

assert.JSONEq(t, expected, result.String())
}

func TestSomeWithLists(t *testing.T) {
rule := strings.NewReader(`{
"some": [
Expand Down

0 comments on commit c755fde

Please sign in to comment.