Merge pull request #5 from benbjohnson/query-tests

Refactor query tests
This commit is contained in:
Ben Johnson 2015-08-19 10:10:42 -06:00
commit 5a00b2f7f4
6 changed files with 715 additions and 591 deletions

View file

@ -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",

View file

@ -7,7 +7,6 @@ import (
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestStorage(t *testing.T) {

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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))
}
}