Merge pull request #455 from benbjohnson/merge-bitmap-profile-call

Consolidate Bitmap() & Profile() calls.
This commit is contained in:
Ben Johnson 2017-04-18 09:14:56 -06:00 committed by GitHub
commit 47a68bc3bd
2 changed files with 111 additions and 42 deletions

View file

@ -100,8 +100,6 @@ func (e *Executor) executeCall(ctx context.Context, db string, c *pql.Call, slic
return e.executeClearBit(ctx, db, c, opt)
case "Count":
return e.executeCount(ctx, db, c, slices, opt)
case "Profile":
return e.executeProfile(ctx, db, c, opt)
case "SetBit":
return e.executeSetBit(ctx, db, c, opt)
case "SetBitmapAttrs":
@ -156,21 +154,32 @@ func (e *Executor) executeBitmapCall(ctx context.Context, db string, c *pql.Call
return nil, err
}
// Attach bitmap attributes for Bitmap() calls.
// Attach attributes for Bitmap() calls.
// If the column label is used then return profile attributes.
// If the row label is used then return bitmap attributes.
bm, _ := other.(*Bitmap)
if c.Name == "Bitmap" {
frame, _ := c.Args["frame"].(string)
fr := e.Index.Frame(db, frame)
if fr != nil {
rowLabel := fr.RowLabel()
rowID, _ := c.Args[rowLabel].(uint64)
attrs, err := fr.BitmapAttrStore().Attrs(rowID)
if err != nil {
return nil, err
d := e.Index.DB(db)
if d != nil {
columnLabel := d.ColumnLabel()
if columnID, ok := c.Args[columnLabel].(uint64); ok {
attrs, err := d.ProfileAttrStore().Attrs(columnID)
if err != nil {
return nil, err
}
bm.Attrs = attrs
} else {
frame, _ := c.Args["frame"].(string)
if fr := d.Frame(frame); fr != nil {
rowLabel := fr.RowLabel()
rowID, _ := c.Args[rowLabel].(uint64)
attrs, err := fr.BitmapAttrStore().Attrs(rowID)
if err != nil {
return nil, err
}
bm.Attrs = attrs
}
}
bm.Attrs = attrs
}
}
@ -330,27 +339,47 @@ func (e *Executor) executeDifferenceSlice(ctx context.Context, db string, c *pql
}
func (e *Executor) executeBitmapSlice(ctx context.Context, db string, c *pql.Call, slice uint64) (*Bitmap, error) {
// Fetch column label from database.
d := e.Index.DB(db)
if d == nil {
return nil, ErrDatabaseNotFound
}
columnLabel := d.ColumnLabel()
// Fetch frame & row label based on argument.
frame, _ := c.Args["frame"].(string)
if frame == "" {
frame = DefaultFrame
}
f := e.Index.Frame(db, frame)
if f == nil {
return nil, ErrFrameNotFound
}
rowLabel := f.RowLabel()
rowID, ok := c.Args[rowLabel].(uint64)
if !ok {
return nil, fmt.Errorf("Bitmap() field required: %s", rowLabel)
// Return an error if both the row and column label are specified.
rowID, rowOK := c.Args[rowLabel].(uint64)
columnID, columnOK := c.Args[columnLabel].(uint64)
if rowOK && columnOK {
return nil, fmt.Errorf("Bitmap() cannot specify both %s and %s values", rowLabel, columnLabel)
} else if !rowOK && !columnOK {
return nil, fmt.Errorf("Bitmap() must specify either %s or %s values", rowLabel, columnLabel)
}
frag := e.Index.Fragment(db, frame, ViewStandard, slice)
// Determine row or column orientation.
view, id := ViewStandard, rowID
if columnOK {
view, id = ViewInverse, columnID
if !f.InverseEnabled() {
return nil, fmt.Errorf("Bitmap() cannot retrieve columns unless inverse storage enabled")
}
}
frag := e.Index.Fragment(db, frame, view, slice)
if frag == nil {
return NewBitmap(), nil
}
return frag.Bitmap(rowID), nil
return frag.Bitmap(id), nil
}
// executeIntersectSlice executes a intersect() call for a local slice.
@ -482,12 +511,6 @@ func (e *Executor) executeCount(ctx context.Context, db string, c *pql.Call, sli
return n, nil
}
// executeProfile executes a Profile() call.
// This call only executes locally since the profile attibutes are stored locally.
func (e *Executor) executeProfile(ctx context.Context, db string, c *pql.Call, opt *ExecOptions) (*Profile, error) {
panic("FIXME: impl: e.Index.ProfileAttr(c.ID)")
}
// executeClearBit executes a ClearBit() call.
func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call, opt *ExecOptions) (bool, error) {
view, _ := c.Args["view"].(string)
@ -1165,7 +1188,7 @@ func needsSlices(calls []*pql.Call) bool {
}
for _, call := range calls {
switch call.Name {
case "ClearBit", "Profile", "SetBit", "SetBitmapAttrs", "SetProfileAttrs":
case "ClearBit", "SetBit", "SetBitmapAttrs", "SetProfileAttrs":
continue
case "Count", "TopN":
return true

View file

@ -2,6 +2,7 @@ package pilosa_test
import (
"context"
"fmt"
"reflect"
"strings"
"testing"
@ -13,23 +14,68 @@ import (
// Ensure a bitmap query can be executed.
func TestExecutor_Execute_Bitmap(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
idx.MustCreateFragmentIfNotExists("d", "f", pilosa.ViewStandard, 0).MustSetBits(10, 3)
idx.MustCreateFragmentIfNotExists("d", "f", pilosa.ViewStandard, 1).MustSetBits(10, SliceWidth+1)
t.Run("Row", func(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
db := idx.MustCreateDBIfNotExists("d", pilosa.DBOptions{})
f, err := db.CreateFrame("f", pilosa.FrameOptions{InverseEnabled: true})
if err != nil {
t.Fatal(err)
}
if err := idx.Frame("d", "f").BitmapAttrStore().SetAttrs(10, map[string]interface{}{"foo": "bar", "baz": uint64(123)}); err != nil {
t.Fatal(err)
}
e := NewExecutor(idx.Index, NewCluster(1))
e := NewExecutor(idx.Index, NewCluster(1))
if res, err := e.Execute(context.Background(), "d", MustParse(`Bitmap(id=10, frame=f)`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{3, SliceWidth + 1}) {
t.Fatalf("unexpected bits: %+v", bits)
} else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{"foo": "bar", "baz": uint64(123)}) {
t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs))
}
// Set bits.
if _, err := e.Execute(context.Background(), "d", MustParse(``+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 10, 3)+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 10, SliceWidth+1)+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 20, SliceWidth+1),
), nil, nil); err != nil {
t.Fatal(err)
}
if err := f.BitmapAttrStore().SetAttrs(10, map[string]interface{}{"foo": "bar", "baz": uint64(123)}); err != nil {
t.Fatal(err)
}
if res, err := e.Execute(context.Background(), "d", MustParse(`Bitmap(id=10, frame=f)`), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{3, SliceWidth + 1}) {
t.Fatalf("unexpected bits: %+v", bits)
} else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{"foo": "bar", "baz": uint64(123)}) {
t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs))
}
})
t.Run("Column", func(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
db := idx.MustCreateDBIfNotExists("d", pilosa.DBOptions{})
if _, err := db.CreateFrame("f", pilosa.FrameOptions{InverseEnabled: true}); err != nil {
t.Fatal(err)
}
e := NewExecutor(idx.Index, NewCluster(1))
// Set bits.
if _, err := e.Execute(context.Background(), "d", MustParse(``+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 10, 3)+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 10, SliceWidth+1)+
fmt.Sprintf("SetBit(frame=f, id=%d, profileID=%d)\n", 20, SliceWidth+1),
), nil, nil); err != nil {
t.Fatal(err)
}
if err := db.ProfileAttrStore().SetAttrs(SliceWidth+1, map[string]interface{}{"foo": "bar", "baz": uint64(123)}); err != nil {
t.Fatal(err)
}
if res, err := e.Execute(context.Background(), "d", MustParse(fmt.Sprintf(`Bitmap(profileID=%d, frame=f)`, SliceWidth+1)), nil, nil); err != nil {
t.Fatal(err)
} else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{10, 20}) {
t.Fatalf("unexpected bits: %+v", bits)
} else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{"foo": "bar", "baz": uint64(123)}) {
t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs))
}
})
}
// Ensure a difference query can be executed.