diff --git a/api_test.go b/api_test.go index ef737d596..45aec0419 100644 --- a/api_test.go +++ b/api_test.go @@ -17,6 +17,7 @@ package pilosa_test import ( "context" "fmt" + "math" "reflect" "strings" "testing" @@ -198,7 +199,7 @@ func TestAPI_ImportValue(t *testing.T) { if err != nil { t.Fatalf("creating index: %v", err) } - _, err = m0.API.CreateField(ctx, index, field, pilosa.OptFieldTypeInt(0)) + _, err = m0.API.CreateField(ctx, index, field, pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)) if err != nil { t.Fatalf("creating field: %v", err) } diff --git a/encoding/proto/proto.go b/encoding/proto/proto.go index 24cfcd232..f8a40badf 100644 --- a/encoding/proto/proto.go +++ b/encoding/proto/proto.go @@ -798,6 +798,8 @@ func decodeFieldOptions(options *internal.FieldOptions, m *pilosa.FieldOptions) m.Type = options.Type m.CacheType = options.CacheType m.CacheSize = options.CacheSize + m.Min = options.Min + m.Max = options.Max m.Base = options.Base m.BitDepth = uint(options.BitDepth) m.TimeQuantum = pilosa.TimeQuantum(options.TimeQuantum) diff --git a/executor.go b/executor.go index 2d05991ae..07a83496d 100644 --- a/executor.go +++ b/executor.go @@ -1408,7 +1408,6 @@ func (e *executor) executeRowBSIGroupShard(ctx context.Context, index string, c return frag.notNull() } else if cond.Op == pql.BETWEEN { - predicates, err := cond.IntSliceValue() if err != nil { return nil, errors.Wrap(err, "getting condition value") @@ -1442,7 +1441,7 @@ func (e *executor) executeRowBSIGroupShard(ctx context.Context, index string, c // If the query is asking for the entire valid range, just return // the not-null bitmap for the bsiGroup. - if predicates[0] <= bsig.Min() && predicates[1] >= bsig.Max() { + if predicates[0] <= bsig.Min && predicates[1] >= bsig.Max { return frag.notNull() } @@ -1474,8 +1473,8 @@ func (e *executor) executeRowBSIGroupShard(ctx context.Context, index string, c } // LT[E] and GT[E] should return all not-null if selected range fully encompasses valid bsiGroup range. - if (cond.Op == pql.LT && value > bsig.Max()) || (cond.Op == pql.LTE && value >= bsig.Max()) || - (cond.Op == pql.GT && value < bsig.Min()) || (cond.Op == pql.GTE && value <= bsig.Min()) { + if (cond.Op == pql.LT && value > bsig.Max) || (cond.Op == pql.LTE && value >= bsig.Max) || + (cond.Op == pql.GT && value < bsig.Min) || (cond.Op == pql.GTE && value <= bsig.Min) { return frag.notNull() } diff --git a/executor_test.go b/executor_test.go index 9473d02ca..ae66c7ff6 100644 --- a/executor_test.go +++ b/executor_test.go @@ -20,6 +20,7 @@ import ( "flag" "fmt" "io/ioutil" + "math" "math/rand" "reflect" "strconv" @@ -769,7 +770,7 @@ func TestExecutor_Execute_SetValue(t *testing.T) { // Create fields. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } else if _, err := index.CreateFieldIfNotExists("xxx", pilosa.OptFieldTypeDefault()); err != nil { t.Fatal(err) @@ -806,7 +807,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.OptFieldTypeInt(0)); err != nil { + if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1214,7 +1215,7 @@ func TestExecutor_Execute_MinMax(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("f", pilosa.OptFieldTypeInt(-10)); err != nil { + if _, err := idx.CreateField("f", pilosa.OptFieldTypeInt(-10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1278,7 +1279,7 @@ func TestExecutor_Execute_MinMax(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("f", pilosa.OptFieldTypeInt(-10)); err != nil { + if _, err := idx.CreateField("f", pilosa.OptFieldTypeInt(-10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1371,15 +1372,15 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10)); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1429,15 +1430,15 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10)); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1831,19 +1832,19 @@ func TestExecutor_Execute_Row_BSIGroup(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10)); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("edge", pilosa.OptFieldTypeInt(-100)); err != nil { + if _, err := idx.CreateField("edge", pilosa.OptFieldTypeInt(-100, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -1942,7 +1943,7 @@ func TestExecutor_Execute_Row_BSIGroup(t *testing.T) { {q: `Row(1000 < other <= 1000)`, exp: false}, {q: `Row(1000 < other < 2000)`, exp: false}, - {q: `Row(1000 <= other < 2000)`, exp: true}, + {q: `Row(1000 <= other < 20000)`, exp: true}, {q: `Row(1000 <= other <= 2000)`, exp: true}, {q: `Row(1000 < other <= 2000)`, exp: false}, } @@ -1955,7 +1956,7 @@ func TestExecutor_Execute_Row_BSIGroup(t *testing.T) { if result, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: test.q}); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(expected, result.Results[0].(*pilosa.Row).Columns()) { - t.Fatalf("unexpected result for query: %s", test.q) + t.Fatalf("unexpected result for query: %s (%#v)", test.q, result.Results[0].(*pilosa.Row).Columns()) } }) } @@ -2025,19 +2026,19 @@ func TestExecutor_Execute_Range_BSIGroup_Deprecated(t *testing.T) { t.Fatal(err) } - if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10)); err != nil { + if _, err := idx.CreateField("foo", pilosa.OptFieldTypeInt(10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("bar", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0)); err != nil { + if _, err := idx.CreateField("other", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } - if _, err := idx.CreateField("edge", pilosa.OptFieldTypeInt(-100)); err != nil { + if _, err := idx.CreateField("edge", pilosa.OptFieldTypeInt(-100, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } @@ -2795,7 +2796,7 @@ func TestExecutor_Execute_ClearRow(t *testing.T) { defer c.Close() hldr := test.Holder{Holder: c[0].Server.Holder()} index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{TrackExistence: true}) - _, err := index.CreateField("f", pilosa.OptFieldTypeInt(0)) + _, err := index.CreateField("f", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)) if err != nil { t.Fatal(err) } diff --git a/field.go b/field.go index ec7a1ccd0..9a33812c3 100644 --- a/field.go +++ b/field.go @@ -130,13 +130,15 @@ func OptFieldTypeSet(cacheType string, cacheSize uint32) FieldOption { // OptFieldTypeInt is a functional option on FieldOptions // used to specify the field as being type `int` and to // provide any respective configuration values. -func OptFieldTypeInt(base int64) FieldOption { +func OptFieldTypeInt(base, min, max int64) FieldOption { return func(fo *FieldOptions) error { if fo.Type != "" { return errors.Errorf("field type is already set to: %s", fo.Type) } fo.Type = FieldTypeInt fo.Base = base + fo.Min = min + fo.Max = max fo.BitDepth = 1 return nil } @@ -485,16 +487,21 @@ func (f *Field) loadMeta() error { } } - // Convert min/max from deprecated v1 int type. - if pb.Min != 0 || pb.Max != 0 { + // Initialize "base" to "min" when upgrading from v1 BSI format. + if pb.BitDepth == 0 { pb.Base = pb.Min - pb.BitDepth = uint64(bitDepth(uint64(pb.Max - pb.Min))) + pb.BitDepth = uint64(bitDepthInt64(pb.Max - pb.Min)) + if pb.BitDepth == 0 { + pb.BitDepth = 1 + } } // Copy metadata fields. f.options.Type = pb.Type f.options.CacheType = pb.CacheType f.options.CacheSize = pb.CacheSize + f.options.Min = pb.Min + f.options.Max = pb.Max f.options.Base = pb.Base f.options.BitDepth = uint(pb.BitDepth) f.options.TimeQuantum = TimeQuantum(pb.TimeQuantum) @@ -540,6 +547,8 @@ func (f *Field) applyOptions(opt FieldOptions) error { f.options.CacheSize = opt.CacheSize } } + f.options.Min = 0 + f.options.Max = 0 f.options.Base = 0 f.options.BitDepth = 0 f.options.TimeQuantum = "" @@ -548,6 +557,8 @@ func (f *Field) applyOptions(opt FieldOptions) error { f.options.Type = opt.Type f.options.CacheType = CacheTypeNone f.options.CacheSize = 0 + f.options.Min = opt.Min + f.options.Max = opt.Max f.options.Base = opt.Base f.options.BitDepth = opt.BitDepth f.options.TimeQuantum = "" @@ -557,6 +568,8 @@ func (f *Field) applyOptions(opt FieldOptions) error { bsig := &bsiGroup{ Name: f.name, Type: bsiGroupTypeInt, + Min: opt.Min, + Max: opt.Max, Base: opt.Base, BitDepth: opt.BitDepth, } @@ -571,6 +584,8 @@ func (f *Field) applyOptions(opt FieldOptions) error { f.options.Type = opt.Type f.options.CacheType = CacheTypeNone f.options.CacheSize = 0 + f.options.Min = 0 + f.options.Max = 0 f.options.Base = 0 f.options.BitDepth = 0 f.options.Keys = opt.Keys @@ -584,6 +599,8 @@ func (f *Field) applyOptions(opt FieldOptions) error { f.options.Type = FieldTypeBool f.options.CacheType = CacheTypeNone f.options.CacheSize = 0 + f.options.Min = 0 + f.options.Max = 0 f.options.Base = 0 f.options.BitDepth = 0 f.options.TimeQuantum = "" @@ -995,7 +1012,7 @@ func (f *Field) Value(columnID uint64) (value int64, exists bool, err error) { // SetValue sets a field value for a column. func (f *Field) SetValue(columnID uint64, value int64) (changed bool, err error) { - // Fetch bsiGroup. + // Fetch bsiGroup & validate min/max. bsig := f.bsiGroup(f.name) if bsig == nil { return false, ErrBSIGroupNotFound @@ -1003,9 +1020,10 @@ func (f *Field) SetValue(columnID uint64, value int64) (changed bool, err error) // Determine base value to store. baseValue := int64(value - bsig.Base) + requiredBitDepth := bitDepthInt64(baseValue) // Increase bit depth value if the unsigned value is greater. - if value < bsig.Min() || value > bsig.Max() { + if requiredBitDepth > bsig.BitDepth { if err := func() error { f.mu.Lock() defer f.mu.Unlock() @@ -1099,7 +1117,7 @@ func (f *Field) Range(name string, op pql.Token, predicate int64) (*Row, error) bsig := f.bsiGroup(name) if bsig == nil { return nil, ErrBSIGroupNotFound - } else if predicate < bsig.Min() || predicate > bsig.Max() { + } else if predicate < bsig.Min || predicate > bsig.Max { return nil, nil } @@ -1365,6 +1383,8 @@ func encodeFieldOptions(o *FieldOptions) *internal.FieldOptions { Type: o.Type, CacheType: o.CacheType, CacheSize: o.CacheSize, + Min: o.Min, + Max: o.Max, Base: o.Base, BitDepth: uint64(o.BitDepth), TimeQuantum: string(o.TimeQuantum), @@ -1454,20 +1474,12 @@ func isValidBSIGroupType(v string) bool { type bsiGroup struct { Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` + Min int64 `json:"min,omitempty"` + Max int64 `json:"max,omitempty"` Base int64 `json:"base,omitempty"` BitDepth uint `json:"bitDepth,omitempty"` } -// Min returns the lowest possible value for the group based on current bit depth. -func (b *bsiGroup) Min() int64 { - return b.Base - (1 << b.BitDepth) + 1 -} - -// Max returns the highest possible value for the group based on current bit depth. -func (b *bsiGroup) Max() int64 { - return b.Base + (1 << b.BitDepth) - 1 -} - // baseValue adjusts the value to align with the range for Field for a certain // operation type. // Note: There is an edge case for GT and LT where this returns a baseValue @@ -1481,7 +1493,7 @@ func (b *bsiGroup) Max() int64 { // Executor.executeBSIGroupRangeShard() takes this into account and returns // `frag.FieldNotNull(bsig.BitDepth())` in such instances. func (b *bsiGroup) baseValue(op pql.Token, value int64) (baseValue int64, outOfRange bool) { - min, max := b.Min(), b.Max() + min, max := b.bitDepthMin(), b.bitDepthMax() if op == pql.GT || op == pql.GTE { if value > max { @@ -1507,23 +1519,20 @@ func (b *bsiGroup) baseValue(op pql.Token, value int64) (baseValue int64, outOfR } // baseValueBetween adjusts the min/max value to align with the range for Field. -func (b *bsiGroup) baseValueBetween(min, max int64) (baseValueMin, baseValueMax int64, outOfRange bool) { - bsiMin, bsiMax := b.Min(), b.Max() +func (b *bsiGroup) baseValueBetween(lo, hi int64) (baseValueLo, baseValueHi int64, outOfRange bool) { + min, max := b.bitDepthMin(), b.bitDepthMax() + if hi < min || lo > max { + return 0, 0, true + } - if max < bsiMin || min > bsiMax { - return baseValueMin, baseValueMax, true + // Limit lo/hi to possible bit range. + if lo < min { + lo = min } - // Adjust min/max to range. - if min > bsiMin { - baseValueMin = int64(min - b.Base) + if hi > max { + hi = max } - // Make sure the high value of the BETWEEN does not exceed BitDepth. - if max > bsiMax { - baseValueMax = int64(bsiMax - b.Base) - } else if max > bsiMin { - baseValueMax = int64(max - b.Base) - } - return baseValueMin, baseValueMax, false + return lo - b.Base, hi - b.Base, false } func (b *bsiGroup) validate() error { @@ -1535,6 +1544,16 @@ func (b *bsiGroup) validate() error { return nil } +// bitDepthMin returns the minimum value possible for the current bit depth. +func (b *bsiGroup) bitDepthMin() int64 { + return b.Base - (1 << b.BitDepth) + 1 +} + +// bitDepthMax returns the maximum value possible for the current bit depth. +func (b *bsiGroup) bitDepthMax() int64 { + return b.Base + (1 << b.BitDepth) - 1 +} + // Cache types. const ( CacheTypeLRU = "lru" diff --git a/field_internal_test.go b/field_internal_test.go index aa8692916..13851a493 100644 --- a/field_internal_test.go +++ b/field_internal_test.go @@ -15,7 +15,9 @@ package pilosa import ( + "fmt" "io/ioutil" + "math" "os" "reflect" "testing" @@ -32,19 +34,24 @@ func TestBSIGroup_BaseValue(t *testing.T) { Type: bsiGroupTypeInt, Base: -100, BitDepth: 10, + Min: -1000, + Max: 1000, } b1 := &bsiGroup{ Name: "b1", Type: bsiGroupTypeInt, Base: 0, BitDepth: 8, + Min: -255, + Max: 255, } - b2 := &bsiGroup{ Name: "b2", Type: bsiGroupTypeInt, Base: 100, BitDepth: 11, + Min: math.MinInt64, + Max: math.MaxInt64, } t.Run("Normal Condition", func(t *testing.T) { @@ -104,10 +111,12 @@ func TestBSIGroup_BaseValue(t *testing.T) { {b2, pql.EQ, 105, 5, false}, {b2, pql.EQ, 1105, 1005, false}, } { - bv, oor := tt.f.baseValue(tt.op, tt.val) - if oor != tt.expOutOfRange || !reflect.DeepEqual(bv, tt.expBaseValue) { - t.Errorf("%d. %s) baseValue(%s, %v)=(%v, %v), expected (%v, %v)", i, tt.f.Name, tt.op, tt.val, bv, oor, tt.expBaseValue, tt.expOutOfRange) - } + t.Run(fmt.Sprint(i), func(t *testing.T) { + bv, oor := tt.f.baseValue(tt.op, tt.val) + if oor != tt.expOutOfRange || !reflect.DeepEqual(bv, tt.expBaseValue) { + t.Errorf("%s) baseValue(%s, %v)=(%v, %v), expected (%v, %v)", tt.f.Name, tt.op, tt.val, bv, oor, tt.expBaseValue, tt.expOutOfRange) + } + }) } }) diff --git a/field_test.go b/field_test.go index 72e700211..d73af7ada 100644 --- a/field_test.go +++ b/field_test.go @@ -16,6 +16,7 @@ package pilosa_test import ( "io/ioutil" + "math" "testing" "github.com/google/go-cmp/cmp" @@ -30,7 +31,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0)) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)) if err != nil { t.Fatal(err) } @@ -63,7 +64,7 @@ func TestField_SetValue(t *testing.T) { idx := test.MustOpenIndex() defer idx.Close() - f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0)) + f, err := idx.CreateField("f", pilosa.OptFieldTypeInt(0, math.MinInt64, math.MaxInt64)) if err != nil { t.Fatal(err) } diff --git a/fragment.go b/fragment.go index a67ea7875..d9b9e8128 100644 --- a/fragment.go +++ b/fragment.go @@ -1273,7 +1273,7 @@ func (f *fragment) rangeBetweenUnsigned(filter *Row, bitDepth uint, predicateMin } } - // LTE predicateMin + // LTE predicateMax // If bit is zero then remove all set bits not in excluded bitmap. if bit2 == 0 { filter = filter.Difference(row.Difference(keep2)) @@ -2492,9 +2492,9 @@ func upgradeRoaringBSIv2(f *fragment, bitDepth uint) (string, error) { f.storage.ForEach(func(i uint64) { rowID, columnID := i/ShardWidth, (f.shard*ShardWidth)+(i%ShardWidth) if rowID == uint64(bitDepth) { - other.Add(pos(bsiExistsBit, columnID)) // move exists bit to beginning + _, _ = other.Add(pos(bsiExistsBit, columnID)) // move exists bit to beginning } else { - other.Add(pos(rowID+bsiOffsetBit, columnID)) // move other bits up + _, _ = other.Add(pos(rowID+bsiOffsetBit, columnID)) // move other bits up } }) }() diff --git a/http/client_test.go b/http/client_test.go index 7fa398efa..2146d2ff7 100644 --- a/http/client_test.go +++ b/http/client_test.go @@ -705,7 +705,7 @@ func TestClient_ImportKeys(t *testing.T) { // Load bitmap into cache to ensure cache gets updated. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{Keys: true}) - field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) + field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(0, -100, 100)) if err != nil { t.Fatal(err) } @@ -790,7 +790,7 @@ func TestClient_ImportValue(t *testing.T) { // Load bitmap into cache to ensure cache gets updated. index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{}) - field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) + field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(0, -100, 100)) if err != nil { t.Fatal(err) } @@ -933,7 +933,7 @@ func TestClient_ImportExistence(t *testing.T) { fldName := "fint" index := hldr.MustCreateIndexIfNotExists(idxName, pilosa.IndexOptions{TrackExistence: true}) - field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) + field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(0, -100, 100)) if err != nil { t.Fatal(err) } diff --git a/http/handler.go b/http/handler.go index 2271d5ec4..16a44c975 100644 --- a/http/handler.go +++ b/http/handler.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "io/ioutil" + "math" "net" "net/http" _ "net/http/pprof" // Imported for its side-effect of registering pprof endpoints with the server. @@ -764,7 +765,7 @@ func (h *Handler) handlePostField(w http.ResponseWriter, r *http.Request) { } else if v := req.Options.Min; v != nil { base = *v } - fos = append(fos, pilosa.OptFieldTypeInt(base)) + fos = append(fos, pilosa.OptFieldTypeInt(base, math.MinInt64, math.MaxInt64)) case pilosa.FieldTypeTime: fos = append(fos, pilosa.OptFieldTypeTime(*req.Options.TimeQuantum, req.Options.NoStandardView)) case pilosa.FieldTypeMutex: diff --git a/index_test.go b/index_test.go index 61f138de9..381a3adce 100644 --- a/index_test.go +++ b/index_test.go @@ -16,6 +16,7 @@ package pilosa_test import ( "io/ioutil" + "math" "reflect" "testing" @@ -93,7 +94,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.OptFieldTypeInt(10)); err != nil { + if f, err := index.CreateField("f", pilosa.OptFieldTypeInt(10, math.MinInt64, math.MaxInt64)); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(f.Type(), pilosa.FieldTypeInt) { t.Fatalf("unexpected type: %#v", f.Type()) diff --git a/internal/private.proto b/internal/private.proto index 01890652d..a327b835e 100644 --- a/internal/private.proto +++ b/internal/private.proto @@ -8,17 +8,16 @@ message IndexMeta { } message FieldOptions { - string Type = 8; + string Type = 8; string CacheType = 3; uint32 CacheSize = 4; string TimeQuantum = 5; - bool Keys = 11; - bool NoStandardView = 12; - int64 Base = 13; - uint64 BitDepth = 14; - - int64 Min = 9 [deprecated=true]; - int64 Max = 10 [deprecated=true]; + int64 Min = 9; + int64 Max = 10; + bool Keys = 11; + bool NoStandardView = 12; + int64 Base = 13; + uint64 BitDepth = 14; } message ImportResponse { @@ -43,154 +42,154 @@ message Cache { } message MaxShards { - map Standard = 1; + map Standard = 1; } message CreateShardMessage { - string Index = 1; - string Field = 3; - uint64 Shard = 2; + string Index = 1; + string Field = 3; + uint64 Shard = 2; } message DeleteIndexMessage { - string Index = 1; + string Index = 1; } message CreateIndexMessage { - string Index = 1; - IndexMeta Meta = 2; + string Index = 1; + IndexMeta Meta = 2; } message CreateFieldMessage { - string Index = 1; - string Field = 2; - FieldOptions Meta = 3; + string Index = 1; + string Field = 2; + FieldOptions Meta = 3; } message DeleteFieldMessage { - string Index = 1; - string Field = 2; + string Index = 1; + string Field = 2; } message DeleteAvailableShardMessage { - string Index = 1; - string Field = 2; - uint64 ShardID = 3; + string Index = 1; + string Field = 2; + uint64 ShardID = 3; } message Field { - string Name = 1; - FieldOptions Meta = 2; - repeated string Views = 3; + string Name = 1; + FieldOptions Meta = 2; + repeated string Views = 3; } message Schema { - repeated Index Indexes = 1; + repeated Index Indexes = 1; } message Index { - string Name = 1; - repeated Field Fields = 4; + string Name = 1; + repeated Field Fields = 4; } message URI { - string Scheme = 1; - string Host = 2; - uint32 Port = 3; + string Scheme = 1; + string Host = 2; + uint32 Port = 3; } message Node { - string ID = 1; - URI URI = 2; - bool IsCoordinator = 3; - string State = 4; + string ID = 1; + URI URI = 2; + bool IsCoordinator = 3; + string State = 4; } message NodeStateMessage { - string NodeID = 1; - string State = 2; + string NodeID = 1; + string State = 2; } message NodeEventMessage { - uint32 Event = 1; - Node Node = 2; + uint32 Event = 1; + Node Node = 2; } message NodeStatus { - Node Node = 1; - Schema Schema = 3; - repeated IndexStatus Indexes = 4; + Node Node = 1; + Schema Schema = 3; + repeated IndexStatus Indexes = 4; } message IndexStatus { - string Name = 1; - repeated FieldStatus Fields = 2; + string Name = 1; + repeated FieldStatus Fields = 2; } message FieldStatus { - string Name = 1; - repeated uint64 AvailableShards = 2; + string Name = 1; + repeated uint64 AvailableShards = 2; } message ClusterStatus { - string ClusterID = 1; - string State = 2; - repeated Node Nodes = 3; + string ClusterID = 1; + string State = 2; + repeated Node Nodes = 3; } message BSIGroup { - string Name = 1; - string Type = 2; - int64 Min = 3; - int64 Max = 4; + string Name = 1; + string Type = 2; + int64 Min = 3; + int64 Max = 4; } message CreateViewMessage { - string Index = 1; - string Field = 2; - string View = 3; + string Index = 1; + string Field = 2; + string View = 3; } message DeleteViewMessage { - string Index = 1; - string Field = 2; - string View = 3; + string Index = 1; + string Field = 2; + string View = 3; } message ResizeInstruction { - int64 JobID = 1; - Node Node = 2; - Node Coordinator = 3; - repeated ResizeSource Sources = 4; - NodeStatus NodeStatus = 7; - ClusterStatus ClusterStatus = 6; + int64 JobID = 1; + Node Node = 2; + Node Coordinator = 3; + repeated ResizeSource Sources = 4; + NodeStatus NodeStatus = 7; + ClusterStatus ClusterStatus = 6; } message ResizeSource { - Node Node = 1; - string Index = 2; - string Field = 3; - string View = 4; - uint64 Shard = 5; + Node Node = 1; + string Index = 2; + string Field = 3; + string View = 4; + uint64 Shard = 5; } message ResizeInstructionComplete { - int64 JobID = 1; - Node Node = 2; - string Error = 3; + int64 JobID = 1; + Node Node = 2; + string Error = 3; } message SetCoordinatorMessage { - Node New = 1; + Node New = 1; } message UpdateCoordinatorMessage { - Node New = 1; + Node New = 1; } message Topology { - string ClusterID = 1; - repeated string NodeIDs = 2; + string ClusterID = 1; + repeated string NodeIDs = 2; } message RecalculateCaches {} diff --git a/server/handler_test.go b/server/handler_test.go index fc832269a..86398eb01 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -230,7 +230,7 @@ func TestHandler_Endpoints(t *testing.T) { t.Run("ImportRoaringFieldTypeFail", func(t *testing.T) { // Roaring import into a non-set field should fail. - if _, err := i0.CreateFieldIfNotExists("int-field", pilosa.OptFieldTypeInt(0, 1)); err != nil { + if _, err := i0.CreateFieldIfNotExists("int-field", pilosa.OptFieldTypeInt(0, 0, 1)); err != nil { t.Fatal(err) } w := httptest.NewRecorder()