diff --git a/api_test.go b/api_test.go index 02e8a182f..3942a7337 100644 --- a/api_test.go +++ b/api_test.go @@ -361,7 +361,7 @@ func TestAPI_ImportValue(t *testing.T) { if err != nil { t.Fatalf("creating index: %v", err) } - fld, err := m1.API.CreateField(ctx, index, field, pilosa.OptFieldTypeDecimal(1)) + _, err = m1.API.CreateField(ctx, index, field, pilosa.OptFieldTypeDecimal(1)) if err != nil { t.Fatalf("creating field: %v", err) } @@ -386,66 +386,14 @@ func TestAPI_ImportValue(t *testing.T) { t.Fatal(err) } - pql := fmt.Sprintf("Row(%s>6)", field) + query := fmt.Sprintf("Row(%s>6)", field) // Query node0. - if res, err := m0.API.Query(ctx, &pilosa.QueryRequest{Index: index, Query: pql}); err != nil { + if res, err := m0.API.Query(ctx, &pilosa.QueryRequest{Index: index, Query: query}); err != nil { t.Fatal(err) } else if ids := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(ids, colIDs[6:]) { t.Fatalf("unexpected column keys: %+v", ids) } - - sum, count, err := fld.FloatSum(nil, field) - if err != nil { - t.Fatalf("getting floatsum: %v", err) - } else if sum != 0.1+1.1+2.1+3.1+4.1+5.1+6.1+7.1+8.1+9.1 { - t.Fatalf("unexpected sum: %f", sum) - } else if count != 10 { - t.Fatalf("unexpected count: %d", count) - } - - min, count, err := fld.FloatMin(nil, field) - if err != nil { - t.Fatalf("getting floatmin: %v", err) - } else if min != 0.1 { - t.Fatalf("unexpected min: %f", min) - } else if count != 1 { - t.Fatalf("unexpected count: %d", count) - } - - max, count, err := fld.FloatMax(nil, field) - if err != nil { - t.Fatalf("getting floatmax: %v", err) - } else if max != 9.1 { - t.Fatalf("unexpected max: %f", max) - } else if count != 1 { - t.Fatalf("unexpected count: %d", count) - } - - val, exists, err := fld.FloatValue(1) - if err != nil { - t.Fatalf("unepxected err getting floatvalue") - } else if !exists { - t.Fatalf("column 1 should exist") - } else if val != 1.1 { - t.Fatalf("unexpected floatvalue %f", val) - } - - changed, err := fld.SetFloatValue(11, 11.1) - if err != nil { - t.Fatalf("setting float value: %v", err) - } else if !changed { - t.Fatalf("expected change") - } - - val, exists, err = fld.FloatValue(11) - if err != nil { - t.Fatalf("getting float val: %v", err) - } else if !exists { - t.Fatalf("should exist") - } else if val != 11.1 { - t.Fatalf("unexpected val: %f", 11.1) - } }) t.Run("ValDecimalFieldNegativeScale", func(t *testing.T) { diff --git a/cache.go b/cache.go index 1c00ed6fd..ec7b3aa85 100644 --- a/cache.go +++ b/cache.go @@ -121,7 +121,8 @@ func (c *lruCache) Top() []bitmapPair { Count: n, }) } - sort.Sort(bitmapPairs(a)) + pairs := bitmapPairs(a) + sort.Sort(&pairs) return a } @@ -137,9 +138,10 @@ var _ cache = &lruCache{} // rankCache represents a cache with sorted entries. type rankCache struct { - mu sync.Mutex - entries map[uint64]uint64 - rankings []bitmapPair // cached, ordered list + mu sync.Mutex + entries map[uint64]uint64 + rankings bitmapPairs // cached, ordered list + rankingsRead bool updateN int updateTime time.Time @@ -214,12 +216,15 @@ func (c *rankCache) Len() int { func (c *rankCache) IDs() []uint64 { c.mu.Lock() defer c.mu.Unlock() - a := make([]uint64, 0, len(c.entries)) - for id := range c.entries { - a = append(a, id) + if len(c.entries) == 0 { + return nil } - sort.Sort(uint64Slice(a)) - return a + ids := make([]uint64, 0, len(c.entries)) + for id := range c.entries { + ids = append(ids, id) + } + sort.Sort(uint64Slice(ids)) + return ids } // Invalidate recalculates the entries by rank. @@ -248,18 +253,26 @@ func (c *rankCache) invalidate() { } func (c *rankCache) recalculate() { + if c.rankingsRead { + c.rankings = nil + c.rankingsRead = false + } + // Convert cache to a sorted list. - rankings := make([]bitmapPair, 0, len(c.entries)) + rankings := c.rankings[:0] + if cap(rankings) < len(c.entries) { + rankings = make([]bitmapPair, 0, len(c.entries)) + } for id, cnt := range c.entries { rankings = append(rankings, bitmapPair{ ID: id, Count: cnt, }) } - sort.Sort(bitmapPairs(rankings)) + c.rankings = rankings + sort.Sort(&c.rankings) // Store the count of the item at the threshold index. - c.rankings = rankings length := len(c.rankings) c.stats.Gauge(MetricRankCacheLength, float64(length), 1.0) @@ -290,7 +303,13 @@ func (c *rankCache) SetStats(s stats.StatsClient) { } // Top returns an ordered list of pairs. -func (c *rankCache) Top() []bitmapPair { return c.rankings } +func (c *rankCache) Top() []bitmapPair { + c.mu.Lock() + defer c.mu.Unlock() + + c.rankingsRead = true + return c.rankings +} // WriteTo writes the cache to w. func (c *rankCache) WriteTo(w io.Writer) (n int64, err error) { @@ -314,9 +333,9 @@ type bitmapPair struct { // bitmapPairs is a sortable list of BitmapPair objects. type bitmapPairs []bitmapPair -func (p bitmapPairs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p bitmapPairs) Len() int { return len(p) } -func (p bitmapPairs) Less(i, j int) bool { return p[i].Count > p[j].Count } +func (p *bitmapPairs) Swap(i, j int) { (*p)[i], (*p)[j] = (*p)[j], (*p)[i] } +func (p *bitmapPairs) Len() int { return len(*p) } +func (p *bitmapPairs) Less(i, j int) bool { return (*p)[i].Count > (*p)[j].Count } // Pair holds an id/count pair. type Pair struct { diff --git a/executor.go b/executor.go index c54ad6d8d..6891072ee 100644 --- a/executor.go +++ b/executor.go @@ -2427,18 +2427,11 @@ func (e *executor) executeRowShard(ctx context.Context, index string, c *pql.Cal if err != nil { return nil, errors.New("Row() argument required: field") } - f := e.Holder.Field(index, fieldName) + f := idx.Field(fieldName) if f == nil { return nil, ErrFieldNotFound } - rowID, rowOK, rowErr := c.UintArg(fieldName) - if rowErr != nil { - return nil, fmt.Errorf("Row() error with arg for row: %v", rowErr) - } else if !rowOK { - return nil, fmt.Errorf("Row() must specify %v", rowLabel) - } - // Parse "from" time, if set. var fromTime time.Time if v, ok := c.Args["from"]; ok { @@ -2455,8 +2448,33 @@ func (e *executor) executeRowShard(ctx context.Context, index string, c *pql.Cal } } + timeNotSet := fromTime.IsZero() && toTime.IsZero() + + // This is workaround to support pql.ASSIGN ('=') as condition ('==') for int and decimal fields + if c.Name == "Row" && timeNotSet && + (f.Type() == FieldTypeInt || f.Type() == FieldTypeDecimal) { + // re-write args as conditions for fieldName + for k, v := range c.Args { + if _, ok := v.(*pql.Condition); k == fieldName && !ok { + c.Args[k] = &pql.Condition{ + Op: pql.EQ, + Value: v, + } + + return e.executeRowBSIGroupShard(ctx, index, c, shard) + } + } + } + + rowID, rowOK, rowErr := c.UintArg(fieldName) + if rowErr != nil { + return nil, fmt.Errorf("Row() error with arg for row: %v", rowErr) + } else if !rowOK { + return nil, fmt.Errorf("Row() must specify %v", rowLabel) + } + // Simply return row if times are not set. - if c.Name == "Row" && fromTime.IsZero() && toTime.IsZero() { + if c.Name == "Row" && timeNotSet { frag := e.Holder.fragment(index, fieldName, viewStandard, shard) if frag == nil { return NewRow(), nil diff --git a/executor_test.go b/executor_test.go index cb878cb15..bc990dff9 100644 --- a/executor_test.go +++ b/executor_test.go @@ -3027,6 +3027,121 @@ func TestExecutor_Execute_Remote_Row(t *testing.T) { test.CheckGroupBy(t, expected, results) } }) + + t.Run("Row on ints with ASSIGN condition", func(t *testing.T) { + _, err := c[0].API.CreateIndex(context.Background(), "intidx", pilosa.IndexOptions{}) + if err != nil { + t.Fatalf("creating index: %v", err) + } + + _, err = c[0].API.CreateField(context.Background(), "intidx", "gint", pilosa.OptFieldTypeInt(-1000, 1000)) + if err != nil { + t.Fatalf("creating field: %v", err) + } + if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "intidx", Query: ` + Set(1000, gint=1) + Set(2000, gint=2) + Set(3000, gint=3) + `}); err != nil { + t.Fatalf("querying remote: %v", err) + } + + if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{ + Index: "intidx", + Query: `Row(gint=2)Row(gint==1)`, + }); err != nil { + t.Fatalf("Row querying: %v", err) + } else { + + row0, row1 := res.Results[0].(*pilosa.Row), res.Results[1].(*pilosa.Row) + if len(row0.Columns()) != 1 || len(row1.Columns()) != 1 { + t.Fatalf(`Expected: []uint64{2000} []uint64{1000}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + if row0.Columns()[0] != 2000 || row1.Columns()[0] != 1000 { + t.Fatalf(`Expected: []uint64{2000} []uint64{1000}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + } + }) + + t.Run("Row on decimals with ASSIGN condition", func(t *testing.T) { + _, err := c[0].API.CreateIndex(context.Background(), "decidx", pilosa.IndexOptions{}) + if err != nil { + t.Fatalf("creating index: %v", err) + } + + _, err = c[0].API.CreateField(context.Background(), "decidx", "fdec", pilosa.OptFieldTypeDecimal(0)) + if err != nil { + t.Fatalf("creating field: %v", err) + } + if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "decidx", Query: ` + Set(11, fdec=1.1) + Set(22, fdec=2.2) + Set(33, fdec=3.3) + `}); err != nil { + t.Fatalf("querying remote: %v", err) + } + + if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{ + Index: "decidx", + Query: `Row(fdec=2.2)Row(fdec==1.1)`, + }); err != nil { + t.Fatalf("Row querying: %v", err) + } else { + row0, row1 := res.Results[0].(*pilosa.Row), res.Results[1].(*pilosa.Row) + if len(row0.Columns()) != 1 || len(row1.Columns()) != 1 { + t.Fatalf(`Expected: []uint64{22} []uint64{11}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + if row0.Columns()[0] != 22 || row1.Columns()[0] != 11 { + t.Fatalf(`Expected: []uint64{22} []uint64{11}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + } + }) + + t.Run("Row on foreign key with ASSIGN condition", func(t *testing.T) { + _, err := c[0].API.CreateIndex(context.Background(), "parent", pilosa.IndexOptions{Keys: true}) + if err != nil { + t.Fatalf("creating index: %v", err) + } + _, err = c[0].API.CreateField(context.Background(), "parent", "general", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize)) + if err != nil { + t.Fatalf("creating field: %v", err) + } + _, err = c[0].API.CreateIndex(context.Background(), "child", pilosa.IndexOptions{Keys: false}) + if err != nil { + t.Fatalf("creating index: %v", err) + } + _, err = c[0].API.CreateField(context.Background(), "child", "parentid", + pilosa.OptFieldForeignIndex("parent"), + pilosa.OptFieldTypeInt(-9223372036854775808, 9223372036854775807), + ) + if err != nil { + t.Fatalf("creating field: %v", err) + } + + if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "child", Query: ` + Set(1, parentid="one") + Set(2, parentid="two") + Set(3, parentid="three") + `}); err != nil { + t.Fatalf("querying remote: %v", err) + } + + if res, err := c[1].API.Query(context.Background(), &pilosa.QueryRequest{ + Index: "child", + Query: `Row(parentid="two")Row(parentid=="one")`, + }); err != nil { + t.Fatalf("Row querying: %v", err) + } else { + + row0, row1 := res.Results[0].(*pilosa.Row), res.Results[1].(*pilosa.Row) + if len(row0.Columns()) != 1 || len(row1.Columns()) != 1 { + t.Fatalf(`Expected: []uint64{1} []uint64{0}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + if row0.Columns()[0] != 2 || row1.Columns()[0] != 1 { + t.Fatalf(`Expected: []uint64{1} []uint64{0}, Got: %+v %+v`, row0.Columns(), row1.Columns()) + } + } + }) } // Ensure executor returns an error if too many writes are in a single request. diff --git a/field.go b/field.go index 34352655d..b746e1800 100644 --- a/field.go +++ b/field.go @@ -1376,37 +1376,6 @@ func (f *Field) StringValue(columnID uint64) (value string, exists bool, err err return value, exists, err } -// FloatValue reads an integer field value for a column, and converts -// it to a float based on the configured scale. -func (f *Field) FloatValue(columnID uint64) (value float64, exists bool, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return 0, false, ErrBSIGroupNotFound - } - - val, exists, err := f.Value(columnID) - if exists { - value = float64(val) / math.Pow10(int(bsig.Scale)) - } - return value, exists, err -} - -// DecimalValue reads a decimal field value for a column, and converts -// it to a pql.Decimal based on the configured scale. -func (f *Field) DecimalValue(columnID uint64) (value pql.Decimal, exists bool, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return value, false, ErrBSIGroupNotFound - } - - val, exists, err := f.Value(columnID) - if exists { - value.Value = val - value.Scale = bsig.Scale - } - return value, exists, err -} - // Value reads a field value for a column. func (f *Field) Value(columnID uint64) (value int64, exists bool, err error) { bsig := f.bsiGroup(f.name) @@ -1429,18 +1398,6 @@ func (f *Field) Value(columnID uint64) (value int64, exists bool, err error) { return int64(v) + bsig.Base, true, nil } -// SetFloatValue takes a floating point value, and converts it to an -// integer based on the field's configured scale, before setting that -// integer via SetValue. -func (f *Field) SetFloatValue(columnID uint64, value float64) (changed bool, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return false, ErrBSIGroupNotFound - } - val := int64(float64(value) * math.Pow10(int(bsig.Scale))) - return f.SetValue(columnID, val) -} - // SetValue sets a field value for a column. func (f *Field) SetValue(columnID uint64, value int64) (changed bool, err error) { // Fetch bsiGroup & validate min/max. @@ -1506,98 +1463,6 @@ func (f *Field) ClearValue(columnID uint64) (changed bool, err error) { return false, nil } -// FloatSum performs a Sum query and converts the result to a float -// based on the field's configured scale. -func (f *Field) FloatSum(filter *Row, name string) (sum float64, count int64, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - sumI, count, err := f.Sum(filter, name) - if err == nil { - sum = float64(sumI) / math.Pow10(int(bsig.Scale)) - } - return sum, count, err - -} - -// Sum returns the sum and count for a field. -// An optional filtering row can be provided. -func (f *Field) Sum(filter *Row, name string) (sum, count int64, err error) { - bsig := f.bsiGroup(name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - view := f.view(viewBSIGroupPrefix + name) - if view == nil { - return 0, 0, nil - } - - vsum, vcount, err := view.sum(filter, bsig.BitDepth) - if err != nil { - return 0, 0, err - } - return int64(vsum) + (int64(vcount) * bsig.Base), int64(vcount), nil -} - -// FloatMin performs a Min query and converts the result to a float -// based on the field's configured scale. -// TODO: this and Min are probably worthless -func (f *Field) FloatMin(filter *Row, name string) (min float64, count int64, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - minI, count, err := f.Min(filter, name) - if err == nil { - min = float64(minI) / math.Pow10(int(bsig.Scale)) - } - return min, count, err -} - -// Min returns the min for a field. -// An optional filtering row can be provided. -func (f *Field) Min(filter *Row, name string) (min, count int64, err error) { - bsig := f.bsiGroup(name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - view := f.view(viewBSIGroupPrefix + name) - if view == nil { - return 0, 0, nil - } - - vmin, vcount, err := view.min(filter, bsig.BitDepth) - if err != nil { - return 0, 0, err - } - return int64(vmin) + bsig.Base, int64(vcount), nil -} - -// FloatMax performs a max query and converts the result to a float -// based on the field's configured scale. -// -// TODO, this isn't really used, because it's kind of useless. It will -// only get the max among shards on this node, but all query execution -// already happens at the shard level and bypasses this entirely -// calling fragment.max instead. -func (f *Field) FloatMax(filter *Row, name string) (max float64, count int64, err error) { - bsig := f.bsiGroup(f.name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - maxI, count, err := f.Max(filter, name) - if err == nil { - max = float64(maxI) / math.Pow10(int(bsig.Scale)) - } - return max, count, err -} - func (f *Field) MaxForShard(shard uint64, filter *Row) (ValCount, error) { bsig := f.bsiGroup(f.name) if bsig == nil { @@ -1667,26 +1532,6 @@ func (f *Field) MinForShard(shard uint64, filter *Row) (ValCount, error) { return valCount, nil } -// Max returns the max for a field. -// An optional filtering row can be provided. -func (f *Field) Max(filter *Row, name string) (max, count int64, err error) { - bsig := f.bsiGroup(name) - if bsig == nil { - return 0, 0, ErrBSIGroupNotFound - } - - view := f.view(viewBSIGroupPrefix + name) - if view == nil { - return 0, 0, nil - } - - vmax, vcount, err := view.max(filter, bsig.BitDepth) - if err != nil { - return 0, 0, err - } - return int64(vmax) + bsig.Base, int64(vcount), nil -} - // Range performs a conditional operation on Field. func (f *Field) Range(name string, op pql.Token, predicate int64) (*Row, error) { // Retrieve and validate bsiGroup. diff --git a/fragment.go b/fragment.go index 5f26828ec..3258d6f00 100644 --- a/fragment.go +++ b/fragment.go @@ -1647,7 +1647,8 @@ func (f *fragment) topBitmapPairs(rowIDs []uint64) []bitmapPair { }) } } - sort.Sort(bitmapPairs(pairs)) + sortPairs := bitmapPairs(pairs) + sort.Sort(&sortPairs) return pairs } diff --git a/http/client_test.go b/http/client_test.go index fb5568658..ff543def5 100644 --- a/http/client_test.go +++ b/http/client_test.go @@ -777,7 +777,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)) + _, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) if err != nil { t.Fatal(err) } @@ -792,15 +792,6 @@ func TestClient_ImportKeys(t *testing.T) { t.Fatal(err) } - // Verify Sum. - sum, cnt, err := field.Sum(nil, fldName) - if err != nil { - t.Fatal(err) - } - if sum != 50 || cnt != 3 { - t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", sum, cnt) - } - // Verify range. queryRequest := &pilosa.QueryRequest{ Query: fmt.Sprintf(`Row(%s>10)`, fldName), @@ -823,15 +814,6 @@ func TestClient_ImportKeys(t *testing.T) { t.Fatal(err) } - // Verify Sum. - sum, cnt, err = field.Sum(nil, fldName) - if err != nil { - t.Fatal(err) - } - if sum != 30 || cnt != 2 { - t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=30, cnt=2", sum, cnt) - } - // Verify Range. queryRequest = &pilosa.QueryRequest{ Query: fmt.Sprintf(`Row(%s>10)`, fldName), @@ -928,7 +910,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)) + _, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) if err != nil { t.Fatal(err) } @@ -944,43 +926,21 @@ func TestClient_ImportValue(t *testing.T) { } // Verify Sum. - sum, cnt, err := field.Sum(nil, fldName) - if err != nil { + if resp, err := c.Query(context.Background(), "i", &pilosa.QueryRequest{Query: `Sum(field=f)`}); err != nil { t.Fatal(err) - } - if sum != 50 || cnt != 3 { - t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", sum, cnt) - } - - // Verify Min. - min, cnt, err := field.Min(nil, fldName) - if err != nil { - t.Fatal(err) - } - if min != -10 || cnt != 1 { - t.Fatalf("unexpected values: got min=%v, count=%v; expected min=-10, cnt=1", min, cnt) - } - - // Verify Min with Filter. - filter, err := field.Range(fldName, pql.GT, 40) - if err != nil { - t.Fatal(err) - } - min, cnt, err = field.Min(filter, fldName) - if err != nil { - t.Fatal(err) - } - if min != 0 || cnt != 0 { - t.Fatalf("unexpected values: got min=%v, count=%v; expected min=0, cnt=0", min, cnt) + } else if vc, ok := resp.Results[0].(pilosa.ValCount); !ok { + t.Fatalf("expected ValCount; got %T", resp.Results[0]) + } else if vc.Val != 50 || vc.Count != 3 { + t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", vc.Val, vc.Count) } // Verify Max. - max, cnt, err := field.Max(nil, fldName) - if err != nil { + if resp, err := c.Query(context.Background(), "i", &pilosa.QueryRequest{Query: `Max(field=f)`}); err != nil { t.Fatal(err) - } - if max != 40 || cnt != 1 { - t.Fatalf("unexpected values: got max=%v, count=%v; expected max=40, cnt=1", max, cnt) + } else if vc, ok := resp.Results[0].(pilosa.ValCount); !ok { + t.Fatalf("expected ValCount; got %T", resp.Results[0]) + } else if vc.Val != 40 || vc.Count != 1 { + t.Fatalf("unexpected values: got max=%v, count=%v; expected max=40, cnt=1", vc.Val, vc.Count) } // Send import request. @@ -992,34 +952,21 @@ func TestClient_ImportValue(t *testing.T) { } // Verify Sum. - sum, cnt, err = field.Sum(nil, fldName) - if err != nil { + if resp, err := c.Query(context.Background(), "i", &pilosa.QueryRequest{Query: `Sum(field=f)`}); err != nil { t.Fatal(err) - } - if sum != 20 || cnt != 1 { - t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=20, cnt=1", sum, cnt) - } - - // Verify Min with Filter. - filter, err = field.Range(fldName, pql.GT, 40) - if err != nil { - t.Fatal(err) - } - min, cnt, err = field.Min(filter, fldName) - if err != nil { - t.Fatal(err) - } - if min != 0 || cnt != 0 { - t.Fatalf("unexpected values: got min=%v, count=%v; expected min=0, cnt=0", min, cnt) + } else if vc, ok := resp.Results[0].(pilosa.ValCount); !ok { + t.Fatalf("expected ValCount; got %T", resp.Results[0]) + } else if vc.Val != 20 || vc.Count != 1 { + t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=20, cnt=1", vc.Val, vc.Count) } // Verify Max. - max, cnt, err = field.Max(nil, fldName) - if err != nil { + if resp, err := c.Query(context.Background(), "i", &pilosa.QueryRequest{Query: `Max(field=f)`}); err != nil { t.Fatal(err) - } - if max != 20 || cnt != 1 { - t.Fatalf("unexpected values: got max=%v, count=%v; expected max=20, cnt=1", max, cnt) + } else if vc, ok := resp.Results[0].(pilosa.ValCount); !ok { + t.Fatalf("expected ValCount; got %T", resp.Results[0]) + } else if vc.Val != 20 || vc.Count != 1 { + t.Fatalf("unexpected values: got max=%v, count=%v; expected max=20, cnt=1", vc.Val, vc.Count) } } @@ -1071,7 +1018,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)) + _, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100)) if err != nil { t.Fatal(err) } @@ -1087,12 +1034,12 @@ func TestClient_ImportExistence(t *testing.T) { } // Verify Sum. - sum, cnt, err := field.Sum(nil, fldName) - if err != nil { + if resp, err := c.Query(context.Background(), idxName, &pilosa.QueryRequest{Query: fmt.Sprintf(`Sum(field=%s)`, fldName)}); err != nil { t.Fatal(err) - } - if sum != 50 || cnt != 3 { - t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", sum, cnt) + } else if vc, ok := resp.Results[0].(pilosa.ValCount); !ok { + t.Fatalf("expected ValCount; got %T", resp.Results[0]) + } else if vc.Val != 50 || vc.Count != 3 { + t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", vc.Val, vc.Count) } // Verify existence. diff --git a/view.go b/view.go index a851b8653..478725e1e 100644 --- a/view.go +++ b/view.go @@ -469,62 +469,6 @@ func (v *view) clearValue(columnID uint64, bitDepth uint, value int64) (changed return frag.clearValue(columnID, bitDepth, value) } -// sum returns the sum & count of a field. -func (v *view) sum(filter *Row, bitDepth uint) (sum int64, count uint64, err error) { - for _, f := range v.allFragments() { - fsum, fcount, err := f.sum(filter, bitDepth) - if err != nil { - return sum, count, err - } - sum += fsum - count += fcount - } - return sum, count, nil -} - -// min returns the min and count of a field. -func (v *view) min(filter *Row, bitDepth uint) (min int64, count uint64, err error) { - var minHasValue bool - for _, f := range v.allFragments() { - fmin, fcount, err := f.min(filter, bitDepth) - if err != nil { - return min, count, err - } - // Don't consider a min based on zero columns. - if fcount == 0 { - continue - } - - if !minHasValue { - min = fmin - minHasValue = true - count += fcount - continue - } - - if fmin < min { - min = fmin - count += fcount - } - } - return min, count, nil -} - -// max returns the max and count of a field. -func (v *view) max(filter *Row, bitDepth uint) (max int64, count uint64, err error) { - for _, f := range v.allFragments() { - fmax, fcount, err := f.max(filter, bitDepth) - if err != nil { - return max, count, err - } - if fcount > 0 && fmax > max { - max = fmax - count += fcount - } - } - return max, count, nil -} - // rangeOp returns rows with a field value encoding matching the predicate. func (v *view) rangeOp(op pql.Token, bitDepth uint, predicate int64) (*Row, error) { r := NewRow()