From d4ddf3676c8f5ff2125b6c3a536e2ab829a9f443 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Sun, 7 May 2017 15:21:30 -0600 Subject: [PATCH] Support inverse Range() queries. --- executor.go | 34 +++++++++++++++++++++++++++----- executor_test.go | 51 +++++++++++++++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/executor.go b/executor.go index 69a39e4a4..86d333b16 100644 --- a/executor.go +++ b/executor.go @@ -502,19 +502,43 @@ func (e *Executor) executeRangeSlice(ctx context.Context, index string, c *pql.C frame = DefaultFrame } + // Retrieve column label. + idx := e.Holder.Index(index) + if idx == nil { + return nil, ErrIndexNotFound + } + columnLabel := idx.ColumnLabel() + // Retrieve base frame. - f := e.Holder.Frame(index, frame) + f := idx.Frame(frame) if f == nil { return nil, ErrFrameNotFound } rowLabel := f.RowLabel() - // Read row id. - rowID, _, err := c.UintArg(rowLabel) // TODO: why are we ignoring missing rowID? + // Read row & column id. + columnID, columnOK, err := c.UintArg(columnLabel) + if err != nil { + return nil, fmt.Errorf("executeRangeSlice - reading column: %v", err) + } + rowID, rowOK, err := c.UintArg(rowLabel) if err != nil { return nil, fmt.Errorf("executeRangeSlice - reading row: %v", err) } + // Determine view. + var id uint64 + var viewName string + if columnOK && rowOK { + return nil, fmt.Errorf("Range() cannot contain both %q and %q", columnLabel, rowLabel) + } else if !columnOK && !rowOK { + return nil, fmt.Errorf("Range() must specify either %q or %q", columnLabel, rowLabel) + } else if columnOK { + viewName, id = ViewInverse, columnID + } else { + viewName, id = ViewStandard, rowID + } + // Parse start time. startTimeStr, ok := c.Args["start"].(string) if !ok { @@ -543,12 +567,12 @@ func (e *Executor) executeRangeSlice(ctx context.Context, index string, c *pql.C // Union bitmaps across all time-based subframes. bm := &Bitmap{} - for _, view := range ViewsByTimeRange(ViewStandard, startTime, endTime, q) { + for _, view := range ViewsByTimeRange(viewName, startTime, endTime, q) { f := e.Holder.Fragment(index, frame, view, slice) if f == nil { continue } - bm = bm.Union(f.Row(rowID)) + bm = bm.Union(f.Row(id)) } return bm, nil } diff --git a/executor_test.go b/executor_test.go index a89911f38..8ae53b0a8 100644 --- a/executor_test.go +++ b/executor_test.go @@ -445,36 +445,51 @@ func TestExecutor_Execute_TopN_Attr_Src(t *testing.T) { func TestExecutor_Execute_Range(t *testing.T) { hldr := MustOpenHolder() defer hldr.Close() + e := NewExecutor(hldr.Holder, NewCluster(1)) // Create index. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) // Create frame. - f, err := index.CreateFrameIfNotExists("f", pilosa.FrameOptions{}) - if err != nil { - t.Fatal(err) - } else if err := f.SetTimeQuantum(pilosa.TimeQuantum("YMDH")); err != nil { + if _, err := index.CreateFrameIfNotExists("f", pilosa.FrameOptions{ + InverseEnabled: true, + TimeQuantum: pilosa.TimeQuantum("YMDH"), + }); err != nil { t.Fatal(err) } // Set bits. - 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")) + if _, err := e.Execute(context.Background(), "i", MustParse(` + SetBit(frame=f, rowID=1, columnID=2, timestamp="1999-12-31T00:00") + SetBit(frame=f, rowID=1, columnID=3, timestamp="2000-01-01T00:00") + SetBit(frame=f, rowID=1, columnID=4, timestamp="2000-01-02T00:00") + SetBit(frame=f, rowID=1, columnID=5, timestamp="2000-02-01T00:00") + SetBit(frame=f, rowID=1, columnID=6, timestamp="2001-01-01T00:00") + SetBit(frame=f, rowID=1, columnID=7, timestamp="2002-01-01T02:00") - 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 row - - e := NewExecutor(hldr.Holder, NewCluster(1)) - if res, err := e.Execute(context.Background(), "i", MustParse(`Range(rowID=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil { + SetBit(frame=f, rowID=1, columnID=2, timestamp="1999-12-30T00:00") + SetBit(frame=f, rowID=1, columnID=2, timestamp="2002-02-01T00:00") + SetBit(frame=f, rowID=10, columnID=2, timestamp="2001-01-01T00:00") + `), nil, nil); err != nil { t.Fatal(err) - } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{2, 3, 4, 5, 6, 7}) { - t.Fatalf("unexpected bits: %+v", bits) } + + t.Run("Standard", func(t *testing.T) { + if res, err := e.Execute(context.Background(), "i", MustParse(`Range(rowID=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil { + t.Fatal(err) + } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{2, 3, 4, 5, 6, 7}) { + t.Fatalf("unexpected bits: %+v", bits) + } + }) + + t.Run("Inverse", func(t *testing.T) { + e := NewExecutor(hldr.Holder, NewCluster(1)) + if res, err := e.Execute(context.Background(), "i", MustParse(`Range(columnID=2, frame=f, start="1999-01-01T00:00", end="2003-01-01T00:00")`), nil, nil); err != nil { + t.Fatal(err) + } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{1, 10}) { + t.Fatalf("unexpected bits: %+v", bits) + } + }) } // Ensure a remote query can return a bitmap.