Skip to content

Commit

Permalink
Merge pull request #149 from TomWright/not-equal
Browse files Browse the repository at this point in the history
Add != comparison operator support in dynamic and search selectors
  • Loading branch information
TomWright authored Aug 8, 2021
2 parents 398f408 + 873fa85 commit 650cd32
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 101 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Nothing
### Added

- Support for `!=` comparison operator in dynamic and search selectors.
- Support for `-`/`keyValue` key in dynamic selectors.

## [v1.16.1] - 2021-08-02

Expand Down
14 changes: 12 additions & 2 deletions condition_equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ type EqualCondition struct {
Key string
// Value is the value we are looking for.
Value string
// Not is true if this is a not equal check.
Not bool
}

func (c EqualCondition) check(a interface{}, b interface{}) (bool, error) {
var res = fmt.Sprint(a) == b
if c.Not {
res = !res
}
return res, nil
}

// Check checks to see if other contains the required key value pair.
Expand All @@ -23,7 +33,7 @@ func (c EqualCondition) Check(other reflect.Value) (bool, error) {
value := unwrapValue(other)

if c.Key == "value" || c.Key == "." {
return fmt.Sprint(value.Interface()) == c.Value, nil
return c.check(value.Interface(), c.Value)
}

switch value.Kind() {
Expand All @@ -39,7 +49,7 @@ func (c EqualCondition) Check(other reflect.Value) (bool, error) {
return false, fmt.Errorf("subquery failed: %w", err)
}

return fmt.Sprint(foundNode.InterfaceValue()) == c.Value, nil
return c.check(foundNode.InterfaceValue(), c.Value)
}

return false, &UnhandledCheckType{Value: value.String()}
Expand Down
13 changes: 12 additions & 1 deletion condition_key_equal.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package dasel

import (
"fmt"
"reflect"
)

// KeyEqualCondition lets you check for an exact match.
type KeyEqualCondition struct {
// Value is the value we are looking for.
Value string
// Not is true if this is a not equal check.
Not bool
}

func (c KeyEqualCondition) check(a interface{}, b interface{}) (bool, error) {
var res = fmt.Sprint(a) == b
if c.Not {
res = !res
}
return res, nil
}

// Check checks to see if other contains the required key value pair.
Expand All @@ -18,5 +29,5 @@ func (c KeyEqualCondition) Check(other reflect.Value) (bool, error) {

value := unwrapValue(other)

return c.Value == value.String(), nil
return c.check(c.Value, value.String())
}
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
Expand Down Expand Up @@ -101,7 +100,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand All @@ -121,7 +119,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
Expand Down
17 changes: 13 additions & 4 deletions node_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,21 @@ func findNextAvailableIndex(n *Node, createIfNotExists bool) (reflect.Value, err
}

// processFindDynamicItem is used by findValueDynamic.
func processFindDynamicItem(n *Node, object reflect.Value) (bool, error) {
func processFindDynamicItem(n *Node, object reflect.Value, key string) (bool, error) {
// Loop through each condition.
allConditionsMatched := true
for _, c := range n.Selector.Conditions {
// If the object doesn't match any checks, return a ValueNotFound.
found, err := c.Check(object)

var found bool
var err error
switch cond := c.(type) {
case *KeyEqualCondition:
found, err = cond.Check(reflect.ValueOf(key))
default:
found, err = cond.Check(object)
}

if err != nil {
return false, err
}
Expand Down Expand Up @@ -152,7 +161,7 @@ func findValueDynamic(n *Node, createIfNotExists bool) (reflect.Value, error) {
case reflect.Slice:
for i := 0; i < value.Len(); i++ {
object := value.Index(i)
found, err := processFindDynamicItem(n, object)
found, err := processFindDynamicItem(n, object, fmt.Sprint(i))
if err != nil {
return nilValue(), err
}
Expand All @@ -171,7 +180,7 @@ func findValueDynamic(n *Node, createIfNotExists bool) (reflect.Value, error) {
case reflect.Map:
for _, key := range value.MapKeys() {
object := value.MapIndex(key)
found, err := processFindDynamicItem(n, object)
found, err := processFindDynamicItem(n, object, key.String())
if err != nil {
return nilValue(), err
}
Expand Down
17 changes: 13 additions & 4 deletions node_query_multiple.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,21 @@ func findNextAvailableIndexNodes(selector Selector, previousValue reflect.Value,
}

// processFindDynamicItems is used by findNodesDynamic.
func processFindDynamicItems(selector Selector, object reflect.Value) (bool, error) {
func processFindDynamicItems(selector Selector, object reflect.Value, key string) (bool, error) {
// Loop through each condition.
allConditionsMatched := true
for _, c := range selector.Conditions {
// If the object doesn't match any checks, return a ValueNotFound.
found, err := c.Check(object)

var found bool
var err error
switch cond := c.(type) {
case *KeyEqualCondition:
found, err = cond.Check(reflect.ValueOf(key))
default:
found, err = cond.Check(object)
}

if err != nil {
return false, err
}
Expand Down Expand Up @@ -215,7 +224,7 @@ func findNodesDynamic(selector Selector, previousValue reflect.Value, createIfNo
results := make([]*Node, 0)
for i := 0; i < value.Len(); i++ {
object := value.Index(i)
found, err := processFindDynamicItems(selector, object)
found, err := processFindDynamicItems(selector, object, fmt.Sprint(i))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -246,7 +255,7 @@ func findNodesDynamic(selector Selector, previousValue reflect.Value, createIfNo
results := make([]*Node, 0)
for _, key := range value.MapKeys() {
object := value.MapIndex(key)
found, err := processFindDynamicItems(selector, object)
found, err := processFindDynamicItems(selector, object, key.String())
if err != nil {
return nil, err
}
Expand Down
77 changes: 75 additions & 2 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ func TestParseSelector(t *testing.T) {
},
},
}))
t.Run("SearchNotEqual", testParseSelector(".(?:name!=asd)", dasel.Selector{
Raw: ".(?:name!=asd)",
Current: ".(?:name!=asd)",
Remaining: "",
Type: "SEARCH",
Conditions: []dasel.Condition{
&dasel.EqualCondition{
Key: "name",
Value: "asd",
Not: true,
},
},
}))
t.Run("SearchMoreThan", testParseSelector(".(?:name.[#]>3)", dasel.Selector{
Raw: ".(?:name.[#]>3)",
Current: ".(?:name.[#]>3)",
Expand Down Expand Up @@ -244,6 +257,41 @@ func TestParseSelector(t *testing.T) {
},
},
}))
t.Run("SearchKeyNotEqual", testParseSelector(".(?:-!=asd)", dasel.Selector{
Raw: ".(?:-!=asd)",
Current: ".(?:-!=asd)",
Remaining: "",
Type: "SEARCH",
Conditions: []dasel.Condition{
&dasel.KeyEqualCondition{
Value: "asd",
Not: true,
},
},
}))
t.Run("DynamicKey", testParseSelector(".(-=asd)", dasel.Selector{
Raw: ".(-=asd)",
Current: ".(-=asd)",
Remaining: "",
Type: "DYNAMIC",
Conditions: []dasel.Condition{
&dasel.KeyEqualCondition{
Value: "asd",
},
},
}))
t.Run("DynamicKeyNotEqual", testParseSelector(".(-!=asd)", dasel.Selector{
Raw: ".(-!=asd)",
Current: ".(-!=asd)",
Remaining: "",
Type: "DYNAMIC",
Conditions: []dasel.Condition{
&dasel.KeyEqualCondition{
Value: "asd",
Not: true,
},
},
}))
t.Run("DynamicEqual", testParseSelector(".(name=asd)", dasel.Selector{
Raw: ".(name=asd)",
Current: ".(name=asd)",
Expand All @@ -256,6 +304,19 @@ func TestParseSelector(t *testing.T) {
},
},
}))
t.Run("DynamicNotEqual", testParseSelector(".(name!=asd)", dasel.Selector{
Raw: ".(name!=asd)",
Current: ".(name!=asd)",
Remaining: "",
Type: "DYNAMIC",
Conditions: []dasel.Condition{
&dasel.EqualCondition{
Key: "name",
Value: "asd",
Not: true,
},
},
}))
t.Run("DynamicMoreThan", testParseSelector(".(name.[#]>3)", dasel.Selector{
Raw: ".(name.[#]>3)",
Current: ".(name.[#]>3)",
Expand Down Expand Up @@ -380,10 +441,22 @@ func TestNode_QueryMultiple(t *testing.T) {
t.Run("SingleResult", testNodeQueryMultipleArray(".[0].name", []interface{}{
"Tom",
}))
t.Run("SingleResultDynamic", testNodeQueryMultipleArray(".(age=25).name", []interface{}{
t.Run("SingleResultDynamicEqual", testNodeQueryMultipleArray(".(age=25).name", []interface{}{
"Amelia",
}))
t.Run("SingleResultDynamic", testNodeQueryMultipleArray(".(age=27).name", []interface{}{
t.Run("SingleResultDynamicNotEqual", testNodeQueryMultipleArray(".(age!=27).name", []interface{}{
"Amelia",
}))
t.Run("SingleResultDynamicKeyEqual", testNodeQueryMultipleArray(".(-=0).name", []interface{}{
"Tom",
}))
t.Run("MultipleResultDynamicKeyNotEqual", testNodeQueryMultipleArray(".(-!=0).name", []interface{}{
"Jim", "Amelia",
}))
t.Run("MultipleResultSearchKeyNotEqual", testNodeQueryMultipleArray(".[*].(?:-!=name)", []interface{}{
"27", "27", "25",
}))
t.Run("MultipleResultDynamic", testNodeQueryMultipleArray(".(age=27).name", []interface{}{
"Tom",
"Jim",
}))
Expand Down
Loading

0 comments on commit 650cd32

Please sign in to comment.