From 93c7ef9d019cabce741922a9f18456e980417094 Mon Sep 17 00:00:00 2001 From: Michael Gurov Date: Sun, 1 Dec 2019 00:50:58 +0300 Subject: [PATCH] "not equal" regexp operator --- README.md | 9 ++++++--- expression.go | 14 ++++++++------ jsonslice.go | 8 ++++---- jsonslice_test.go | 8 ++++++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 11b9140..9b35bfe 100644 --- a/README.md +++ b/README.md @@ -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? @@ -134,6 +133,7 @@ If `step` is negative: `<` | Less than `<=` | Less than or equal to `=~` | Match a regexp
`[?(@.name =~ /sword.*/i]` + `!~` or `!=~` | Don't match a regexp
`[?(@.name !~ /sword.*/i]` `&&` | Logical AND
`[?(@.price < 10 && @isbn)]` `\|\|` | Logical OR
`[?(@.price > 10 \|\| @.category == 'reference')]` @@ -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. @@ -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 diff --git a/expression.go b/expression.go index 4b60b8a..447897a 100644 --- a/expression.go +++ b/expression.go @@ -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 @@ -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 @@ -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 == '|' { @@ -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 } @@ -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 } diff --git a/jsonslice.go b/jsonslice.go index a9771f2..116894c 100644 --- a/jsonslice.go +++ b/jsonslice.go @@ -1,7 +1,7 @@ package jsonslice /** - JsonSlice 1.0.0 + JsonSlice 1.0.1 Michael Gurov, 2018-2019 MIT licenced @@ -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 @@ -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 { @@ -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 { diff --git a/jsonslice_test.go b/jsonslice_test.go index d8ce133..d2605aa 100644 --- a/jsonslice_test.go +++ b/jsonslice_test.go @@ -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"`)},