diff --git a/core/topn.go b/core/topn.go index 38226d957..7483e13ae 100644 --- a/core/topn.go +++ b/core/topn.go @@ -166,35 +166,36 @@ func init() { func (self *Service) TopNQueryStepHandler(msg *db.Message) { qs := msg.Data.(query.TopNQueryStep) - var categoryleaves []uint64 - input := qs.Input - value, _ := self.Hold.Get(input, 10) var bh index.BitmapHandle - switch val := value.(type) { - case index.BitmapHandle: - bh = val - case []byte: - bh, _ = self.Index.FromBytes(qs.Location.FragmentId, val) + var topnPackage TopNPackage + + // if we have an input, hold for it. if we don't, we assume an all() query + if qs.Input == nil { + topn, err := self.Index.TopNAll(qs.Location.FragmentId, qs.N*2, qs.Filters) + if err != nil { + log.Println(spew.Sdump(err)) + } + topnPackage = TopNPackage{*qs.Location.ProcessId, qs.Location.FragmentId, topn, bh} + } else { + input := qs.Input + value, _ := self.Hold.Get(input, 10) + //var bh index.BitmapHandle + switch val := value.(type) { + case index.BitmapHandle: + bh = val + case []byte: + bh, _ = self.Index.FromBytes(qs.Location.FragmentId, val) + } + + topn, err := self.Index.TopN(qs.Location.FragmentId, bh, qs.N*2, qs.Filters) + if err != nil { + log.Println(spew.Sdump(err)) + } + topnPackage = TopNPackage{*qs.Location.ProcessId, qs.Location.FragmentId, topn, bh} } - categoryleaves = qs.Filters - - /* - spew.Dump(bh, qs.N*2, categoryleaves) - result_message := db.Message{Data: "foobar"} - self.Transport.Send(&result_message, qs.Destination.ProcessId) - */ - - topn, err := self.Index.TopN(qs.Location.FragmentId, bh, qs.N*2, categoryleaves) - topnPackage := TopNPackage{*qs.Location.ProcessId, qs.Location.FragmentId, topn, bh} - - if err != nil { - log.Println(spew.Sdump(err)) - - } result_message := db.Message{Data: query.TopNQueryResult{&query.BaseQueryResult{Id: qs.Id, Data: topnPackage}}} self.Transport.Send(&result_message, qs.Destination.ProcessId) - } func canCompile() bool { diff --git a/query/parser.go b/query/parser.go index 8891c8ca8..b69bd3c99 100644 --- a/query/parser.go +++ b/query/parser.go @@ -192,6 +192,8 @@ ArgLoop: } query.Args["n"] = i } + case "all": + // do nothing case "recall": //need pair based list of frag_id,handle arg, ok := query.Args["stash"] if !ok { diff --git a/query/parser_test.go b/query/parser_test.go index f21884a24..7b3c23872 100644 --- a/query/parser_test.go +++ b/query/parser_test.go @@ -90,4 +90,17 @@ func TestQueryParser(t *testing.T) { 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{}{}) + }) } diff --git a/query/planner.go b/query/planner.go index 580af9a8c..e7af17073 100644 --- a/query/planner.go +++ b/query/planner.go @@ -294,6 +294,19 @@ func (qt *CatQueryTree) getLocation(d *db.Database) (*db.Location, error) { return qt.location, err } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ALL +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// QueryTree for GET queries +type AllQueryTree struct { +} + +// Uses consistent hashing function to select node containing data for GET operation +func (qt *AllQueryTree) getLocation(d *db.Database) (*db.Location, error) { + return nil, nil +} + /////////////////////////////////////////////////////////////////////////////////////////////////// // GET /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -493,6 +506,8 @@ func (self *QueryPlanner) buildTree(query *Query, slice int) (QueryTree, error) return nil, err } tree = &CountQueryTree{subquery: subquery} + } else if query.Operation == "all" { + tree = &AllQueryTree{} } else if query.Operation == "top-n" { var frame string var n int @@ -715,12 +730,18 @@ func (self *QueryPlanner) flatten(qt QueryTree, id *util.GUID, location *db.Loca if err != nil { return nil, err } - step := &TopNQueryStep{&BaseQueryStep{id, "top-n", loc, location}, &sub_id, topn.Filters, topn.N, topn.Frame} - subq_steps, err := self.flatten(topn.subquery, &sub_id, loc) - if err != nil { - return nil, err + step := &TopNQueryStep{&BaseQueryStep{id, "top-n", loc, location}, nil, topn.Filters, topn.N, topn.Frame} + switch topn.subquery.(type) { + case *AllQueryTree: + // do nothing + default: + step.Input = &sub_id + subq_steps, err := self.flatten(topn.subquery, &sub_id, loc) + if err != nil { + return nil, err + } + plan = append(plan, *subq_steps...) } - plan = append(plan, *subq_steps...) plan = append(plan, step) } return &plan, nil diff --git a/query/planner_test.go b/query/planner_test.go index b01bdcab4..1ffe6ec2d 100644 --- a/query/planner_test.go +++ b/query/planner_test.go @@ -203,6 +203,25 @@ func TestQueryPlanner(t *testing.T) { 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()")