mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 17:15:56 +00:00
Previously, multiple frames with different prefixes were used to separate different data layouts. This included separating standard row/column layouts from inverted column/row layouts as well as storing aggregate information for timestamp data. Unfortunately, this caused frame meta data to be copied between multiple frames and it made it difficult to keep these frames in sync. This commit separates these different physical layouts into `Views`. A `Frame` now has one or more views which represent each layout. Fragments have been moved from under the `Frame` to be contained within the `View`. There are two primary views: - `standard` - `inverse` If a frame has a time quantum, then views are generated for these each of the standard/inverse views. For example a time quantum of `YMDH` for the date `2000-01-02T00:00:00Z` would create the following views: - `standard_2000` - `inverse_2000` - `standard_200001` - `inverse_200001` - `standard_20000102` - `inverse_20000102` From the user's perspective, nothing should change in PQL. Different PQL statements will handle the appropriate view automatically. For example, `Bitmap()` and `Profile()` will fetch using the `standard` view or the `inverse` view, respectively. The `Range()` statement will lookup the appropriate time-based views automatically.
128 lines
2.7 KiB
Go
128 lines
2.7 KiB
Go
package pilosa_test
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pilosa/pilosa"
|
|
)
|
|
|
|
// Ensure frame can open and retrieve a view.
|
|
func TestFrame_CreateViewIfNotExists(t *testing.T) {
|
|
f := MustOpenFrame()
|
|
defer f.Close()
|
|
|
|
// Create view.
|
|
view, err := f.CreateViewIfNotExists("v")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if view == nil {
|
|
t.Fatal("expected view")
|
|
}
|
|
|
|
// Retrieve existing view.
|
|
view2, err := f.CreateViewIfNotExists("v")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if view != view2 {
|
|
t.Fatal("view mismatch")
|
|
}
|
|
|
|
if view != f.View("v") {
|
|
t.Fatal("view mismatch")
|
|
}
|
|
}
|
|
|
|
// Ensure frame can set its time quantum.
|
|
func TestFrame_SetTimeQuantum(t *testing.T) {
|
|
f := MustOpenFrame()
|
|
defer f.Close()
|
|
|
|
// Set & retrieve time quantum.
|
|
if err := f.SetTimeQuantum(pilosa.TimeQuantum("YMDH")); err != nil {
|
|
t.Fatal(err)
|
|
} else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YMDH") {
|
|
t.Fatalf("unexpected quantum: %s", q)
|
|
}
|
|
|
|
// Reload frame and verify that it is persisted.
|
|
if err := f.Reopen(); err != nil {
|
|
t.Fatal(err)
|
|
} else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YMDH") {
|
|
t.Fatalf("unexpected quantum (reopen): %s", q)
|
|
}
|
|
}
|
|
|
|
func TestFrame_NameRestriction(t *testing.T) {
|
|
path, err := ioutil.TempDir("", "pilosa-frame-")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
frame, err := pilosa.NewFrame(path, "d", ".meta")
|
|
if frame != nil {
|
|
t.Fatalf("unexpected frame name %s", err)
|
|
}
|
|
}
|
|
|
|
// Frame represents a test wrapper for pilosa.Frame.
|
|
type Frame struct {
|
|
*pilosa.Frame
|
|
}
|
|
|
|
// NewFrame returns a new instance of Frame d/0.
|
|
func NewFrame() *Frame {
|
|
path, err := ioutil.TempDir("", "pilosa-frame-")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
frame, err := pilosa.NewFrame(path, "d", "f")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &Frame{Frame: frame}
|
|
}
|
|
|
|
// MustOpenFrame returns a new, opened frame at a temporary path. Panic on error.
|
|
func MustOpenFrame() *Frame {
|
|
f := NewFrame()
|
|
if err := f.Open(); err != nil {
|
|
panic(err)
|
|
}
|
|
return f
|
|
}
|
|
|
|
// Close closes the frame and removes the underlying data.
|
|
func (f *Frame) Close() error {
|
|
defer os.RemoveAll(f.Path())
|
|
return f.Frame.Close()
|
|
}
|
|
|
|
// Reopen closes the database and reopens it.
|
|
func (f *Frame) Reopen() error {
|
|
var err error
|
|
if err := f.Frame.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
path, db, name := f.Path(), f.DB(), f.Name()
|
|
f.Frame, err = pilosa.NewFrame(path, db, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := f.Open(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return changed
|
|
}
|