mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Merge pull request #551 from benbjohnson/515-topn-inverse
Add inverse TopN() support.
This commit is contained in:
commit
928ee49187
3 changed files with 61 additions and 26 deletions
13
executor.go
13
executor.go
|
|
@ -279,7 +279,7 @@ func (e *Executor) executeBitmapCallSlice(ctx context.Context, index string, c *
|
|||
// This first performs the TopN() to determine the top results and then
|
||||
// requeries to retrieve the full counts for each of the top results.
|
||||
func (e *Executor) executeTopN(ctx context.Context, index string, c *pql.Call, slices []uint64, opt *ExecOptions) ([]Pair, error) {
|
||||
rowIDs, _, err := c.UintSliceArg("ids")
|
||||
idsArg, _, err := c.UintSliceArg("ids")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("executeTopN: %v", err)
|
||||
}
|
||||
|
|
@ -296,7 +296,7 @@ func (e *Executor) executeTopN(ctx context.Context, index string, c *pql.Call, s
|
|||
|
||||
// If this call is against specific ids, or we didn't get results,
|
||||
// or we are part of a larger distributed query then don't refetch.
|
||||
if len(pairs) == 0 || len(rowIDs) > 0 || opt.Remote {
|
||||
if len(pairs) == 0 || len(idsArg) > 0 || opt.Remote {
|
||||
return pairs, nil
|
||||
}
|
||||
// Only the original caller should refetch the full counts.
|
||||
|
|
@ -344,6 +344,7 @@ func (e *Executor) executeTopNSlices(ctx context.Context, index string, c *pql.C
|
|||
// executeTopNSlice executes a TopN call for a single slice.
|
||||
func (e *Executor) executeTopNSlice(ctx context.Context, index string, c *pql.Call, slice uint64) ([]Pair, error) {
|
||||
frame, _ := c.Args["frame"].(string)
|
||||
inverse, _ := c.Args["inverse"].(bool)
|
||||
n, _, err := c.UintArg("n")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("executeTopNSlice: %v", err)
|
||||
|
|
@ -380,7 +381,13 @@ func (e *Executor) executeTopNSlice(ctx context.Context, index string, c *pql.Ca
|
|||
frame = DefaultFrame
|
||||
}
|
||||
|
||||
f := e.Holder.Fragment(index, frame, ViewStandard, slice)
|
||||
// Determine view.
|
||||
view := ViewStandard
|
||||
if inverse {
|
||||
view = ViewInverse
|
||||
}
|
||||
|
||||
f := e.Holder.Fragment(index, frame, view, slice)
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -274,32 +275,55 @@ func TestExecutor_Execute_SetRowAttrs(t *testing.T) {
|
|||
func TestExecutor_Execute_TopN(t *testing.T) {
|
||||
hldr := MustOpenHolder()
|
||||
defer hldr.Close()
|
||||
e := NewExecutor(hldr.Holder, NewCluster(1))
|
||||
|
||||
// Set bits for rows 0, 10, & 20 across two slices.
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 0).SetBit(0, 0)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 0).SetBit(0, 1)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).SetBit(0, SliceWidth)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).SetBit(0, SliceWidth+2)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 5).SetBit(0, (5*SliceWidth)+100)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 0).SetBit(10, 0)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).SetBit(10, SliceWidth)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).SetBit(20, SliceWidth)
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "other", pilosa.ViewStandard, 0).SetBit(0, 0)
|
||||
if idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := idx.CreateFrame("f", pilosa.FrameOptions{InverseEnabled: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := idx.CreateFrame("other", pilosa.FrameOptions{InverseEnabled: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := e.Execute(context.Background(), "i", MustParse(`
|
||||
SetBit(frame=f, rowID=0, columnID=0)
|
||||
SetBit(frame=f, rowID=0, columnID=1)
|
||||
SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa(SliceWidth)+`)
|
||||
SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa(SliceWidth+2)+`)
|
||||
SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa((5*SliceWidth)+100)+`)
|
||||
SetBit(frame=f, rowID=10, columnID=0)
|
||||
SetBit(frame=f, rowID=10, columnID=`+strconv.Itoa(SliceWidth)+`)
|
||||
SetBit(frame=f, rowID=20, columnID=`+strconv.Itoa(SliceWidth)+`)
|
||||
SetBit(frame=other, rowID=0, columnID=0)
|
||||
`), nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 0).RecalculateCache()
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewInverse, 0).RecalculateCache()
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 1).RecalculateCache()
|
||||
hldr.MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, 5).RecalculateCache()
|
||||
|
||||
// Execute query.
|
||||
e := NewExecutor(hldr.Holder, NewCluster(1))
|
||||
if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(frame=f, n=2)`), nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(result[0], []pilosa.Pair{
|
||||
{ID: 0, Count: 5},
|
||||
{ID: 10, Count: 2},
|
||||
}) {
|
||||
t.Fatalf("unexpected result: %s", spew.Sdump(result))
|
||||
}
|
||||
t.Run("Standard", func(t *testing.T) {
|
||||
if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(frame=f, n=2)`), nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(result[0], []pilosa.Pair{
|
||||
{ID: 0, Count: 5},
|
||||
{ID: 10, Count: 2},
|
||||
}) {
|
||||
t.Fatalf("unexpected result: %s", spew.Sdump(result))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Inverse", func(t *testing.T) {
|
||||
if result, err := e.Execute(context.Background(), "i", MustParse(`TopN(frame=f, inverse=true, n=2)`), nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(result[0], []pilosa.Pair{
|
||||
{ID: SliceWidth, Count: 3},
|
||||
{ID: 0, Count: 2},
|
||||
}) {
|
||||
t.Fatalf("unexpected result: %s", spew.Sdump(result))
|
||||
}
|
||||
})
|
||||
}
|
||||
func TestExecutor_Execute_TopN_fill(t *testing.T) {
|
||||
hldr := MustOpenHolder()
|
||||
|
|
|
|||
12
pql/ast.go
12
pql/ast.go
|
|
@ -184,16 +184,20 @@ func (c *Call) String() string {
|
|||
|
||||
// SupportsInverse indicates that the call may be on an inverse frame.
|
||||
func (c *Call) SupportsInverse() bool {
|
||||
if c.Name == "Bitmap" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return c.Name == "Bitmap" || c.Name == "TopN"
|
||||
}
|
||||
|
||||
// IsInverse specifies if the call is for an inverse view.
|
||||
// Return defaults to false unless absolutely sure of inversion.
|
||||
func (c *Call) IsInverse(rowLabel, columnLabel string) bool {
|
||||
if c.SupportsInverse() {
|
||||
// Top-n has an explicit inverse flag.
|
||||
if c.Name == "TopN" {
|
||||
inverse, _ := c.Args["inverse"].(bool)
|
||||
return inverse
|
||||
}
|
||||
|
||||
// Bitmap calls use the row/column labels to determine whether inverse.
|
||||
_, rowOK, rowErr := c.UintArg(rowLabel)
|
||||
_, columnOK, columnErr := c.UintArg(columnLabel)
|
||||
if rowErr != nil || columnErr != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue