Skip to content

Commit

Permalink
Added contains operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
elgs committed Mar 29, 2020
1 parent 00e077a commit 8bb9e9f
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 18 deletions.
20 changes: 6 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,14 @@ This library enables query against JSON. Currently supported operators are: (pre
```
||
&&
= != > < >= <= ~= !~= is isnot
= != > < >= <= ~= !~= is isnot contains
+ -
* / %
^
( )
```

## Changes

The following operators are added: (Credit goes to Ahmad Baitalmal)
* is defined
* isnot defined
* is null
* isnot null

Previously I was hoping to make the query as similar to SQL `WHERE` clause as possible. Later I found a problem parsing the term `PRIORITY>5`. The tokenizer split it as `PRI`, `OR`, `ITY`, `>`, `5`, since `OR` was then an operator, which is terribly wrong. At that point, I thought of two choices:
1. to force the query expression to contain at least one white space between tokens, thus `PRIORITY>5` should be written as `PRIORITY > 5`;
2. to replace operators as follows:

The following are the operators maping to SQL:
* `AND` to `&&`
* `OR` to `||`
* `RLIKE` to `~=`
Expand Down Expand Up @@ -127,6 +116,9 @@ func main() {
// [map[hobby:<nil> skills:[IC Electric design Verification] name:enny gender:f age:36]] <nil>

fmt.Println(parser.Query("hobby isnot null"))
// [map[name:sam gender:m age:1 hobby:dancing skills:[Eating Sleeping Crawling]]] <nil>
// [map[name:sam gender:m age:1 hobby:dancing skills:[Eating Sleeping Crawling]]] <nil>

fmt.Println(parser.Query("skills contains 'Eating'"))
// [map[age:1 gender:m hobby:dancing name:sam skills:[Eating Sleeping Crawling]]] <nil>
}
```
1 change: 1 addition & 0 deletions exparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestTokenize(t *testing.T) {
{"'123 456' 789", []string{"'123 456'", "789"}},
{`123 "456 789"`, []string{"123", "\"456 789\""}},
{`123 "456 '''789"`, []string{"123", "\"456 '''789\""}},
{`2*x+y is null`, []string{"2", "*", "x", "+", "y", "is", "null"}},
}
var fail = []struct {
in string
Expand Down
8 changes: 4 additions & 4 deletions jsonql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ func TestParse(t *testing.T) {

var pass = []struct {
in string
ex int
}{
{"name='elgs'", 1},
{"gender='f'", 1},
{"skills.[1]='Sleeping'", 1},
{"name='elgs'"},
{"gender='f'"},
{"skills.[1]='Sleeping'"},
{"skills contains 'Verification'"},
}
var fail = []struct {
in string
Expand Down
21 changes: 21 additions & 0 deletions sql_operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,27 @@ var sqlOperators = map[string]*Operator{
return "false", nil
},
},
"contains": {
Precedence: 5,
Eval: func(symbolTable interface{}, left string, right string) (string, error) {
l, err := evalToken(symbolTable, left)
if err != nil {
return "false", err
}
r, err := evalToken(symbolTable, right)
if err != nil {
return "false", err
}
if al, ok := l.([]interface{}); ok {
for _, item := range al {
if item == r {
return "true", nil
}
}
}
return "false", nil
},
},
"=": {
Precedence: 5,
Eval: func(symbolTable interface{}, left string, right string) (string, error) {
Expand Down

0 comments on commit 8bb9e9f

Please sign in to comment.