diff --git a/deps.json b/deps.json index 0d3df27da..4eafb126c 100644 --- a/deps.json +++ b/deps.json @@ -14,11 +14,6 @@ "version": "master", "type": "git" }, - "goconvey": { - "repo": "github.com/smartystreets/goconvey", - "version": "master", - "type": "git" - }, "gocql": { "repo": "github.com/gocql/gocql", "version": "f8fb76bb772442ea938e4be46ab1666e542411a5", diff --git a/index/storage_test.go b/index/storage_test.go index d6f3c83b4..bc9f0775d 100644 --- a/index/storage_test.go +++ b/index/storage_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - . "github.com/smartystreets/goconvey/convey" ) func TestStorage(t *testing.T) { diff --git a/query/lexer_test.go b/query/lexer_test.go index bf845eedf..4e5f0578a 100644 --- a/query/lexer_test.go +++ b/query/lexer_test.go @@ -1,213 +1,279 @@ -package query +package query_test import ( + "reflect" "testing" - . "github.com/smartystreets/goconvey/convey" + "github.com/davecgh/go-spew/spew" + "github.com/umbel/pilosa/query" ) -func TestLexer(t *testing.T) { - Convey("Basic lexical analysis", t, func() { - var tokens []Token - var err error - - tokens, err = Lex("get(10)") - So(err, ShouldBeNil) - So(len(tokens), ShouldEqual, 4) - So(tokens, ShouldResemble, []Token{ - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"10", TYPE_VALUE}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("get(id=10)") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"id", TYPE_KEYWORD}, - {"=", TYPE_EQUALS}, - {"10", TYPE_VALUE}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("get(id=10, frame=brand)") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"id", TYPE_KEYWORD}, - {"=", TYPE_EQUALS}, - {"10", TYPE_VALUE}, - {",", TYPE_COMMA}, - {"frame", TYPE_KEYWORD}, - {"=", TYPE_EQUALS}, - {"brand", TYPE_VALUE}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("union(get(10))") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"union", TYPE_FUNC}, - {"(", TYPE_LP}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"10", TYPE_VALUE}, - {")", TYPE_RP}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("intersect(get(10), get(11), get(12))") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"intersect", TYPE_FUNC}, - {"(", TYPE_LP}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"10", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"11", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"12", TYPE_VALUE}, - {")", TYPE_RP}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("intersect(get(10), get(11), concat(get(12),get(14)))") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"intersect", TYPE_FUNC}, - {"(", TYPE_LP}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"10", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"11", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"concat", TYPE_FUNC}, - {"(", TYPE_LP}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"12", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"14", TYPE_VALUE}, - {")", TYPE_RP}, - {")", TYPE_RP}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("concat(get(1, brand),get(2))") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"concat", TYPE_FUNC}, - {"(", TYPE_LP}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"1", TYPE_VALUE}, - {",", TYPE_COMMA}, - {"brand", TYPE_VALUE}, - {")", TYPE_RP}, - {",", TYPE_COMMA}, - {"get", TYPE_FUNC}, - {"(", TYPE_LP}, - {"2", TYPE_VALUE}, - {")", TYPE_RP}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("set(1, 987)") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"set", TYPE_FUNC}, - {"(", TYPE_LP}, - {"1", TYPE_VALUE}, - {",", TYPE_COMMA}, - {"987", TYPE_VALUE}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("set(1, general, 987)") - So(err, ShouldBeNil) - So(tokens, ShouldResemble, []Token{ - {"set", TYPE_FUNC}, - {"(", TYPE_LP}, - {"1", TYPE_VALUE}, - {",", TYPE_COMMA}, - {"general", TYPE_VALUE}, - {",", TYPE_COMMA}, - {"987", TYPE_VALUE}, - {")", TYPE_RP}, - }) - - tokens, err = Lex("top-n(get(10), 8)") - So(tokens, ShouldResemble, []Token{ - Token{"top-n", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"get", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"10", TYPE_VALUE}, - Token{")", TYPE_RP}, - Token{",", TYPE_COMMA}, - Token{"8", TYPE_VALUE}, - Token{")", TYPE_RP}, - }) - - tokens, err = Lex("top-n(get(10, general), [1,2,3])") - So(tokens, ShouldResemble, []Token{ - Token{"top-n", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"get", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"10", TYPE_VALUE}, - Token{",", TYPE_COMMA}, - Token{"general", TYPE_VALUE}, - Token{")", TYPE_RP}, - Token{",", TYPE_COMMA}, - Token{"[", TYPE_LB}, - Token{"1", TYPE_VALUE}, - Token{",", TYPE_COMMA}, - Token{"2", TYPE_VALUE}, - Token{",", TYPE_COMMA}, - Token{"3", TYPE_VALUE}, - Token{"]", TYPE_RB}, - Token{")", TYPE_RP}, - }) - - tokens, err = Lex("plugin(get(10, general), [get(11, general)])") - So(tokens, ShouldResemble, []Token{ - Token{"plugin", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"get", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"10", TYPE_VALUE}, - Token{",", TYPE_COMMA}, - Token{"general", TYPE_VALUE}, - Token{")", TYPE_RP}, - Token{",", TYPE_COMMA}, - Token{"[", TYPE_LB}, - Token{"get", TYPE_FUNC}, - Token{"(", TYPE_LP}, - Token{"11", TYPE_VALUE}, - Token{",", TYPE_COMMA}, - Token{"general", TYPE_VALUE}, - Token{")", TYPE_RP}, - Token{"]", TYPE_RB}, - Token{")", TYPE_RP}, - }) - }) +// Ensure a simple function and value can be lexed. +func TestLexer_Lex_FuncValue(t *testing.T) { + if tokens, err := query.Lex("get(10)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure a simple function, keyword, & value can be lexed. +func TestLexer_Lex_FuncKeywordValue(t *testing.T) { + if tokens, err := query.Lex("get(id=10)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"id", query.TYPE_KEYWORD}, + {"=", query.TYPE_EQUALS}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure a more complex function can be lexed. +func TestLexer_Lex_FuncComplex(t *testing.T) { + if tokens, err := query.Lex("get(id=10, frame=brand)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"id", query.TYPE_KEYWORD}, + {"=", query.TYPE_EQUALS}, + {"10", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"frame", query.TYPE_KEYWORD}, + {"=", query.TYPE_EQUALS}, + {"brand", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that nested functions can be lexed. +func TestLexer_Lex_FuncNested(t *testing.T) { + if tokens, err := query.Lex("union(get(10))"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"union", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a list of nested functions can be lexed. +func TestLexer_Lex_FuncNestedList(t *testing.T) { + if tokens, err := query.Lex("intersect(get(10), get(11), get(12))"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"intersect", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"11", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"12", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that complex nested functions can be lexed. +func TestLexer_Lex_FuncNestedComplex(t *testing.T) { + if tokens, err := query.Lex("intersect(get(10), get(11), concat(get(12),get(14)))"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"intersect", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"11", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"concat", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"12", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"14", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {")", query.TYPE_RP}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that complex nested functions can be lexed. +func TestLexer_Lex_FuncNestedComplex2(t *testing.T) { + if tokens, err := query.Lex("concat(get(1, brand),get(2))"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"concat", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"1", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"brand", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"2", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a set function can be lexed. +func TestLexer_Lex_Set1(t *testing.T) { + if tokens, err := query.Lex("set(1, 987)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"set", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"1", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"987", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a set function can be lexed. +func TestLexer_Lex_Set2(t *testing.T) { + if tokens, err := query.Lex("set(1, general, 987)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"set", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"1", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"general", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"987", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a TopN function can be lexed. +func TestLexer_Lex_TopN1(t *testing.T) { + if tokens, err := query.Lex("top-n(get(10), 8)"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"top-n", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"8", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a TopN function can be lexed. +func TestLexer_Lex_TopN2(t *testing.T) { + if tokens, err := query.Lex("top-n(get(10, general), [1,2,3])"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"top-n", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"general", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"[", query.TYPE_LB}, + {"1", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"2", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"3", query.TYPE_VALUE}, + {"]", query.TYPE_RB}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// Ensure that a plugin function can be lexed. +func TestLexer_Lex_Plugin(t *testing.T) { + if tokens, err := query.Lex("plugin(get(10, general), [get(11, general)])"); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(tokens, []query.Token{ + {"plugin", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"10", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"general", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {",", query.TYPE_COMMA}, + {"[", query.TYPE_LB}, + {"get", query.TYPE_FUNC}, + {"(", query.TYPE_LP}, + {"11", query.TYPE_VALUE}, + {",", query.TYPE_COMMA}, + {"general", query.TYPE_VALUE}, + {")", query.TYPE_RP}, + {"]", query.TYPE_RB}, + {")", query.TYPE_RP}, + }) { + t.Fatalf("unexpected tokens:\n\n%s", spew.Sprint(tokens)) + } +} + +// MustLex lexes s and returns a set of tokens. Panic on error. +func MustLex(s string) []query.Token { + a, err := query.Lex(s) + if err != nil { + panic(err) + } + return a } diff --git a/query/parser_test.go b/query/parser_test.go index 1311c4d38..6c3a4976e 100644 --- a/query/parser_test.go +++ b/query/parser_test.go @@ -1,127 +1,157 @@ -package query +package query_test import ( + "reflect" "testing" "github.com/davecgh/go-spew/spew" - . "github.com/smartystreets/goconvey/convey" + "github.com/umbel/pilosa/query" ) -func TestQueryParser(t *testing.T) { - Convey("Basic parse - get()", t, func() { - tokens, err := Lex("get(10)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "get") - So(query.Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general"}) - }) - Convey("Basic parse - clear()", t, func() { - tokens, err := Lex("clear(10, general, 0, 20)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - spew.Dump(query) - So(query.Operation, ShouldEqual, "clear") - So(query.Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general", "filter": uint64(0), "profile_id": uint64(20)}) - }) - Convey("Basic parse - set()", t, func() { - tokens, err := Lex("set(10, general, 0, 20)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "set") - So(query.Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general", "filter": uint64(0), "profile_id": uint64(20)}) - }) - Convey("Basic nested query parse", t, func() { - tokens, err := Lex("union(get(10,general), get(11,brand), get(12))") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "union") - So(len(query.Subqueries), ShouldEqual, 3) - - So(query.Subqueries[0].Operation, ShouldEqual, "get") - So(query.Subqueries[0].Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general"}) - So(query.Subqueries[1].Operation, ShouldEqual, "get") - So(query.Subqueries[1].Args, ShouldResemble, map[string]interface{}{"id": uint64(11), "frame": "brand"}) - So(query.Subqueries[2].Operation, ShouldEqual, "get") - So(query.Subqueries[2].Args, ShouldResemble, map[string]interface{}{"id": uint64(12), "frame": "general"}) - }) - Convey("Keyword args", t, func() { - tokens, err := Lex("get(id=10)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "get") - So(query.Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general"}) - }) - Convey("Keyword args - multiple", t, func() { - tokens, err := Lex("get(id=10, frame=brands)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "get") - So(query.Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "brands"}) - }) - Convey("Lists", t, func() { - tokens, err := Lex("top-n(get(10, general), [1,2,3], 50)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "top-n") - So(query.Args, ShouldResemble, map[string]interface{}{"ids": []uint64{1, 2, 3}, "n": 50}) - - So(len(query.Subqueries), ShouldEqual, 1) - So(query.Subqueries[0].Operation, ShouldEqual, "get") - So(query.Subqueries[0].Args, ShouldResemble, map[string]interface{}{"id": uint64(10), "frame": "general"}) - }) - Convey("Bracketed Lists", t, func() { - tokens, err := Lex("plugin(get(99), [get(10), get(11)])") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - spew.Dump("*********************************************") - spew.Dump(query) - spew.Dump("*********************************************") - }) - Convey("Lists", t, func() { - tokens, err := Lex("wat(50)") - _, err = Parse(tokens) - So(err, ShouldBeNil) - }) - Convey("Recall", t, func() { - tokens, err := Lex("recall(12345,1,12345,2,12345,3)") - q, err := Parse(tokens) - spew.Dump(q) - So(err, ShouldBeNil) - }) - Convey("all() query parse", t, func() { - tokens, err := Lex("top-n(all(), general, 30)") - So(err, ShouldBeNil) - - query, err := Parse(tokens) - So(err, ShouldBeNil) - - So(query.Operation, ShouldEqual, "top-n") - So(len(query.Subqueries), ShouldEqual, 1) - - So(query.Subqueries[0].Operation, ShouldEqual, "all") - So(query.Subqueries[0].Args, ShouldResemble, map[string]interface{}{}) - }) +// Ensure the parser can parse a get() query. +func TestParser_Parse_Get(t *testing.T) { + if q, err := query.Parse(MustLex("get(10)")); err != nil { + t.Fatal(err) + } else if q.Operation != "get" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{ + "id": uint64(10), + "frame": "general", + }) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse a get() query with a keyword. +func TestParser_Parse_Get_Keyword(t *testing.T) { + if q, err := query.Parse(MustLex("get(id=10)")); err != nil { + t.Fatal(err) + } else if q.Operation != "get" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{"id": uint64(10), "frame": "general"}) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse a get() query with a keyword. +func TestParser_Parse_Get_MultiKeyword(t *testing.T) { + if q, err := query.Parse(MustLex("get(id=10, frame=brands)")); err != nil { + t.Fatal(err) + } else if q.Operation != "get" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{"id": uint64(10), "frame": "brands"}) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse a clear() query. +func TestParser_Parse_Clear(t *testing.T) { + if q, err := query.Parse(MustLex("clear(10, general, 0, 20)")); err != nil { + t.Fatal(err) + } else if q.Operation != "clear" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{ + "id": uint64(10), + "frame": "general", + "filter": uint64(0), + "profile_id": uint64(20), + }) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse a set() query. +func TestParser_Parse_Set(t *testing.T) { + if q, err := query.Parse(MustLex("set(10, general, 0, 20)")); err != nil { + t.Fatal(err) + } else if q.Operation != "set" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{ + "id": uint64(10), + "frame": "general", + "filter": uint64(0), + "profile_id": uint64(20), + }) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse a nested query. +func TestParser_Parse_Nested(t *testing.T) { + q, err := query.Parse(MustLex("union(get(10,general), get(11,brand), get(12))")) + if err != nil { + t.Fatal(err) + } else if q.Operation != "union" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if len(q.Subqueries) != 3 { + t.Fatalf("unexpected subquery count: %d", len(q.Subqueries)) + } + + if sq := q.Subqueries[0]; sq.Operation != "get" { + t.Fatalf("unexpected subquery(0) operation: %q", sq.Operation) + } else if !reflect.DeepEqual(sq.Args, map[string]interface{}{"id": uint64(10), "frame": "general"}) { + t.Fatalf("unexpected subquery(0) args:\n\n%s", spew.Sprint(q.Args)) + } + + if sq := q.Subqueries[1]; sq.Operation != "get" { + t.Fatalf("unexpected subquery(1) operation: %q", sq.Operation) + } else if !reflect.DeepEqual(sq.Args, map[string]interface{}{"id": uint64(11), "frame": "brand"}) { + t.Fatalf("unexpected subquery(1) args:\n\n%s", spew.Sprint(sq.Args)) + } + + if sq := q.Subqueries[2]; sq.Operation != "get" { + t.Fatalf("unexpected subquery(2) operation: %q", sq.Operation) + } else if !reflect.DeepEqual(sq.Args, map[string]interface{}{"id": uint64(12), "frame": "general"}) { + t.Fatalf("unexpected subquery(2) args:\n\n%s", spew.Sprint(sq.Args)) + } +} + +// Ensure the parser can parse a query with lists. +func TestParser_Parse_Lists(t *testing.T) { + q, err := query.Parse(MustLex("top-n(get(10, general), [1,2,3], 50)")) + if err != nil { + t.Fatal(err) + } else if q.Operation != "top-n" { + t.Fatalf("unexpected operation: %q", q.Operation) + } else if !reflect.DeepEqual(q.Args, map[string]interface{}{"ids": []uint64{1, 2, 3}, "n": 50}) { + t.Fatalf("unexpected args:\n\n%s", spew.Sprint(q.Args)) + } + + if sq := q.Subqueries[0]; sq.Operation != "get" { + t.Fatalf("unexpected subquery(0) operation: %q", sq.Operation) + } else if !reflect.DeepEqual(sq.Args, map[string]interface{}{"id": uint64(10), "frame": "general"}) { + t.Fatalf("unexpected subquery(0) args:\n\n%s", spew.Sprint(q.Args)) + } +} + +// Ensure the parser can parse "all()". +func TestParser_Parse_All(t *testing.T) { + q, err := query.Parse(MustLex("top-n(all(), general, 30)")) + if err != nil { + t.Fatal(err) + } else if q.Operation != "top-n" { + t.Fatalf("unexpected operation: %q", q.Operation) + } + + if sq := q.Subqueries[0]; sq.Operation != "all" { + t.Fatalf("unexpected subquery(0) operation: %q", sq.Operation) + } +} + +// Ensure the parser can parse bracketed lists. +func TestParser_Parse_Lists_Bracketed(t *testing.T) { + q, err := query.Parse(MustLex("plugin(get(99), [get(10), get(11)])")) + if err != nil { + t.Fatalf("expected error") + } + spew.Dump(q) +} + +// Ensure the parser can parse a recall query. +func TestParser_Parse_Recall(t *testing.T) { + q, err := query.Parse(MustLex("recall(12345,1,12345,2,12345,3)")) + if err != nil { + t.Fatal(err) + } + spew.Dump(q) } diff --git a/query/planner_test.go b/query/planner_test.go index 6c2695653..5f0a04f2b 100644 --- a/query/planner_test.go +++ b/query/planner_test.go @@ -1,253 +1,283 @@ -package query +package query_test import ( - "log" + "reflect" "testing" "github.com/davecgh/go-spew/spew" - . "github.com/smartystreets/goconvey/convey" "github.com/umbel/pilosa/db" + "github.com/umbel/pilosa/query" "github.com/umbel/pilosa/util" ) -func basic_database() (*db.Database, *db.Fragment) { - // create an empty database - cluster := db.NewCluster() - database := cluster.GetOrCreateDatabase("main") - frame := database.GetOrCreateFrame("default") +// Ensure the query planner can plan a "get" query. +func TestQueryPlanner_Plan_Get(t *testing.T) { + q, err := query.QueryForPQL("get(10,default)") + if err != nil { + t.Fatal(err) + } - slice1 := database.GetOrCreateSlice(0) - fragment_id1 := util.Id() - fragment1 := database.GetOrCreateFragment(frame, slice1, fragment_id1) - process_id1 := util.RandomUUID() - process1 := db.NewProcess(&process_id1) - process1.SetHost("----192.1.1.0----") - fragment1.SetProcess(process1) + d, frag1 := NewDefaultDB() + planner := query.QueryPlanner{Database: d, Query: q} - slice2 := database.GetOrCreateSlice(1) - fragment_id2 := util.Id() - fragment2 := database.GetOrCreateFragment(frame, slice2, fragment_id2) - process_id2 := util.RandomUUID() - process2 := db.NewProcess(&process_id2) - process2.SetHost("----192.1.1.1----") - fragment2.SetProcess(process2) - return database, fragment1 + id := util.RandomUUID() + plan, err := planner.Plan(q, &id, frag1.GetLocation()) + if err != nil { + t.Fatal(err) + } + + if n := len(*plan); n != 3 { + t.Fatalf("unexpected plan length: %d", n) + } + + if step := (*plan)[0].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(0) operation: %s", step.Operation) + } else if step.Slice != 0 { + t.Fatalf("unexpected step(0) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(0) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + if step := (*plan)[1].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(1) operation: %s", step.Operation) + } else if step.Slice != 1 { + t.Fatalf("unexpected step(1) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(1) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + if step := (*plan)[2].(query.CatQueryStep); step.Operation != "cat" { + t.Fatalf("unexpected step(2) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Inputs, []*util.GUID{ + (*plan)[0].(query.GetQueryStep).Id, + (*plan)[1].(query.GetQueryStep).Id, + }) { + t.Fatalf("unexpected step(2) inputs: %s", spew.Sprint(step.Inputs)) + } } -func TestQueryPlanner(t *testing.T) { - Convey("Union query plan", t, func() { +// Ensure the query planner can plan a "set" query. +func TestQueryPlanner_Plan_Set(t *testing.T) { + q, err := query.QueryForPQL("set(10, default, 0, 100)") + if err != nil { + t.Fatal(err) + } - id1 := util.RandomUUID() - query1 := Query{Id: &id1, Operation: "get", Args: map[string]interface{}{"id": uint64(10), "frame": "default"}} + d, frag1 := NewDefaultDB() + planner := query.QueryPlanner{Database: d, Query: q} - id2 := util.RandomUUID() - query2 := Query{Id: &id2, Operation: "get", Args: map[string]interface{}{"id": uint64(20), "frame": "default"}} + id := util.RandomUUID() + plan, err := planner.Plan(q, &id, frag1.GetLocation()) + if err != nil { + t.Fatal(err) + } - id3 := util.RandomUUID() - query := Query{Id: &id3, Operation: "union", Subqueries: []Query{query1, query2}} - - database, fragment1 := basic_database() - - qplanner := QueryPlanner{Database: database, Query: &query} - destination := fragment1.GetLocation() - - id := util.RandomUUID() - log.Println(id) - qpp, err := qplanner.Plan(&query, &id, destination) - So(err, ShouldEqual, nil) - - qp := *qpp - - So(len(qp), ShouldEqual, 7) - So(qp[0].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[0].(GetQueryStep).Slice, ShouldEqual, 0) - So(*(qp[0].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[1].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[1].(GetQueryStep).Slice, ShouldEqual, 0) - So(*(qp[1].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{20, "default", 0}) - So(qp[2].(UnionQueryStep).Operation, ShouldEqual, "union") - So(qp[2].(UnionQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[0].(GetQueryStep).Id, - qp[1].(GetQueryStep).Id, - }) - So(qp[3].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[3].(GetQueryStep).Slice, ShouldEqual, 1) - So(*(qp[3].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[4].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[4].(GetQueryStep).Slice, ShouldEqual, 1) - So(*(qp[4].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{20, "default", 0}) - So(qp[5].(UnionQueryStep).Operation, ShouldEqual, "union") - So(qp[5].(UnionQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[3].(GetQueryStep).Id, - qp[4].(GetQueryStep).Id, - }) - So(qp[6].(CatQueryStep).Operation, ShouldEqual, "cat") - So(qp[6].(CatQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[2].(UnionQueryStep).Id, - qp[5].(UnionQueryStep).Id, - }) - }) - Convey("Get query plan - including parsing", t, func() { - - query, err := QueryForPQL("get(10,default)") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - - qplanner := QueryPlanner{Database: database, Query: query} - destination := fragment1.GetLocation() - - id := util.RandomUUID() - qpp, err := qplanner.Plan(query, &id, destination) - So(err, ShouldEqual, nil) - qp := *qpp - - So(len(qp), ShouldEqual, 3) - So(qp[0].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[0].(GetQueryStep).Slice, ShouldEqual, 0) - So(*(qp[0].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[1].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[1].(GetQueryStep).Slice, ShouldEqual, 1) - So(*(qp[1].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[2].(CatQueryStep).Operation, ShouldEqual, "cat") - So(qp[2].(CatQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[0].(GetQueryStep).Id, - qp[1].(GetQueryStep).Id, - }) - }) - Convey("Union query plan - including parsing", t, func() { - - query, err := QueryForPQL("union(get(10, default), get(20, default))") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - - qplanner := QueryPlanner{Database: database, Query: query} - destination := fragment1.GetLocation() - - id := util.RandomUUID() - qpp, err := qplanner.Plan(query, &id, destination) - So(err, ShouldEqual, nil) - qp := *qpp - - So(len(qp), ShouldEqual, 7) - So(qp[0].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[0].(GetQueryStep).Slice, ShouldEqual, 0) - So(*(qp[0].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[1].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[1].(GetQueryStep).Slice, ShouldEqual, 0) - So(*(qp[1].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{20, "default", 0}) - So(qp[2].(UnionQueryStep).Operation, ShouldEqual, "union") - So(qp[2].(UnionQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[0].(GetQueryStep).Id, - qp[1].(GetQueryStep).Id, - }) - So(qp[3].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[3].(GetQueryStep).Slice, ShouldEqual, 1) - So(*(qp[3].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[4].(GetQueryStep).Operation, ShouldEqual, "get") - So(qp[4].(GetQueryStep).Slice, ShouldEqual, 1) - So(*(qp[4].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{20, "default", 0}) - So(qp[5].(UnionQueryStep).Operation, ShouldEqual, "union") - So(qp[5].(UnionQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[3].(GetQueryStep).Id, - qp[4].(GetQueryStep).Id, - }) - So(qp[6].(CatQueryStep).Operation, ShouldEqual, "cat") - So(qp[6].(CatQueryStep).Inputs, ShouldResemble, []*util.GUID{ - qp[2].(UnionQueryStep).Id, - qp[5].(UnionQueryStep).Id, - }) - }) - Convey("Set query plan - including parsing", t, func() { - query, err := QueryForPQL("set(10, default, 0, 100)") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - - qplanner := QueryPlanner{Database: database, Query: query} - destination := fragment1.GetLocation() - - id := util.RandomUUID() - qpp, err := qplanner.Plan(query, &id, destination) - So(err, ShouldEqual, nil) - qp := *qpp - So(len(qp), ShouldEqual, 1) - So(qp[0].(SetQueryStep).Operation, ShouldEqual, "set") - So(qp[0].(SetQueryStep).ProfileId, ShouldEqual, 100) - So(*(qp[0].(SetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - }) - Convey("Top-n query plan - including parsing", t, func() { - query, err := QueryForPQL("top-n(get(10, default), default, 50,[1,2,3])") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - qplanner := QueryPlanner{Database: database, Query: query} - destination := fragment1.GetLocation() - id := util.RandomUUID() - qpp, err := qplanner.Plan(query, &id, destination) - - So(err, ShouldEqual, nil) - qp := *qpp - So(err, ShouldEqual, nil) - So(len(qp), ShouldEqual, 5) - So(qp[0].(GetQueryStep).Operation, ShouldEqual, "get") - So(*(qp[0].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[1].(*TopNQueryStep).Operation, ShouldEqual, "top-n") - So(qp[1].(*TopNQueryStep).Input, ShouldEqual, qp[0].(GetQueryStep).Id) - So(qp[1].(*TopNQueryStep).N, ShouldEqual, 50) - So(qp[1].(*TopNQueryStep).Filters, ShouldResemble, []uint64{1, 2, 3}) - So(qp[2].(GetQueryStep).Operation, ShouldEqual, "get") - So(*(qp[2].(GetQueryStep).Bitmap), ShouldResemble, db.Bitmap{10, "default", 0}) - So(qp[3].(*TopNQueryStep).Operation, ShouldEqual, "top-n") - So(qp[3].(*TopNQueryStep).Input, ShouldEqual, qp[2].(GetQueryStep).Id) - So(qp[3].(*TopNQueryStep).N, ShouldEqual, 50) - }) - Convey("All query plan - including parsing", t, func() { - query, err := QueryForPQL("top-n(all(), default, 50, [1,2,3])") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - qplanner := QueryPlanner{Database: database, Query: query} - destination := fragment1.GetLocation() - id := util.RandomUUID() - qpp, err := qplanner.Plan(query, &id, destination) - - So(err, ShouldEqual, nil) - qp := *qpp - So(err, ShouldEqual, nil) - So(len(qp), ShouldEqual, 3) - So(qp[0].(*TopNQueryStep).Operation, ShouldEqual, "top-n") - So(qp[0].(*TopNQueryStep).Input, ShouldEqual, nil) - So(qp[0].(*TopNQueryStep).N, ShouldEqual, 50) - So(qp[0].(*TopNQueryStep).Filters, ShouldResemble, []uint64{1, 2, 3}) - }) - Convey("Get query plan - including parsing", t, func() { - - _, err := QueryForPQL("count()") - So(err, ShouldNotEqual, nil) - _, err = QueryForPQL("count(intersect())") - So(err, ShouldNotEqual, nil) - _, err = QueryForPQL("count(get(10, default))") - So(err, ShouldEqual, nil) - _, err = QueryForPQL("count(union())") - So(err, ShouldNotEqual, nil) - }) - - Convey("Stash including parsing", t, func() { - qp, err := QueryForPQL("stash(union(get(10,default),get(20,default)))") - So(err, ShouldEqual, nil) - - database, fragment1 := basic_database() - qplanner := QueryPlanner{Database: database, Query: qp} - destination := fragment1.GetLocation() - id := util.RandomUUID() - qpp, _ := qplanner.Plan(qp, &id, destination) - p := *qpp - - So(len(p), ShouldNotEqual, 0) - log.Println(len(p)) - spew.Dump(p[0]) - }) + if n := len(*plan); n != 1 { + t.Fatalf("unexpected plan length: %d", n) + } + if step := (*plan)[0].(query.SetQueryStep); step.Operation != "set" { + t.Fatalf("unexpected step(0) operation: %s", step.Operation) + } else if step.ProfileId != 100 { + t.Fatalf("unexpected step(0) profile id: %d", step.ProfileId) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(0) bitmap: %s", spew.Sprint(step.Bitmap)) + } +} + +// Ensure the query planner can plan a "top-n" query. +func TestQueryPlanner_Plan_TopN(t *testing.T) { + q, err := query.QueryForPQL("top-n(get(10, default), default, 50,[1,2,3])") + if err != nil { + t.Fatal(err) + } + + d, frag1 := NewDefaultDB() + planner := query.QueryPlanner{Database: d, Query: q} + + id := util.RandomUUID() + plan, err := planner.Plan(q, &id, frag1.GetLocation()) + if err != nil { + t.Fatal(err) + } + + if n := len(*plan); n != 5 { + t.Fatalf("unexpected plan length: %d", n) + } + + if step := (*plan)[0].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(0) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(0) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + if step := (*plan)[1].(*query.TopNQueryStep); step.Operation != "top-n" { + t.Fatalf("unexpected step(1) operation: %s", step.Operation) + } else if step.Input != (*plan)[0].(query.GetQueryStep).Id { + t.Fatalf("unexpected step(1) input: %d", step.Input) + } else if step.N != 50 { + t.Fatalf("unexpected step(1) n: %d", step.N) + } else if !reflect.DeepEqual(step.Filters, []uint64{1, 2, 3}) { + t.Fatalf("unexpected step(1) filters: %s", spew.Sprint(step.Filters)) + } + + if step := (*plan)[2].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(2) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(2) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + if step := (*plan)[3].(*query.TopNQueryStep); step.Operation != "top-n" { + t.Fatalf("unexpected step(3) operation: %s", step.Operation) + } else if step.Input != (*plan)[2].(query.GetQueryStep).Id { + t.Fatalf("unexpected step(3) input: %d", step.Input) + } else if step.N != 50 { + t.Fatalf("unexpected step(3) n: %d", step.N) + } +} + +// Ensure the query planner can plan a "top-n" all() query. +func TestQueryPlanner_Plan_TopN_All(t *testing.T) { + q, err := query.QueryForPQL("top-n(all(), default, 50, [1,2,3])") + if err != nil { + t.Fatal(err) + } + + d, frag1 := NewDefaultDB() + planner := query.QueryPlanner{Database: d, Query: q} + + id := util.RandomUUID() + plan, err := planner.Plan(q, &id, frag1.GetLocation()) + if err != nil { + t.Fatal(err) + } + + if n := len(*plan); n != 3 { + t.Fatalf("unexpected plan length: %d", n) + } + + if step := (*plan)[0].(*query.TopNQueryStep); step.Operation != "top-n" { + t.Fatalf("unexpected step(0) operation: %s", step.Operation) + } else if step.Input != nil { + t.Fatalf("unexpected step(0) input: %v", step.Input) + } else if step.N != 50 { + t.Fatalf("unexpected step(0) n: %d", step.N) + } else if !reflect.DeepEqual(step.Filters, []uint64{1, 2, 3}) { + t.Fatalf("unexpected step(0) filters: %s", spew.Sprint(step.Filters)) + } +} + +// Ensure the query planner can plan a "union" query. +func TestQueryPlanner_Plan_Union(t *testing.T) { + id1 := util.RandomUUID() + q1 := query.Query{Id: &id1, Operation: "get", Args: map[string]interface{}{"id": uint64(10), "frame": "default"}} + + id2 := util.RandomUUID() + q2 := query.Query{Id: &id2, Operation: "get", Args: map[string]interface{}{"id": uint64(20), "frame": "default"}} + + id3 := util.RandomUUID() + q := query.Query{Id: &id3, Operation: "union", Subqueries: []query.Query{q1, q2}} + + d, frag1 := NewDefaultDB() + planner := query.QueryPlanner{Database: d, Query: &q} + + id := util.RandomUUID() + plan, err := planner.Plan(&q, &id, frag1.GetLocation()) + if err != nil { + t.Fatal(err) + } + + if n := len(*plan); n != 7 { + t.Fatalf("unexpected plan size: %d", n) + } + + // First step should be a "get" step. + if step := (*plan)[0].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(0) operation: %s", step.Operation) + } else if step.Slice != 0 { + t.Fatalf("unexpected step(0) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(0) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + // Second step should also be a "get" step. + if step := (*plan)[1].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(1) operation: %s", step.Operation) + } else if step.Slice != 0 { + t.Fatalf("unexpected step(1) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{20, "default", 0}) { + t.Fatalf("unexpected step(1) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + // Third step should union the first two steps. + if step := (*plan)[2].(query.UnionQueryStep); step.Operation != "union" { + t.Fatalf("unexpected step(2) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Inputs, []*util.GUID{ + (*plan)[0].(query.GetQueryStep).Id, + (*plan)[1].(query.GetQueryStep).Id, + }) { + t.Fatalf("unexpected step(2) inputs: %s", spew.Sprint(step.Inputs)) + } + + // Fourth step should be a "get" step. + if step := (*plan)[3].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(3) operation: %s", step.Operation) + } else if step.Slice != 1 { + t.Fatalf("unexpected step(3) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{10, "default", 0}) { + t.Fatalf("unexpected step(3) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + // Fifth step should also be a "get" step. + if step := (*plan)[4].(query.GetQueryStep); step.Operation != "get" { + t.Fatalf("unexpected step(4) operation: %s", step.Operation) + } else if step.Slice != 1 { + t.Fatalf("unexpected step(4) slice: %d", step.Slice) + } else if !reflect.DeepEqual(step.Bitmap, &db.Bitmap{20, "default", 0}) { + t.Fatalf("unexpected step(4) bitmap: %s", spew.Sprint(step.Bitmap)) + } + + // Sixth step should union the previous two steps. + if step := (*plan)[5].(query.UnionQueryStep); step.Operation != "union" { + t.Fatalf("unexpected step(5) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Inputs, []*util.GUID{ + (*plan)[3].(query.GetQueryStep).Id, + (*plan)[4].(query.GetQueryStep).Id, + }) { + t.Fatalf("unexpected step(5) inputs: %s", spew.Sprint(step.Inputs)) + } + + // Final step should concatenate the two union steps. + if step := (*plan)[6].(query.CatQueryStep); step.Operation != "cat" { + t.Fatalf("unexpected step(6) operation: %s", step.Operation) + } else if !reflect.DeepEqual(step.Inputs, []*util.GUID{ + (*plan)[2].(query.UnionQueryStep).Id, + (*plan)[5].(query.UnionQueryStep).Id, + }) { + t.Fatalf("unexpected step(6) inputs: %s", spew.Sprint(step.Inputs)) + } +} + +// NewDefaultDB returns a simple, initialized database and fragment. +func NewDefaultDB() (*db.Database, *db.Fragment) { + // Create an empty database + c := db.NewCluster() + d := c.GetOrCreateDatabase("main") + f := d.GetOrCreateFrame("default") + + s1 := d.GetOrCreateSlice(0) + frag1 := d.GetOrCreateFragment(f, s1, util.Id()) + pid := util.RandomUUID() + p1 := db.NewProcess(&pid) + p1.SetHost("----192.1.1.0----") + frag1.SetProcess(p1) + + s2 := d.GetOrCreateSlice(1) + frag2 := d.GetOrCreateFragment(f, s2, util.Id()) + pid = util.RandomUUID() + p2 := db.NewProcess(&pid) + p2.SetHost("----192.1.1.1----") + frag2.SetProcess(p2) + + return d, frag1 } diff --git a/query/query_test.go b/query/query_test.go index c8d0e42b1..5850295b6 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -1,32 +1,36 @@ -package query +package query_test import ( + "reflect" "testing" - . "github.com/smartystreets/goconvey/convey" + "github.com/davecgh/go-spew/spew" + "github.com/umbel/pilosa/query" ) -func TestQuery(t *testing.T) { - Convey("Bracketed Lists 1", t, func() { - tokens, err := Lex("plugin(get(88, general), [get(12, general), get(13, general)])") - So(err, ShouldBeNil) - filter, filters := TokensToFilterStrings(tokens) - So(filter, ShouldEqual, "get(88,general)") - So(filters, ShouldResemble, []string{"get(12,general)", "get(13,general)"}) - }) - Convey("Bracketed Lists 2", t, func() { - tokens, err := Lex("plugin(intersect(get(88, general, [0]), get(77, b.n)), [get(12, general), get(13, general)])") - So(err, ShouldBeNil) - filter, filters := TokensToFilterStrings(tokens) - So(filter, ShouldEqual, "intersect(get(88,general,[0]),get(77,b.n))") - So(filters, ShouldResemble, []string{"get(12,general)", "get(13,general)"}) - }) - Convey("Bracketed Lists 3", t, func() { - tokens, err := Lex("plugin(intersect(get(88, general, [0]), get(77, b.n)))") - So(err, ShouldBeNil) - filter, filters := TokensToFilterStrings(tokens) - So(filter, ShouldEqual, "intersect(get(88,general,[0]),get(77,b.n))") - var empty []string - So(filters, ShouldResemble, empty) - }) +func TestTokensToFilterStrings1(t *testing.T) { + filter, filters := query.TokensToFilterStrings(MustLex("plugin(get(88, general), [get(12, general), get(13, general)])")) + if filter != "get(88,general)" { + t.Fatalf("unexpected filter: %s", filter) + } else if !reflect.DeepEqual(filters, []string{"get(12,general)", "get(13,general)"}) { + t.Fatalf("unexpected filters: %s", spew.Sprint(filters)) + } +} + +func TestTokensToFilterStrings2(t *testing.T) { + filter, filters := query.TokensToFilterStrings(MustLex("plugin(intersect(get(88, general, [0]), get(77, b.n)), [get(12, general), get(13, general)])")) + if filter != "intersect(get(88,general,[0]),get(77,b.n))" { + t.Fatalf("unexpected filter: %s", filter) + } else if !reflect.DeepEqual(filters, []string{"get(12,general)", "get(13,general)"}) { + t.Fatalf("unexpected filters: %s", spew.Sprint(filters)) + } +} + +func TestTokensToFilterStrings3(t *testing.T) { + filter, filters := query.TokensToFilterStrings(MustLex("plugin(intersect(get(88, general, [0]), get(77, b.n)))")) + if filter != "intersect(get(88,general,[0]),get(77,b.n))" { + t.Fatalf("unexpected filter: %s", filter) + } else if !reflect.DeepEqual(filters, []string(nil)) { + t.Fatalf("unexpected filters: %s", spew.Sprint(filters)) + } }