diff --git a/db/topology.go b/db/topology.go index ed4cfb4c6..bb3b25542 100644 --- a/db/topology.go +++ b/db/topology.go @@ -7,6 +7,7 @@ import ( "pilosa/config" "pilosa/util" "sync" + "github.com/stathat/consistent" ) @@ -363,6 +364,23 @@ func (d *Database) GetFragmentForBitmap(slice *Slice, bitmap *Bitmap) (*Fragment return fsi.GetFragment(frag_id) } +func (d *Database) GetFragmentForFrameSlice(frame *Frame, slice *Slice) (*Fragment, error) { + fsi, err := d.GetFrameSliceIntersect(frame, slice) + if err != nil { + log.Println("Missing frame,slice", frame, slice) + log.Println(err) + return nil, err + } + frag_id_s, err := fsi.hashring.Get("0") // we don't need a specific bitmap in here because we're assuming the hashring only has a single element + if err != nil { + log.Println("ERROR FSI.GET:", d.Name, frame, slice) + log.Println(err) + return nil, err + } + frag_id := util.Hex_to_SUUID(frag_id_s) + return fsi.GetFragment(frag_id) +} + /* // NOT IMPLEMENTED // this would loop through all frame_slice_intersect[], then all fragmments to find a match diff --git a/query/parser.go b/query/parser.go index ddb5109fe..e688d3da0 100644 --- a/query/parser.go +++ b/query/parser.go @@ -139,11 +139,16 @@ ArgLoop: } case "top-n": - i, err := strconv.Atoi(token.Text) - if err != nil { - return nil, fmt.Errorf("Expecting integer! (%v)", err) + switch len(query.Args) { + case 0: + query.Args["frame"] = token.Text + case 1: + i, err := strconv.Atoi(token.Text) + if err != nil { + return nil, fmt.Errorf("Expecting integer! (%v)", err) + } + query.Args["n"] = i } - query.Args["n"] = i default: spew.Dump("UNPROCESSED VALUE", token) } diff --git a/query/planner.go b/query/planner.go index 0021e3fb9..b08829e5f 100644 --- a/query/planner.go +++ b/query/planner.go @@ -121,6 +121,7 @@ type TopNQueryStep struct { Input *util.GUID Filters []uint64 N int + Frame string } type TopNQueryResult struct { @@ -133,11 +134,25 @@ type TopNQueryTree struct { location *db.Location Filters []uint64 N int + Frame string + Slice int } // Uses consistent hashing function to select node containing data for GET operation func (qt *TopNQueryTree) getLocation(d *db.Database) (*db.Location, error) { - return qt.subquery.getLocation(d) + var err error + if qt.location == nil { + frame := d.GetOrCreateFrame(qt.Frame) + slice := d.GetOrCreateSlice(qt.Slice) + fragment, err := d.GetFragmentForFrameSlice(frame, slice) + if err != nil { + log.Println("GetFragmentForFrameSliceFailed GetQueryTree", frame, slice) + return nil, err + } + qt.location = fragment.GetLocation() + return qt.location, nil + } + return qt.location, err } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -382,8 +397,13 @@ func (qp *QueryPlanner) buildTree(query *Query, slice int) (QueryTree, error) { } tree = &CountQueryTree{subquery: subquery} } else if query.Operation == "top-n" { + var frame string var n int var filters []uint64 + frame_, ok := query.Args["frame"] + if ok { + frame = frame_.(string) + } n_, ok := query.Args["n"] if ok { n = n_.(int) @@ -397,7 +417,7 @@ func (qp *QueryPlanner) buildTree(query *Query, slice int) (QueryTree, error) { if err != nil { return nil, err } - tree = &TopNQueryTree{subquery: subquery, Filters: filters, N: n} + tree = &TopNQueryTree{subquery: subquery, Filters: filters, N: n, Frame: frame, Slice: slice} } else if query.Operation == "union" { subqueries := make([]QueryTree, len(query.Subqueries)) var err error @@ -516,7 +536,7 @@ func (qp *QueryPlanner) flatten(qt QueryTree, id *util.GUID, location *db.Locati if err != nil { return nil, err } - step := &TopNQueryStep{&BaseQueryStep{id, "top-n", loc, location}, &sub_id, topn.Filters, topn.N} + step := &TopNQueryStep{&BaseQueryStep{id, "top-n", loc, location}, &sub_id, topn.Filters, topn.N, topn.Frame} subq_steps, err := qp.flatten(topn.subquery, &sub_id, loc) if err != nil { return nil, err