Skip to content

Commit

Permalink
Merge pull request #24 from gshilin-sdb/has-many-strings
Browse files Browse the repository at this point in the history
Support has-many relation on string field
  • Loading branch information
yaacov authored Jun 30, 2021
2 parents 54d2556 + 940a712 commit dd307c2
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 5 deletions.
4 changes: 0 additions & 4 deletions v5/go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module github.com/yaacov/tree-search-language/v5

require (
cloud.google.com/go v0.37.4 // indirect
github.com/Masterminds/squirrel v1.4.0
github.com/antlr/antlr4 v0.0.0-20200712162734-eb1adaa8a7a6
github.com/fatih/color v1.9.0 // indirect
Expand All @@ -12,13 +11,10 @@ require (
github.com/mattn/go-sqlite3 v1.14.0
github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega v1.10.1
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
github.com/xdg/stringprep v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.3.5
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
golang.org/x/text v0.3.3 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.3.0
)

Expand Down
53 changes: 53 additions & 0 deletions v5/pkg/walkers/semantics/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ func runSemantics(n tsl.Node, eval EvalFunc) (bool, error) {
}

switch l.Func {
case tsl.ArrayOp:
// This is a case of "has-many-table.fld IN (...)"
if r.Func == tsl.ArrayOp {
return handleStringArraysOp(n, eval)
}
case tsl.StringOp:
if r.Func == tsl.StringOp {
return handleStringOp(n, eval)
Expand Down Expand Up @@ -209,6 +214,11 @@ func evalIdentNode(node tsl.Node, eval EvalFunc) (tsl.Node, error) {
n := tsl.Node{}

switch v := _v.(type) {
case []string:
n = tsl.Node{
Func: tsl.ArrayOp,
Left: v,
}
case string:
n = tsl.Node{
Func: tsl.StringOp,
Expand Down Expand Up @@ -490,6 +500,49 @@ func handleBooleanOp(n tsl.Node, eval EvalFunc) (bool, error) {
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
}

// contains tells whether a contains x performing linear search
func contains(a []string, x string) bool {
for _, n := range a {
if x == n {
return true
}
}
return false
}

func handleStringArraysOp(n tsl.Node, eval EvalFunc) (bool, error) {
l := n.Left.(tsl.Node)
r := n.Right.(tsl.Node)

left := l.Left.([]string)
right := r.Right.([]tsl.Node)

switch n.Func {
case tsl.InOp:
b := false
for _, node := range right {
s, ok := node.Left.(string)
if !ok {
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
}
b = b || contains(left, s)
}
return b, nil
case tsl.NotInOp:
b := true
for _, node := range right {
s, ok := node.Left.(string)
if !ok {
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
}
b = b && !contains(left, s)
}
return b, nil
}

return false, tsl.UnexpectedLiteralError{Literal: n.Func}
}

func handleStringArrayOp(n tsl.Node, eval EvalFunc) (bool, error) {
l := n.Left.(tsl.Node)
r := n.Right.(tsl.Node)
Expand Down
57 changes: 56 additions & 1 deletion v5/pkg/walkers/semantics/walk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var _ = Describe("Walk", func() {
Entry("not ilike GOOD", "title not ilike '%GOOD%'", false),
Entry("not ilike BAD", "title not ilike '%BAD%'", true),

// Two identififers
// Two identifiers
Entry("more pages", "spec.pages <= spec.rating", false),
Entry("add pages to number", "spec.pages = (spec.rating + 9)", true),
Entry("multiply pages with number", "spec.pages < (spec.rating * 3)", true),
Expand All @@ -91,3 +91,58 @@ var _ = Describe("Walk", func() {
Entry("dates", "date between 2019-12-30 and 2020-01-02", true),
)
})

func evalFactory(record map[string]interface{}) EvalFunc {
return func(name string) (value interface{}, ok bool) {
value, ok = record[name]
return
}
}

var _ = Describe("Walk has-many relationship", func() {
// Subscriptions has many ReservedResource
// ReservedResource has string field ResourceName
text := "subscription.managed = 'true' and subscription.status not in ('Deprovisioned','Deleted') and reserved_resource.resource_name in ('resourceA', 'resourceB')\n"

// Parse the text:
tree, err := tsl.ParseTSL(text)
Expect(err).ToNot(HaveOccurred())

subscriptionWithResourceAB := map[string]interface{}{
"subscription.managed": true,
"subscription.status": "Active",
"reserved_resource.resource_name": []string{
"resourceA",
"resourceB",
},
}
subscriptionWithResourceB := map[string]interface{}{
"subscription.managed": true,
"subscription.status": "Active",
"reserved_resource.resource_name": []string{
"resourceB",
},
}
subscriptionWithoutResourceAB := map[string]interface{}{
"subscription.managed": true,
"subscription.status": "Active",
"reserved_resource.resource_name": []string{
"resourceC",
},
}

eval := evalFactory(subscriptionWithResourceAB)
actual, err := Walk(tree, eval)
Expect(err).ToNot(HaveOccurred())
Expect(actual).To(Equal(true))

eval = evalFactory(subscriptionWithResourceB)
actual, err = Walk(tree, eval)
Expect(err).ToNot(HaveOccurred())
Expect(actual).To(Equal(true))

eval = evalFactory(subscriptionWithoutResourceAB)
actual, err = Walk(tree, eval)
Expect(err).ToNot(HaveOccurred())
Expect(actual).To(Equal(false))
})

0 comments on commit dd307c2

Please sign in to comment.