Skip to content

Commit

Permalink
"not equal" regexp operator
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Gurov committed Nov 30, 2019
1 parent 76f2882 commit 93c7ef9
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 15 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/bhmj/jsonslice)](https://goreportcard.com/report/github.com/bhmj/jsonslice)
[![GoDoc](https://godoc.org/github.com/bhmj/jsonslice?status.svg)](https://godoc.org/github.com/bhmj/jsonslice)


# JSON Slice

## What is it?
Expand Down Expand Up @@ -134,6 +133,7 @@ If `step` is negative:
`<` | Less than
`<=` | Less than or equal to
`=~` | Match a regexp<br>`[?(@.name =~ /sword.*/i]`
`!~` or `!=~` | Don't match a regexp<br>`[?(@.name !~ /sword.*/i]`
`&&` | Logical AND<br>`[?(@.price < 10 && @isbn)]`
`\|\|` | Logical OR<br>`[?(@.price > 10 \|\| @.category == 'reference')]`

Expand Down Expand Up @@ -179,7 +179,9 @@ ok github.com/bhmj/jsonslice 83.152s

## Changelog

**0.1.0** (2019-11-29) -- deepscan operator (`..`) added, slice with step `$[1:9:2]` is now supported, syntax extensions added.
**0.1.1** (2019-12-01) -- "not equal" regexp operator added (`!=~` or `!~`).

**0.1.0** (2019-11-29) -- deepscan operator (`..`) added, slice with step `$[1:9:2]` is now supported, syntax extensions added. `GetArrayElements()` removed.

**0.7.6** (2019-09-11) -- bugfix: escaped backslash at the end of a string value.

Expand Down Expand Up @@ -227,10 +229,11 @@ ok github.com/bhmj/jsonslice 83.152s
- [x] filters: complex expressions (with logical operators)
- [x] nested arrays support
- [x] wildcard operator (`*`)
- [x] bracket notation for multiple field queries
- [x] bracket notation for multiple field queries (`$['a','b']`)
- [x] deepscan operator (`..`)
- [x] syntax extensions: `$.'keys with spaces'.price`
- [x] flexible syntax: `$[0]` works on both `[1,2,3]` and `{"0":"abc"}`
- [ ] Optionally unmarshal the result

## Contributing

Expand Down
14 changes: 8 additions & 6 deletions expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ type tOperand struct {
Regexp *regexp.Regexp
}

var operator = [...]string{">=", "<=", "==", "!=", "=~", ">", "<", "&&", "||"}
var operatorCode = [...]byte{'G', 'L', 'E', 'N', 'R', 'g', 'l', '&', '|'}
var operatorPrecedence = map[byte]int{'&': 1, '|': 1, 'g': 2, 'l': 2, 'E': 2, 'N': 2, 'R': 2, 'G': 2, 'L': 2, '+': 3, '-': 3, '*': 4, '/': 4}
var operator = [...]string{">=", "<=", "==", "!=~", "!~", "!=", "=~", ">", "<", "&&", "||"}
var operatorCode = [...]byte{'G', 'L', 'E', 'r', 'r', 'N', 'R', 'g', 'l', '&', '|'}
var operatorPrecedence = map[byte]int{'&': 1, '|': 1, 'g': 2, 'l': 2, 'E': 2, 'N': 2, 'R': 2, 'r': 2, 'G': 2, 'L': 2, '+': 3, '-': 3, '*': 4, '/': 4}

type stack struct {
s []*tToken
Expand Down Expand Up @@ -165,7 +165,7 @@ func nextToken(path []byte, i int, prevOperator byte) (int, *tToken, error) {
break
}
// regexp
if path[i] == '/' && prevOperator == 'R' {
if path[i] == '/' && prevOperator&^('r'-'R') == 'R' {
return readRegexp(path, i)
}
// number
Expand Down Expand Up @@ -405,7 +405,7 @@ func execOperator(op byte, left *tOperand, right *tOperand) (*tOperand, error) {
if op == '+' || op == '-' || op == '*' || op == '/' {
// arithmetic
return opArithmetic(op, left, right)
} else if op == 'g' || op == 'l' || op == 'E' || op == 'N' || op == 'G' || op == 'L' || op == 'R' {
} else if op == 'g' || op == 'l' || op == 'E' || op == 'N' || op == 'G' || op == 'L' || op == 'R' || op == 'r' {
// comparison
return opComparison(op, left, right)
} else if op == '&' || op == '|' {
Expand Down Expand Up @@ -443,7 +443,7 @@ func opComparison(op byte, left *tOperand, right *tOperand) (*tOperand, error) {
res.Bool = false
return &res, nil
}
if op == 'R' {
if op == 'R' || op == 'r' {
if !(left.Type == cOpString && right.Type == cOpRegexp) {
return nil, errInvalidRegexp
}
Expand Down Expand Up @@ -511,6 +511,8 @@ func opComparisonString(op byte, left *tOperand, right *tOperand) (*tOperand, er
res.Bool = compareSlices(left.Str, right.Str) != 0
case 'R':
res.Bool = right.Regexp.MatchString(string(left.Str))
case 'r':
res.Bool = !right.Regexp.MatchString(string(left.Str))
default:
return left, errInvalidOperatorStrings
}
Expand Down
8 changes: 4 additions & 4 deletions jsonslice.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package jsonslice

/**
JsonSlice 1.0.0
JsonSlice 1.0.1
Michael Gurov, 2018-2019
MIT licenced
Expand Down Expand Up @@ -97,7 +97,7 @@ const (
cFullScan = 1 << iota // array slice: need fullscan
cFilter = 1 << iota // filter
cWild = 1 << iota // wildcard (*)
cDeep = 1 << iota // deepscan
cDeep = 1 << iota // deepscan (..)

cRoot = 1 << iota // key is referred from root

Expand Down Expand Up @@ -237,7 +237,7 @@ func readRef(path []byte, i int, uptype int) (*tNode, int, error) {
// single key
key, nod.Slice[0], sep, i, flags, err = readKey(path, i)
if len(key) > 0 {
nod.Keys = []word{key}
nod.Keys = append(nod.Keys, key)
}
nod.Type |= flags // cWild, cFullScan
if i == l {
Expand Down Expand Up @@ -289,7 +289,7 @@ func readBrackets(nod *tNode, path []byte, i int) (int, error) {
if i == l {
return i, errPathUnexpectedEnd
}
if nod.Type&cSlice > 0 && nod.Slice[0]+nod.Slice[1]+nod.Slice[2] == cEmpty*3 {
if nod.Type&cSlice > 0 && nod.Slice[0]+nod.Slice[1]+nod.Slice[2] == 2*cEmpty+1 {
nod.Type |= cWild
}
if len(nod.Elems) > 0 {
Expand Down
8 changes: 6 additions & 2 deletions jsonslice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,14 @@ func Test_Expressions(t *testing.T) {
// numbers (+)
{`$.store.book[?(@.price != 22.99)].price`, []byte(`[8.95,12.99,8.99]`)},

// regexp
// regexp: simple
{`$.store.book[?(@.title =~ /the/i)].title`, []byte(`["Sayings of the Century","The Lord of the Rings"]`)},
// regexp
// regexp: complex
{`$.store.book[?(@.title =~ /(Saying)|(Lord)/)].title`, []byte(`["Sayings of the Century","The Lord of the Rings"]`)},
// regexp: not equal
{`$.store.book[?(@.title !=~ /(Saying)|(Lord)/)].title`, []byte(`["Sword of Honour","Moby Dick"]`)},
// regexp: same as above plus syntax variation: != or !=~
{`$.store.book[?(@.title !=~ /saying/i && @.title !~ /Lord/)].title`, []byte(`["Sword of Honour","Moby Dick"]`)},

// array of arrays
{`$.store.bicycle.equipment[1][0]`, []byte(`"peg leg"`)},
Expand Down

0 comments on commit 93c7ef9

Please sign in to comment.