Merge pull request #415 from benbjohnson/view-refactor

Refactor higher level view interface.
This commit is contained in:
Ben Johnson 2017-04-05 17:01:01 -06:00 committed by GitHub
commit 6ce4621eba
7 changed files with 103 additions and 70 deletions

View file

@ -490,26 +490,27 @@ func (e *Executor) executeProfile(ctx context.Context, db string, c *pql.Call, o
// 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)
frame, ok := c.Args["frame"].(string)
if !ok {
return false, errors.New("ClearBit() frame required")
}
// Lookup column label.
// Retrieve frame.
d := e.Index.DB(db)
if d == nil {
return false, nil
return false, ErrDatabaseNotFound
}
columnLabel := d.ColumnLabel()
// Lookup row label.
f := e.Index.Frame(db, frame)
f := d.Frame(frame)
if f == nil {
return false, nil
return false, ErrFrameNotFound
}
// Retrieve labels.
columnLabel := d.ColumnLabel()
rowLabel := f.RowLabel()
// Read row & column ids.
// Read fields using labels.
rowID, ok := c.Args[rowLabel].(uint64)
if !ok {
return false, fmt.Errorf("ClearBit() field required: %s", rowLabel)
@ -520,12 +521,38 @@ func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call,
return false, fmt.Errorf("ClearBit() field required: %s", columnLabel)
}
// Clear bits for each view.
switch view {
case ViewStandard:
return e.executeClearBitView(ctx, db, c, f, view, colID, rowID, opt)
case ViewInverse:
return e.executeClearBitView(ctx, db, c, f, view, rowID, colID, opt)
case "":
var ret bool
if changed, err := e.executeClearBitView(ctx, db, c, f, ViewStandard, colID, rowID, opt); err != nil {
return ret, err
} else if changed {
ret = true
}
if changed, err := e.executeClearBitView(ctx, db, c, f, ViewInverse, rowID, colID, opt); err != nil {
return ret, err
} else if changed {
ret = true
}
return ret, nil
default:
return false, fmt.Errorf("invalid view: %s", view)
}
}
// executeClearBitView executes a ClearBit() call for a single view.
func (e *Executor) executeClearBitView(ctx context.Context, db string, c *pql.Call, f *Frame, view string, colID, rowID uint64, opt *ExecOptions) (bool, error) {
slice := colID / SliceWidth
ret := false
for _, node := range e.Cluster.FragmentNodes(db, slice) {
// Update locally if host matches.
if node.Host == e.Host {
val, err := f.ClearBit(rowID, colID, nil)
val, err := f.ClearBit(view, rowID, colID, nil)
if err != nil {
return false, err
} else if val {
@ -550,6 +577,7 @@ func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call,
// executeSetBit executes a SetBit() call.
func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, opt *ExecOptions) (bool, error) {
view, _ := c.Args["view"].(string)
frame, ok := c.Args["frame"].(string)
if !ok {
return false, errors.New("SetBit() field required: frame")
@ -558,7 +586,7 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
// Retrieve frame.
d := e.Index.DB(db)
if d == nil {
return false, ErrFrameNotFound
return false, ErrDatabaseNotFound
}
f := d.Frame(frame)
if f == nil {
@ -590,13 +618,39 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
timestamp = &t
}
// Set bits for each view.
switch view {
case ViewStandard:
return e.executeSetBitView(ctx, db, c, f, view, colID, rowID, timestamp, opt)
case ViewInverse:
return e.executeSetBitView(ctx, db, c, f, view, rowID, colID, timestamp, opt)
case "":
var ret bool
if changed, err := e.executeSetBitView(ctx, db, c, f, ViewStandard, colID, rowID, timestamp, opt); err != nil {
return ret, err
} else if changed {
ret = true
}
if changed, err := e.executeSetBitView(ctx, db, c, f, ViewInverse, rowID, colID, timestamp, opt); err != nil {
return ret, err
} else if changed {
ret = true
}
return ret, nil
default:
return false, fmt.Errorf("invalid view: %s", view)
}
}
// executeSetBitView executes a SetBit() call for a specific view.
func (e *Executor) executeSetBitView(ctx context.Context, db string, c *pql.Call, f *Frame, view string, colID, rowID uint64, timestamp *time.Time, opt *ExecOptions) (bool, error) {
slice := colID / SliceWidth
ret := false
for _, node := range e.Cluster.FragmentNodes(db, slice) {
// Update locally if host matches.
if node.Host == e.Host {
val, err := f.SetBit(rowID, colID, timestamp)
val, err := f.SetBit(view, rowID, colID, timestamp)
if err != nil {
return false, err
} else if val {

View file

@ -390,16 +390,16 @@ func TestExecutor_Execute_Range(t *testing.T) {
}
// Set bits.
f.MustSetBit(1, 2, MustParseTimePtr("1999-12-31 00:00"))
f.MustSetBit(1, 3, MustParseTimePtr("2000-01-01 00:00"))
f.MustSetBit(1, 4, MustParseTimePtr("2000-01-02 00:00"))
f.MustSetBit(1, 5, MustParseTimePtr("2000-02-01 00:00"))
f.MustSetBit(1, 6, MustParseTimePtr("2001-01-01 00:00"))
f.MustSetBit(1, 7, MustParseTimePtr("2002-01-01 02:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("1999-12-31 00:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 3, MustParseTimePtr("2000-01-01 00:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 4, MustParseTimePtr("2000-01-02 00:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 5, MustParseTimePtr("2000-02-01 00:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 6, MustParseTimePtr("2001-01-01 00:00"))
f.MustSetBit(pilosa.ViewStandard, 1, 7, MustParseTimePtr("2002-01-01 02:00"))
f.MustSetBit(1, 2, MustParseTimePtr("1999-12-30 00:00")) // too early
f.MustSetBit(1, 2, MustParseTimePtr("2002-02-01 00:00")) // too late
f.MustSetBit(10, 2, MustParseTimePtr("2001-01-01 00:00")) // different bitmap
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("1999-12-30 00:00")) // too early
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("2002-02-01 00:00")) // too late
f.MustSetBit(pilosa.ViewStandard, 10, 2, MustParseTimePtr("2001-01-01 00:00")) // different bitmap
e := NewExecutor(idx.Index, NewCluster(1))
if res, err := e.Execute(context.Background(), "d", MustParse(`Range(id=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil {

View file

@ -348,28 +348,13 @@ func (f *Frame) newView(path, name string) *View {
return view
}
// SetBit sets a bit within the frame.
func (f *Frame) SetBit(rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Set standard layout bits.
if v, err := f.setBit(ViewStandard, rowID, colID, t); err != nil {
return changed, err
} else if v {
changed = v
// SetBit sets a bit on a view within the frame.
func (f *Frame) SetBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Validate view name.
if !IsValidView(name) {
return false, ErrInvalidView
}
// Set inverse layout bits.
// NOTE: The row & col are transposed for the inverted view.
if v, err := f.setBit(ViewInverse, colID, rowID, t); err != nil {
return changed, err
} else if v {
changed = v
}
return changed, nil
}
// setBit sets a bit for a given layout (default or inverted).
func (f *Frame) setBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Retrieve view. Exit if it doesn't exist.
view, err := f.CreateViewIfNotExists(name)
if err != nil {
@ -406,27 +391,12 @@ func (f *Frame) setBit(name string, rowID, colID uint64, t *time.Time) (changed
}
// ClearBit clears a bit within the frame.
func (f *Frame) ClearBit(rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Clear standard layout bits.
if v, err := f.clearBit(ViewStandard, rowID, colID, t); err != nil {
return changed, err
} else if v {
changed = v
func (f *Frame) ClearBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Validate view name.
if !IsValidView(name) {
return false, ErrInvalidView
}
// Clear inverse layout bits.
// NOTE: The row & col are transposed for the inverted view.
if v, err := f.clearBit(ViewInverse, colID, rowID, t); err != nil {
return changed, err
} else if v {
changed = v
}
return changed, nil
}
// clearBit clears a bit for a given layout (default or inverted).
func (f *Frame) clearBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
// Retrieve view. Exit if it doesn't exist.
view, err := f.CreateViewIfNotExists(name)
if err != nil {

View file

@ -119,8 +119,8 @@ func (f *Frame) Reopen() error {
}
// MustSetBit sets a bit on the frame. Panic on error.
func (f *Frame) MustSetBit(bitmapID, profileID uint64, t *time.Time) (changed bool) {
changed, err := f.SetBit(bitmapID, profileID, t)
func (f *Frame) MustSetBit(view string, bitmapID, profileID uint64, t *time.Time) (changed bool) {
changed, err := f.SetBit(view, bitmapID, profileID, t)
if err != nil {
panic(err)
}

View file

@ -39,12 +39,14 @@ func TestHandler_Schema(t *testing.T) {
if f, err := d0.CreateFrameIfNotExists("f1", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
} else if _, err := f.SetBit(0, 0, nil); err != nil {
} else if _, err := f.SetBit(pilosa.ViewStandard, 0, 0, nil); err != nil {
t.Fatal(err)
} else if _, err := f.SetBit(pilosa.ViewInverse, 0, 0, nil); err != nil {
t.Fatal(err)
}
if f, err := d1.CreateFrameIfNotExists("f0", pilosa.FrameOptions{}); err != nil {
t.Fatal(err)
} else if _, err := f.SetBit(0, 0, nil); err != nil {
} else if _, err := f.SetBit(pilosa.ViewStandard, 0, 0, nil); err != nil {
t.Fatal(err)
}
if _, err := d0.CreateFrameIfNotExists("f0", pilosa.FrameOptions{}); err != nil {
@ -57,7 +59,7 @@ func TestHandler_Schema(t *testing.T) {
h.ServeHTTP(w, MustNewHTTPRequest("GET", "/schema", nil))
if w.Code != http.StatusOK {
t.Fatalf("unexpected status code: %d", w.Code)
} else if body := w.Body.String(); body != `{"dbs":[{"name":"d0","frames":[{"name":"f0"},{"name":"f1","views":[{"name":"inverse"},{"name":"standard"}]}]},{"name":"d1","frames":[{"name":"f0","views":[{"name":"inverse"},{"name":"standard"}]}]}]}`+"\n" {
} else if body := w.Body.String(); body != `{"dbs":[{"name":"d0","frames":[{"name":"f0"},{"name":"f1","views":[{"name":"inverse"},{"name":"standard"}]}]},{"name":"d1","frames":[{"name":"f0","views":[{"name":"standard"}]}]}]}`+"\n" {
t.Fatalf("unexpected body: %s", body)
}
}
@ -95,11 +97,11 @@ func TestHandler_MaxSlices_Inverse(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, err := f0.SetBit((1*SliceWidth)+1, 30, nil); err != nil {
if _, err := f0.SetBit(pilosa.ViewInverse, 30, (1*SliceWidth)+1, nil); err != nil {
t.Fatal(err)
} else if _, err := f0.SetBit((1*SliceWidth)+2, 30, nil); err != nil {
} else if _, err := f0.SetBit(pilosa.ViewInverse, 30, (1*SliceWidth)+2, nil); err != nil {
t.Fatal(err)
} else if _, err := f0.SetBit((3*SliceWidth)+4, 30, nil); err != nil {
} else if _, err := f0.SetBit(pilosa.ViewInverse, 30, (3*SliceWidth)+4, nil); err != nil {
t.Fatal(err)
}
@ -107,11 +109,11 @@ func TestHandler_MaxSlices_Inverse(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, err := f1.SetBit((0*SliceWidth)+1, 40, nil); err != nil {
if _, err := f1.SetBit(pilosa.ViewStandard, 40, (0*SliceWidth)+1, nil); err != nil {
t.Fatal(err)
} else if _, err := f1.SetBit((0*SliceWidth)+2, 40, nil); err != nil {
} else if _, err := f1.SetBit(pilosa.ViewInverse, 40, (0*SliceWidth)+2, nil); err != nil {
t.Fatal(err)
} else if _, err := f1.SetBit((0*SliceWidth)+4, 40, nil); err != nil {
} else if _, err := f1.SetBit(pilosa.ViewInverse, 40, (0*SliceWidth)+4, nil); err != nil {
t.Fatal(err)
}

View file

@ -20,6 +20,8 @@ var (
ErrFrameExists = errors.New("frame already exists")
ErrFrameNotFound = errors.New("frame not found")
ErrInvalidView = errors.New("invalid veiw")
ErrName = errors.New("invalid database or frame's name, must match [a-z0-9_-]")
// ErrFragmentNotFound is returned when a fragment does not exist.

View file

@ -17,6 +17,11 @@ const (
ViewInverse = "inverse"
)
// IsValidView returns true if name is valid.
func IsValidView(name string) bool {
return name == ViewStandard || name == ViewInverse
}
// View represents a container for frame data.
type View struct {
mu sync.Mutex