diff --git a/api.go b/api.go index abf2a84ea..f0e2dbfb0 100644 --- a/api.go +++ b/api.go @@ -257,7 +257,7 @@ func (api *API) CreateField(ctx context.Context, indexName string, fieldName str } // Create field. - field, err := index.CreateField(fieldName, fo) + field, err := index.CreateField(fieldName, opts) if err != nil { return nil, errors.Wrap(err, "creating field") } diff --git a/cluster_internal_test.go b/cluster_internal_test.go index aee4ea09b..6dc79fdef 100644 --- a/cluster_internal_test.go +++ b/cluster_internal_test.go @@ -150,7 +150,7 @@ func TestFragSources(t *testing.T) { c5.addNodeBasicSorted(node3) idx := newIndexWithTempPath("i") - field, err := idx.CreateFieldIfNotExists("f", FieldOptions{}) + field, err := idx.CreateFieldIfNotExists("f", OptFieldTypeDefault()) if err != nil { t.Fatal(err) } @@ -697,7 +697,7 @@ func TestCluster_ResizeStates(t *testing.T) { } // Add Bit Data to node0. - if err := tc.CreateField("i", "f", FieldOptions{}); err != nil { + if err := tc.CreateField("i", "f", OptFieldTypeDefault()); err != nil { t.Fatal(err) } tc.SetBit("i", "f", 1, 101, nil) diff --git a/executor_test.go b/executor_test.go index f54fb7e33..c5e2f24a8 100644 --- a/executor_test.go +++ b/executor_test.go @@ -39,7 +39,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { defer c.Close() hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - f, err := index.CreateField("f", pilosa.FieldOptions{}) + f, err := index.CreateField("f", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatal(err) } @@ -89,7 +89,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := index.CreateField("f", pilosa.FieldOptions{}); err != nil { + if _, err := index.CreateField("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } @@ -112,7 +112,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{Keys: true}) - if _, err := index.CreateField("f", pilosa.FieldOptions{Keys: true}); err != nil { + if _, err := index.CreateField("f", pilosa.OptFieldTypeDefault(), pilosa.OptFieldKeys()); err != nil { t.Fatal(err) } @@ -354,7 +354,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) { if err := index.DeleteField("f"); err != nil { t.Fatal(err) } - if _, err := index.CreateField("f", pilosa.FieldOptions{}); err != nil { + if _, err := index.CreateField("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } @@ -365,7 +365,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) { t.Run("ErrInvalidRowValueType", func(t *testing.T) { index := hldr.MustCreateIndexIfNotExists("inokey", pilosa.IndexOptions{}) - if _, err := index.CreateField("f", pilosa.FieldOptions{Keys: true}); err != nil { + if _, err := index.CreateField("f", pilosa.OptFieldTypeDefault(), pilosa.OptFieldKeys()); err != nil { t.Fatal(err) } if _, err := cmd.API.Query(context.Background(), &pilosa.QueryRequest{Index: "inokey", Query: `Set(2, f=1)`}); err == nil || errors.Cause(err).Error() != `row value must be a string when field 'keys' option enabled` { @@ -398,13 +398,9 @@ func TestExecutor_Execute_SetValue(t *testing.T) { // Create felds. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 50, - }); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeInt(0, 50)); err != nil { t.Fatal(err) - } else if _, err := index.CreateFieldIfNotExists("xxx", pilosa.FieldOptions{}); err != nil { + } else if _, err := index.CreateFieldIfNotExists("xxx", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } @@ -439,11 +435,7 @@ func TestExecutor_Execute_SetValue(t *testing.T) { hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 100, - }); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeInt(0, 100)); err != nil { t.Fatal(err) } @@ -475,9 +467,9 @@ func TestExecutor_Execute_SetRowAttrs(t *testing.T) { // Create fields. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{}); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) - } else if _, err := index.CreateFieldIfNotExists("xxx", pilosa.FieldOptions{}); err != nil { + } else if _, err := index.CreateFieldIfNotExists("xxx", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } @@ -514,9 +506,9 @@ func TestExecutor_Execute_TopN(t *testing.T) { // Set columns for rows 0, 10, & 20 across two shards. if idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("f", pilosa.FieldOptions{}); err != nil { + } else if _, err := idx.CreateField("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("other", pilosa.FieldOptions{}); err != nil { + } else if _, err := idx.CreateField("other", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: ` Set(0, f=0) @@ -555,9 +547,9 @@ func TestExecutor_Execute_TopN(t *testing.T) { // Set columns for rows 0, 10, & 20 across two shards. if idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{Keys: true}); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("f", pilosa.FieldOptions{Keys: true}); err != nil { + } else if _, err := idx.CreateField("f", pilosa.OptFieldTypeDefault(), pilosa.OptFieldKeys()); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("other", pilosa.FieldOptions{Keys: true}); err != nil { + } else if _, err := idx.CreateField("other", pilosa.OptFieldTypeDefault(), pilosa.OptFieldKeys()); err != nil { t.Fatal(err) } else if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: ` Set("a", f="foo") @@ -741,15 +733,11 @@ func TestExecutor_Execute_MinMax(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("x", pilosa.FieldOptions{}); err != nil { + if _, err := idx.CreateField("x", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: -10, - Max: 100, - }); err != nil { + if _, err := idx.CreateField("f", pilosa.OptFieldTypeInt(-10, 100)); err != nil { t.Fatal(err) } @@ -836,31 +824,19 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("x", pilosa.FieldOptions{}); err != nil { + if _, err := idx.CreateField("x", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 10, - Max: 100, - }); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, 100)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 100000, - }); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, 100000)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 1000, - }); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, 1000)); err != nil { t.Fatal(err) } @@ -906,10 +882,7 @@ func TestExecutor_Execute_Range(t *testing.T) { index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) // Create field. - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeTime, - TimeQuantum: pilosa.TimeQuantum("YMDH"), - }); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeTime(pilosa.TimeQuantum("YMDH"))); err != nil { t.Fatal(err) } @@ -962,39 +935,23 @@ func TestExecutor_Execute_BSIGroupRange(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("f", pilosa.FieldOptions{}); err != nil { + if _, err := idx.CreateField("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 10, - Max: 100, - }); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, 100)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 100000, - }); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, 100000)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 1000, - }); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, 1000)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("edge", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: -100, - Max: 100, - }); err != nil { + if _, err := idx.CreateField("edge", pilosa.OptFieldTypeInt(-100, 100)); err != nil { t.Fatal(err) } @@ -1268,7 +1225,7 @@ func TestExecutor_SetColumnAttrs_ExcludeField(t *testing.T) { hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - _, err := index.CreateField("f", pilosa.FieldOptions{}) + _, err := index.CreateField("f", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatalf("creating field: %v", err) } @@ -1352,10 +1309,7 @@ func TestExecutor_Time_Clear_Quantums(t *testing.T) { indexName := strings.ToLower(string(tt.quantum)) index := hldr.MustCreateIndexIfNotExists(indexName, pilosa.IndexOptions{}) // Create field. - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeTime, - TimeQuantum: tt.quantum, - }); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeTime(tt.quantum)); err != nil { t.Fatal(err) } // Populate diff --git a/field.go b/field.go index 76be9cf3f..2b78176e1 100644 --- a/field.go +++ b/field.go @@ -78,6 +78,25 @@ type Field struct { // FieldOption is a functional option type for pilosa.FieldOptions. type FieldOption func(fo *FieldOptions) error +func OptFieldKeys() FieldOption { + return func(fo *FieldOptions) error { + fo.Keys = true + return nil + } +} + +func OptFieldTypeDefault() FieldOption { + return func(fo *FieldOptions) error { + if fo.Type != "" { + return errors.Errorf("field type is already set to: %s", fo.Type) + } + fo.Type = FieldTypeSet + fo.CacheType = DefaultCacheType + fo.CacheSize = DefaultCacheSize + return nil + } +} + func OptFieldTypeSet(cacheType string, cacheSize uint32) FieldOption { return func(fo *FieldOptions) error { if fo.Type != "" { @@ -120,12 +139,19 @@ func OptFieldTypeTime(timeQuantum TimeQuantum) FieldOption { } // NewField returns a new instance of field. -func NewField(path, index, name string, options FieldOptions) (*Field, error) { +func NewField(path, index, name string, opts FieldOption) (*Field, error) { err := validateName(name) if err != nil { return nil, err } + // Apply functional option. + fo := FieldOptions{} + err = opts(&fo) + if err != nil { + return nil, errors.Wrap(err, "applying option") + } + f := &Field{ path: path, index: index, @@ -138,7 +164,7 @@ func NewField(path, index, name string, options FieldOptions) (*Field, error) { broadcaster: NopBroadcaster, Stats: NopStatsClient, - options: applyDefaultOptions(options), + options: applyDefaultOptions(fo), Logger: NopLogger, } diff --git a/field_internal_test.go b/field_internal_test.go index a11b5c394..4ded2bebe 100644 --- a/field_internal_test.go +++ b/field_internal_test.go @@ -153,7 +153,7 @@ func TestBSIGroup_BaseValue(t *testing.T) { // Ensure field can open and retrieve a view. func TestField_DeleteView(t *testing.T) { - f := MustOpenField(FieldOptions{}) + f := MustOpenField(OptFieldTypeDefault()) defer f.Close() viewName := viewStandard + "_v" @@ -190,12 +190,12 @@ type TestField struct { } // NewTestField returns a new instance of TestField d/0. -func NewTestField(options FieldOptions) *TestField { +func NewTestField(opts FieldOption) *TestField { path, err := ioutil.TempDir("", "pilosa-field-") if err != nil { panic(err) } - field, err := NewField(path, "i", "f", options) + field, err := NewField(path, "i", "f", opts) if err != nil { panic(err) } @@ -203,8 +203,8 @@ func NewTestField(options FieldOptions) *TestField { } // MustOpenField returns a new, opened field at a temporary path. Panic on error. -func MustOpenField(options FieldOptions) *TestField { - f := NewTestField(options) +func MustOpenField(opts FieldOption) *TestField { + f := NewTestField(opts) if err := f.Open(); err != nil { panic(err) } @@ -225,7 +225,7 @@ func (f *TestField) Reopen() error { } path, index, name := f.Path(), f.Index(), f.Name() - f.Field, err = NewField(path, index, name, FieldOptions{}) + f.Field, err = NewField(path, index, name, OptFieldTypeDefault()) if err != nil { return err } @@ -253,7 +253,7 @@ func (f *TestField) MustSetBit(row, col uint64, ts ...time.Time) { // Ensure field can open and retrieve a view. func TestField_CreateViewIfNotExists(t *testing.T) { - f := MustOpenField(FieldOptions{}) + f := MustOpenField(OptFieldTypeDefault()) defer f.Close() // Create view. @@ -278,7 +278,7 @@ func TestField_CreateViewIfNotExists(t *testing.T) { } func TestField_SetTimeQuantum(t *testing.T) { - f := MustOpenField(FieldOptions{Type: FieldTypeTime}) + f := MustOpenField(OptFieldTypeTime(TimeQuantum(""))) defer f.Close() // Set & retrieve time quantum. @@ -297,7 +297,7 @@ func TestField_SetTimeQuantum(t *testing.T) { } func TestField_RowTime(t *testing.T) { - f := MustOpenField(FieldOptions{Type: FieldTypeTime}) + f := MustOpenField(OptFieldTypeTime(TimeQuantum(""))) defer f.Close() if err := f.SetTimeQuantum(TimeQuantum("YMDH")); err != nil { diff --git a/field_test.go b/field_test.go index 4b1def96c..520179f52 100644 --- a/field_test.go +++ b/field_test.go @@ -28,11 +28,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 30, - }) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0, 30)) if err != nil { t.Fatal(err) } @@ -65,11 +61,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 0, - Max: 30, - }) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0, 30)) if err != nil { t.Fatal(err) } @@ -102,9 +94,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeSet, - }) + f, err := idx.CreateField("f", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatal(err) } @@ -119,11 +109,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 20, - Max: 30, - }) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(20, 30)) if err != nil { t.Fatal(err) } @@ -138,11 +124,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 20, - Max: 30, - }) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(20, 30)) if err != nil { t.Fatal(err) } @@ -159,7 +141,7 @@ func TestField_NameRestriction(t *testing.T) { if err != nil { panic(err) } - field, err := pilosa.NewField(path, "i", ".meta", pilosa.FieldOptions{}) + field, err := pilosa.NewField(path, "i", ".meta", pilosa.OptFieldTypeDefault()) if field != nil { t.Fatalf("unexpected field name %s", err) } @@ -191,13 +173,13 @@ func TestField_NameValidation(t *testing.T) { panic(err) } for _, name := range validFieldNames { - _, err := pilosa.NewField(path, "i", name, pilosa.FieldOptions{}) + _, err := pilosa.NewField(path, "i", name, pilosa.OptFieldTypeDefault()) if err != nil { t.Fatalf("unexpected field name: %s %s", name, err) } } for _, name := range invalidFieldNames { - _, err := pilosa.NewField(path, "i", name, pilosa.FieldOptions{}) + _, err := pilosa.NewField(path, "i", name, pilosa.OptFieldTypeDefault()) if err == nil { t.Fatalf("expected error on field name: %s", name) } diff --git a/fragment_internal_test.go b/fragment_internal_test.go index eda4cbbdc..4ceb33819 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -750,7 +750,7 @@ func TestFragment_TopN_CacheSize(t *testing.T) { defer index.Close() // Create field. - field, err := index.CreateFieldIfNotExists("f", FieldOptions{CacheType: CacheTypeRanked, CacheSize: cacheSize}) + field, err := index.CreateFieldIfNotExists("f", OptFieldTypeSet(CacheTypeRanked, cacheSize)) if err != nil { t.Fatal(err) } @@ -916,7 +916,7 @@ func TestFragment_RankCache_Persistence(t *testing.T) { defer index.Close() // Create field. - field, err := index.CreateFieldIfNotExists("f", FieldOptions{CacheType: CacheTypeRanked}) + field, err := index.CreateFieldIfNotExists("f", OptFieldTypeSet(CacheTypeRanked, DefaultCacheSize)) if err != nil { t.Fatal(err) } diff --git a/holder.go b/holder.go index e599d21f2..3aae91475 100644 --- a/holder.go +++ b/holder.go @@ -241,7 +241,7 @@ func (h *Holder) applySchema(schema *internal.Schema) error { // Create fields that don't exist. for _, f := range index.Fields { opt := decodeFieldOptions(f.Meta) - field, err := idx.CreateFieldIfNotExists(f.Name, *opt) + field, err := idx.createFieldIfNotExists(f.Name, *opt) if err != nil { return errors.Wrap(err, "creating field") } diff --git a/holder_internal_test.go b/holder_internal_test.go index 425873005..75a3508fd 100644 --- a/holder_internal_test.go +++ b/holder_internal_test.go @@ -60,7 +60,7 @@ func newHolder() *tHolder { // MustCreateFieldIfNotExists returns a given field. Panic on error. func (h *tHolder) MustCreateFieldIfNotExists(index, field string) *Field { - f, err := h.MustCreateIndexIfNotExists(index, IndexOptions{}).CreateFieldIfNotExists(field, FieldOptions{}) + f, err := h.MustCreateIndexIfNotExists(index, IndexOptions{}).CreateFieldIfNotExists(field, OptFieldTypeDefault()) if err != nil { panic(err) } @@ -105,7 +105,7 @@ func TestHolder_Optn(t *testing.T) { if idx, err := h.CreateIndex("foo", IndexOptions{}); err != nil { t.Fatal(err) - } else if field, err := idx.CreateField("bar", FieldOptions{}); err != nil { + } else if field, err := idx.CreateField("bar", OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := field.createViewIfNotExists(viewStandard); err != nil { t.Fatal(err) @@ -129,7 +129,7 @@ func TestHolder_Optn(t *testing.T) { if idx, err := h.CreateIndex("foo", IndexOptions{}); err != nil { t.Fatal(err) - } else if field, err := idx.CreateField("bar", FieldOptions{}); err != nil { + } else if field, err := idx.CreateField("bar", OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := field.createViewIfNotExists(viewStandard); err != nil { t.Fatal(err) @@ -154,7 +154,7 @@ func TestHolder_Optn(t *testing.T) { if idx, err := h.CreateIndex("foo", IndexOptions{}); err != nil { t.Fatal(err) - } else if field, err := idx.CreateField("bar", FieldOptions{}); err != nil { + } else if field, err := idx.CreateField("bar", OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if view, err := field.createViewIfNotExists(viewStandard); err != nil { t.Fatal(err) diff --git a/holder_test.go b/holder_test.go index 078ab55f3..d4886ff30 100644 --- a/holder_test.go +++ b/holder_test.go @@ -98,7 +98,7 @@ func TestHolder_Open(t *testing.T) { if idx, err := h.CreateIndex("foo", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("bar", pilosa.FieldOptions{}); err != nil { + } else if _, err := idx.CreateField("bar", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if err := h.Holder.Close(); err != nil { t.Fatal(err) @@ -117,7 +117,7 @@ func TestHolder_Open(t *testing.T) { if idx, err := h.CreateIndex("foo", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("bar", pilosa.FieldOptions{}); err != nil { + } else if _, err := idx.CreateField("bar", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if err := h.Holder.Close(); err != nil { t.Fatal(err) @@ -135,7 +135,7 @@ func TestHolder_Open(t *testing.T) { if idx, err := h.CreateIndex("foo", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if _, err := idx.CreateField("bar", pilosa.FieldOptions{}); err != nil { + } else if _, err := idx.CreateField("bar", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if err := h.Holder.Close(); err != nil { t.Fatal(err) @@ -157,7 +157,7 @@ func TestHolder_Open(t *testing.T) { if idx, err := h.CreateIndex("foo", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if field, err := idx.CreateField("bar", pilosa.FieldOptions{}); err != nil { + } else if field, err := idx.CreateField("bar", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := field.SetBit(0, 0, nil); err != nil { t.Fatal(err) @@ -178,7 +178,7 @@ func TestHolder_Open(t *testing.T) { if idx, err := h.CreateIndex("foo", pilosa.IndexOptions{}); err != nil { t.Fatal(err) - } else if field, err := idx.CreateField("bar", pilosa.FieldOptions{}); err != nil { + } else if field, err := idx.CreateField("bar", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := field.SetBit(0, 0, nil); err != nil { t.Fatal(err) diff --git a/http/client_test.go b/http/client_test.go index fb1105a27..00a9735a6 100644 --- a/http/client_test.go +++ b/http/client_test.go @@ -218,15 +218,10 @@ func TestClient_ImportValue(t *testing.T) { hldr := test.Holder{Holder: holder} fldName := "f" - fo := pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: -100, - Max: 100, - } // Load bitmap into cache to ensure cache gets updated. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - field, err := index.CreateFieldIfNotExists(fldName, fo) + field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) if err != nil { t.Fatal(err) } diff --git a/index.go b/index.go index 98f50eced..882bc75d3 100644 --- a/index.go +++ b/index.go @@ -287,7 +287,7 @@ func (i *Index) RecalculateCaches() { } // CreateField creates a field. -func (i *Index) CreateField(name string, opt FieldOptions) (*Field, error) { +func (i *Index) CreateField(name string, opts ...FieldOption) (*Field, error) { i.mu.Lock() defer i.mu.Unlock() @@ -295,11 +295,40 @@ func (i *Index) CreateField(name string, opt FieldOptions) (*Field, error) { if i.fields[name] != nil { return nil, NewConflictError(ErrFieldExists) } - return i.createField(name, opt) + + // Apply functional options. + fo := FieldOptions{} + for _, opt := range opts { + err := opt(&fo) + if err != nil { + return nil, errors.Wrap(err, "applying option") + } + } + + return i.createField(name, fo) } // CreateFieldIfNotExists creates a field with the given options if it doesn't exist. -func (i *Index) CreateFieldIfNotExists(name string, opt FieldOptions) (*Field, error) { +func (i *Index) CreateFieldIfNotExists(name string, opts FieldOption) (*Field, error) { + i.mu.Lock() + defer i.mu.Unlock() + + // Find field in cache first. + if f := i.fields[name]; f != nil { + return f, nil + } + + // Apply functional option. + fo := FieldOptions{} + err := opts(&fo) + if err != nil { + return nil, errors.Wrap(err, "applying option") + } + + return i.createField(name, fo) +} + +func (i *Index) createFieldIfNotExists(name string, opt FieldOptions) (*Field, error) { i.mu.Lock() defer i.mu.Unlock() @@ -347,7 +376,7 @@ func (i *Index) createField(name string, opt FieldOptions) (*Field, error) { } func (i *Index) newField(path, name string) (*Field, error) { - f, err := NewField(path, i.name, name, FieldOptions{}) // TODO: NewField should be un-exported along with FieldOptions + f, err := NewField(path, i.name, name, OptFieldTypeDefault()) // TODO: NewField should be un-exported along with FieldOptions if err != nil { return nil, err } diff --git a/index_test.go b/index_test.go index 5490e2978..bc412646a 100644 --- a/index_test.go +++ b/index_test.go @@ -33,7 +33,7 @@ func TestIndex_CreateFieldIfNotExists(t *testing.T) { defer index.Close() // Create field. - f, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{}) + f, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatal(err) } else if f == nil { @@ -41,7 +41,7 @@ func TestIndex_CreateFieldIfNotExists(t *testing.T) { } // Retrieve existing field. - other, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{}) + other, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatal(err) } else if f.Field != other.Field { @@ -61,10 +61,7 @@ func TestIndex_CreateField(t *testing.T) { defer index.Close() // Create field with explicit quantum. - f, err := index.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeTime, - TimeQuantum: pilosa.TimeQuantum("YMDH"), - }) + f, err := index.CreateField("f", pilosa.OptFieldTypeTime(pilosa.TimeQuantum("YMDH"))) if err != nil { t.Fatal(err) } else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YMDH") { @@ -80,11 +77,7 @@ func TestIndex_CreateField(t *testing.T) { defer index.Close() // Create field with schema and verify it exists. - if f, err := index.CreateField("f", pilosa.FieldOptions{ - Type: pilosa.FieldTypeInt, - Min: 10, - Max: 20, - }); err != nil { + if f, err := index.CreateField("f", pilosa.OptFieldTypeInt(10, 20)); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(f.Type(), pilosa.FieldTypeInt) { t.Fatalf("unexpected type: %#v", f.Type()) @@ -184,7 +177,7 @@ func TestIndex_DeleteField(t *testing.T) { defer index.Close() // Create field. - if _, err := index.CreateFieldIfNotExists("f", pilosa.FieldOptions{}); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } diff --git a/server.go b/server.go index 56195b427..ba058d134 100644 --- a/server.go +++ b/server.go @@ -455,7 +455,7 @@ func (s *Server) receiveMessage(pb proto.Message) error { return fmt.Errorf("Local Index not found: %s", obj.Index) } opt := decodeFieldOptions(obj.Meta) - _, err := idx.CreateField(obj.Field, *opt) + _, err := idx.createField(obj.Field, *opt) if err != nil { return err } diff --git a/server/handler_test.go b/server/handler_test.go index e49b1eb0f..a5ba8c398 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -62,17 +62,17 @@ func TestHandler_Endpoints(t *testing.T) { i0 := hldr.MustCreateIndexIfNotExists("i0", pilosa.IndexOptions{}) i1 := hldr.MustCreateIndexIfNotExists("i1", pilosa.IndexOptions{}) - if f, err := i0.CreateFieldIfNotExists("f1", pilosa.FieldOptions{}); err != nil { + if f, err := i0.CreateFieldIfNotExists("f1", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := f.SetBit(0, 0, nil); err != nil { t.Fatal(err) } - if f, err := i1.CreateFieldIfNotExists("f0", pilosa.FieldOptions{}); err != nil { + if f, err := i1.CreateFieldIfNotExists("f0", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } else if _, err := f.SetBit(0, 0, nil); err != nil { t.Fatal(err) } - if _, err := i0.CreateFieldIfNotExists("f0", pilosa.FieldOptions{}); err != nil { + if _, err := i0.CreateFieldIfNotExists("f0", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } @@ -401,7 +401,7 @@ func TestHandler_Endpoints(t *testing.T) { t.Run("Field delete", func(t *testing.T) { i := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := i.CreateFieldIfNotExists("f1", pilosa.FieldOptions{}); err != nil { + if _, err := i.CreateFieldIfNotExists("f1", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) } w := httptest.NewRecorder() @@ -453,7 +453,7 @@ func TestHandler_Endpoints(t *testing.T) { } }) - meta, err := i.CreateFieldIfNotExists("meta", pilosa.FieldOptions{}) + meta, err := i.CreateFieldIfNotExists("meta", pilosa.OptFieldTypeDefault()) if err != nil { t.Fatal(err) } diff --git a/test/field.go b/test/field.go index 345deadf8..7a0439026 100644 --- a/test/field.go +++ b/test/field.go @@ -28,12 +28,12 @@ type Field struct { } // NewField returns a new instance of Field d/0. -func NewField(options pilosa.FieldOptions) *Field { +func NewField(opts pilosa.FieldOption) *Field { path, err := ioutil.TempDir("", "pilosa-field-") if err != nil { panic(err) } - field, err := pilosa.NewField(path, "i", "f", options) + field, err := pilosa.NewField(path, "i", "f", opts) if err != nil { panic(err) } @@ -41,8 +41,8 @@ func NewField(options pilosa.FieldOptions) *Field { } // MustOpenField returns a new, opened field at a temporary path. Panic on error. -func MustOpenField(options pilosa.FieldOptions) *Field { - f := NewField(options) +func MustOpenField(opts pilosa.FieldOption) *Field { + f := NewField(opts) if err := f.Open(); err != nil { panic(err) } @@ -63,7 +63,7 @@ func (f *Field) Reopen() error { } path, index, name := f.Path(), f.Index(), f.Name() - f.Field, err = pilosa.NewField(path, index, name, pilosa.FieldOptions{}) + f.Field, err = pilosa.NewField(path, index, name, pilosa.OptFieldTypeDefault()) if err != nil { return err } @@ -76,7 +76,7 @@ func (f *Field) Reopen() error { // Ensure field can set its cache func TestField_SetCacheSize(t *testing.T) { - f := MustOpenField(pilosa.FieldOptions{}) + f := MustOpenField(pilosa.OptFieldTypeDefault()) defer f.Close() cacheSize := uint32(100) diff --git a/test/holder.go b/test/holder.go index 94d1c778f..9cc96fe14 100644 --- a/test/holder.go +++ b/test/holder.go @@ -83,7 +83,7 @@ func (h *Holder) MustCreateIndexIfNotExists(index string, opt pilosa.IndexOption // MustCreateFieldIfNotExists returns a given field. Panic on error. func (h *Holder) MustCreateFieldIfNotExists(index, field string) *Field { - f, err := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}).CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}).CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } @@ -93,7 +93,7 @@ func (h *Holder) MustCreateFieldIfNotExists(index, field string) *Field { // Row returns a Row for a given field. func (h *Holder) Row(index, field string, rowID uint64) *pilosa.Row { idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}) - f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } @@ -106,7 +106,7 @@ func (h *Holder) Row(index, field string, rowID uint64) *pilosa.Row { func (h *Holder) RowAttrStore(index, field string) pilosa.AttrStore { idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}) - f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } @@ -115,7 +115,7 @@ func (h *Holder) RowAttrStore(index, field string) pilosa.AttrStore { func (h *Holder) RowTime(index, field string, rowID uint64, t time.Time, quantum string) *pilosa.Row { idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}) - f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } @@ -129,7 +129,7 @@ func (h *Holder) RowTime(index, field string, rowID uint64, t time.Time, quantum // SetBit clears a bit on the given field. func (h *Holder) SetBit(index, field string, rowID, columnID uint64) { idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}) - f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } @@ -142,7 +142,7 @@ func (h *Holder) SetBit(index, field string, rowID, columnID uint64) { // ClearBit clears a bit on the given field. func (h *Holder) ClearBit(index, field string, rowID, columnID uint64) { idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{}) - f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{}) + f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault()) if err != nil { panic(err) } diff --git a/test/index.go b/test/index.go index 69c18682c..9b7c1684b 100644 --- a/test/index.go +++ b/test/index.go @@ -74,8 +74,8 @@ func (i *Index) Reopen() error { } // CreateField creates a field with the given options. -func (i *Index) CreateField(name string, opt pilosa.FieldOptions) (*Field, error) { - f, err := i.Index.CreateField(name, opt) +func (i *Index) CreateField(name string, opts ...pilosa.FieldOption) (*Field, error) { + f, err := i.Index.CreateField(name, opts...) if err != nil { return nil, err } @@ -83,8 +83,8 @@ func (i *Index) CreateField(name string, opt pilosa.FieldOptions) (*Field, error } // CreateFieldIfNotExists creates a field with the given options if it doesn't exist. -func (i *Index) CreateFieldIfNotExists(name string, opt pilosa.FieldOptions) (*Field, error) { - f, err := i.Index.CreateFieldIfNotExists(name, opt) +func (i *Index) CreateFieldIfNotExists(name string, opts pilosa.FieldOption) (*Field, error) { + f, err := i.Index.CreateFieldIfNotExists(name, opts) if err != nil { return nil, err } diff --git a/utils_internal_test.go b/utils_internal_test.go index ca7bd1fa7..bbcf300cd 100644 --- a/utils_internal_test.go +++ b/utils_internal_test.go @@ -104,13 +104,13 @@ func (t *ClusterCluster) CreateIndex(name string) error { return nil } -func (t *ClusterCluster) CreateField(index, field string, opt FieldOptions) error { +func (t *ClusterCluster) CreateField(index, field string, opts FieldOption) error { for _, c := range t.Clusters { idx, err := c.holder.CreateIndexIfNotExists(index, IndexOptions{}) if err != nil { return err } - if _, err := idx.CreateField(field, opt); err != nil { + if _, err := idx.CreateField(field, opts); err != nil { return err } }