Skip to content

Commit dd307c2

Browse files
authored
Merge pull request #24 from gshilin-sdb/has-many-strings
Support has-many relation on string field
2 parents 54d2556 + 940a712 commit dd307c2

File tree

3 files changed

+109
-5
lines changed

3 files changed

+109
-5
lines changed

v5/go.mod

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module github.com/yaacov/tree-search-language/v5
22

33
require (
4-
cloud.google.com/go v0.37.4 // indirect
54
github.com/Masterminds/squirrel v1.4.0
65
github.com/antlr/antlr4 v0.0.0-20200712162734-eb1adaa8a7a6
76
github.com/fatih/color v1.9.0 // indirect
@@ -12,13 +11,10 @@ require (
1211
github.com/mattn/go-sqlite3 v1.14.0
1312
github.com/onsi/ginkgo v1.14.0
1413
github.com/onsi/gomega v1.10.1
15-
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
1614
github.com/xdg/stringprep v1.0.0 // indirect
1715
go.mongodb.org/mongo-driver v1.3.5
1816
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
1917
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
20-
golang.org/x/text v0.3.3 // indirect
21-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
2218
gopkg.in/yaml.v2 v2.3.0
2319
)
2420

v5/pkg/walkers/semantics/walk.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ func runSemantics(n tsl.Node, eval EvalFunc) (bool, error) {
132132
}
133133

134134
switch l.Func {
135+
case tsl.ArrayOp:
136+
// This is a case of "has-many-table.fld IN (...)"
137+
if r.Func == tsl.ArrayOp {
138+
return handleStringArraysOp(n, eval)
139+
}
135140
case tsl.StringOp:
136141
if r.Func == tsl.StringOp {
137142
return handleStringOp(n, eval)
@@ -209,6 +214,11 @@ func evalIdentNode(node tsl.Node, eval EvalFunc) (tsl.Node, error) {
209214
n := tsl.Node{}
210215

211216
switch v := _v.(type) {
217+
case []string:
218+
n = tsl.Node{
219+
Func: tsl.ArrayOp,
220+
Left: v,
221+
}
212222
case string:
213223
n = tsl.Node{
214224
Func: tsl.StringOp,
@@ -490,6 +500,49 @@ func handleBooleanOp(n tsl.Node, eval EvalFunc) (bool, error) {
490500
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
491501
}
492502

503+
// contains tells whether a contains x performing linear search
504+
func contains(a []string, x string) bool {
505+
for _, n := range a {
506+
if x == n {
507+
return true
508+
}
509+
}
510+
return false
511+
}
512+
513+
func handleStringArraysOp(n tsl.Node, eval EvalFunc) (bool, error) {
514+
l := n.Left.(tsl.Node)
515+
r := n.Right.(tsl.Node)
516+
517+
left := l.Left.([]string)
518+
right := r.Right.([]tsl.Node)
519+
520+
switch n.Func {
521+
case tsl.InOp:
522+
b := false
523+
for _, node := range right {
524+
s, ok := node.Left.(string)
525+
if !ok {
526+
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
527+
}
528+
b = b || contains(left, s)
529+
}
530+
return b, nil
531+
case tsl.NotInOp:
532+
b := true
533+
for _, node := range right {
534+
s, ok := node.Left.(string)
535+
if !ok {
536+
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
537+
}
538+
b = b && !contains(left, s)
539+
}
540+
return b, nil
541+
}
542+
543+
return false, tsl.UnexpectedLiteralError{Literal: n.Func}
544+
}
545+
493546
func handleStringArrayOp(n tsl.Node, eval EvalFunc) (bool, error) {
494547
l := n.Left.(tsl.Node)
495548
r := n.Right.(tsl.Node)

v5/pkg/walkers/semantics/walk_test.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ var _ = Describe("Walk", func() {
7171
Entry("not ilike GOOD", "title not ilike '%GOOD%'", false),
7272
Entry("not ilike BAD", "title not ilike '%BAD%'", true),
7373

74-
// Two identififers
74+
// Two identifiers
7575
Entry("more pages", "spec.pages <= spec.rating", false),
7676
Entry("add pages to number", "spec.pages = (spec.rating + 9)", true),
7777
Entry("multiply pages with number", "spec.pages < (spec.rating * 3)", true),
@@ -91,3 +91,58 @@ var _ = Describe("Walk", func() {
9191
Entry("dates", "date between 2019-12-30 and 2020-01-02", true),
9292
)
9393
})
94+
95+
func evalFactory(record map[string]interface{}) EvalFunc {
96+
return func(name string) (value interface{}, ok bool) {
97+
value, ok = record[name]
98+
return
99+
}
100+
}
101+
102+
var _ = Describe("Walk has-many relationship", func() {
103+
// Subscriptions has many ReservedResource
104+
// ReservedResource has string field ResourceName
105+
text := "subscription.managed = 'true' and subscription.status not in ('Deprovisioned','Deleted') and reserved_resource.resource_name in ('resourceA', 'resourceB')\n"
106+
107+
// Parse the text:
108+
tree, err := tsl.ParseTSL(text)
109+
Expect(err).ToNot(HaveOccurred())
110+
111+
subscriptionWithResourceAB := map[string]interface{}{
112+
"subscription.managed": true,
113+
"subscription.status": "Active",
114+
"reserved_resource.resource_name": []string{
115+
"resourceA",
116+
"resourceB",
117+
},
118+
}
119+
subscriptionWithResourceB := map[string]interface{}{
120+
"subscription.managed": true,
121+
"subscription.status": "Active",
122+
"reserved_resource.resource_name": []string{
123+
"resourceB",
124+
},
125+
}
126+
subscriptionWithoutResourceAB := map[string]interface{}{
127+
"subscription.managed": true,
128+
"subscription.status": "Active",
129+
"reserved_resource.resource_name": []string{
130+
"resourceC",
131+
},
132+
}
133+
134+
eval := evalFactory(subscriptionWithResourceAB)
135+
actual, err := Walk(tree, eval)
136+
Expect(err).ToNot(HaveOccurred())
137+
Expect(actual).To(Equal(true))
138+
139+
eval = evalFactory(subscriptionWithResourceB)
140+
actual, err = Walk(tree, eval)
141+
Expect(err).ToNot(HaveOccurred())
142+
Expect(actual).To(Equal(true))
143+
144+
eval = evalFactory(subscriptionWithoutResourceAB)
145+
actual, err = Walk(tree, eval)
146+
Expect(err).ToNot(HaveOccurred())
147+
Expect(actual).To(Equal(false))
148+
})

0 commit comments

Comments
 (0)