mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-10 15:01:03 +00:00
Add execution for median
This commit is contained in:
parent
26fd199716
commit
d740a0cda7
2 changed files with 84 additions and 12 deletions
76
executor.go
76
executor.go
|
|
@ -830,6 +830,9 @@ func (e *executor) executeCall(ctx context.Context, qcx *Qcx, index string, c *p
|
|||
case "Limit":
|
||||
res, err := e.executeLimitCall(ctx, qcx, index, c, shards, opt)
|
||||
return res, errors.Wrapf(err, "executeLimitCall %v", shardSlice(shards))
|
||||
case "Percentile":
|
||||
res, err := e.executePercentile(ctx, qcx, index, c, shards, opt)
|
||||
return res, errors.Wrapf(err, "executePercentile %v", shardSlice(shards))
|
||||
default: // e.g. "Row", "Union", "Intersect" or anything that returns a bitmap.
|
||||
statFn()
|
||||
res, err := e.executeBitmapCall(ctx, qcx, index, c, shards, opt)
|
||||
|
|
@ -1293,6 +1296,79 @@ func (e *executor) executeMax(ctx context.Context, qcx *Qcx, index string, c *pq
|
|||
return other, nil
|
||||
}
|
||||
|
||||
// executePercentile executes a Percentile() call.
|
||||
func (e *executor) executePercentile(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shards []uint64, opt *execOptions) (_ ValCount, err error) {
|
||||
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executePercentile")
|
||||
defer span.Finish()
|
||||
|
||||
if field := c.Args["field"]; field == "" {
|
||||
return ValCount{}, errors.New("Percentile(): field required")
|
||||
}
|
||||
fieldName, _, _ := c.StringArg("field")
|
||||
|
||||
// get min
|
||||
q, _ := pql.ParseString(fmt.Sprintf(`Min(field="%s")`, fieldName))
|
||||
minCall := q.Calls[0]
|
||||
minVal, err := e.executeMin(ctx, qcx, index, minCall, shards, opt)
|
||||
if err != nil {
|
||||
return ValCount{}, errors.Wrap(err, "executing Min call for Percentile")
|
||||
}
|
||||
|
||||
// get max
|
||||
q, _ = pql.ParseString(fmt.Sprintf(`Max(field="%s")`, fieldName))
|
||||
maxCall := q.Calls[0]
|
||||
maxVal, err := e.executeMax(ctx, qcx, index, maxCall, shards, opt)
|
||||
if err != nil {
|
||||
return ValCount{}, errors.Wrap(err, "executing Max call for Percentile")
|
||||
}
|
||||
// set up reusables
|
||||
countQuery, _ := pql.ParseString("Count(Row(fld < 0))")
|
||||
countCall := countQuery.Calls[0]
|
||||
rangeQuery, _ := pql.ParseString("Row(fld < 0)")
|
||||
rangeCall := rangeQuery.Calls[0]
|
||||
|
||||
min, max := minVal.Val, maxVal.Val
|
||||
// estimate nth val, eg median when nth=0.5
|
||||
for min < max {
|
||||
possibleNthVal := (max - min) / 2
|
||||
// get left count
|
||||
rangeCall.Args[fieldName] = &pql.Condition{
|
||||
Op: pql.Token(pql.LT),
|
||||
Value: possibleNthVal,
|
||||
}
|
||||
countCall.Children = []*pql.Call{rangeCall}
|
||||
leftCountUint64, err := e.executeCount(ctx, qcx, index, countCall, shards, opt)
|
||||
if err != nil {
|
||||
return ValCount{}, errors.Wrap(err, "executing Count call L for Percentile")
|
||||
}
|
||||
leftCount := int64(leftCountUint64)
|
||||
|
||||
// get right count
|
||||
rangeCall.Args[fieldName] = &pql.Condition{
|
||||
Op: pql.Token(pql.GT),
|
||||
Value: possibleNthVal,
|
||||
}
|
||||
countCall.Children = []*pql.Call{rangeCall}
|
||||
rightCountUint64, err := e.executeCount(ctx, qcx, index, countCall, shards, opt)
|
||||
if err != nil {
|
||||
return ValCount{}, errors.Wrap(err, "executing Count call R for Percentile")
|
||||
}
|
||||
rightCount := int64(rightCountUint64)
|
||||
|
||||
// binary search
|
||||
if leftCount > rightCount {
|
||||
max = possibleNthVal - 1
|
||||
} else if leftCount < rightCount {
|
||||
min = possibleNthVal + 1
|
||||
} else {
|
||||
return ValCount{Val: possibleNthVal, Count: 1}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return ValCount{Val: min, Count: 1}, nil
|
||||
|
||||
}
|
||||
|
||||
// executeMinRow executes a MinRow() call.
|
||||
func (e *executor) executeMinRow(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shards []uint64, opt *execOptions) (_ interface{}, err error) {
|
||||
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executeMinRow")
|
||||
|
|
|
|||
|
|
@ -6871,13 +6871,13 @@ func variousQueriesOnPercentiles(t *testing.T, clusterSize int) {
|
|||
// get binned to the same hour
|
||||
c.CreateField(t, "users", pilosa.IndexOptions{Keys: true, TrackExistence: true}, "net_worth", pilosa.OptFieldTypeInt(-1000, 1000))
|
||||
c.ImportIntKey(t, "users", "net_worth", []test.IntKey{
|
||||
{Key: "user1", Val: 1},
|
||||
{Key: "user2", Val: 2},
|
||||
{Key: "user3", Val: 3},
|
||||
{Key: "user4", Val: 4},
|
||||
{Key: "user5", Val: 5},
|
||||
{Key: "user6", Val: 6},
|
||||
{Key: "user7", Val: 7},
|
||||
{Key: "user1", Val: 10},
|
||||
{Key: "user2", Val: 20},
|
||||
{Key: "user3", Val: 30},
|
||||
{Key: "user4", Val: 40},
|
||||
{Key: "user5", Val: 50},
|
||||
{Key: "user6", Val: 60},
|
||||
{Key: "user7", Val: 70},
|
||||
})
|
||||
|
||||
splitSortBackToCSV := func(csvStr string) string {
|
||||
|
|
@ -6886,10 +6886,6 @@ func variousQueriesOnPercentiles(t *testing.T, clusterSize int) {
|
|||
return strings.Join(ss, "\n") + "\n"
|
||||
}
|
||||
|
||||
toCSV := func(s string) string {
|
||||
return strings.Join(strings.Split(s, " "), "\n") + "\n"
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
query string
|
||||
qrVerifier func(t *testing.T, resp pilosa.QueryResponse)
|
||||
|
|
@ -6900,7 +6896,7 @@ func variousQueriesOnPercentiles(t *testing.T, clusterSize int) {
|
|||
// Rows
|
||||
{
|
||||
query: `Percentile(field="net_worth", nth=0.5)`,
|
||||
csvVerifier: toCSV("4"),
|
||||
csvVerifier: "10,1\n",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue