Consistency with aggregate functions

This commit is contained in:
Kuba Podgórski 2021-03-16 15:02:40 +01:00
parent c6ea56bbad
commit 1dac4c7622
6 changed files with 1680 additions and 1230 deletions

View file

@ -1125,8 +1125,11 @@ func (e *executor) executeSum(ctx context.Context, qcx *Qcx, index string, c *pq
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executeSum")
defer span.Finish()
fieldName, ok := c.Args["field"].(string)
if !ok || fieldName == "" {
fieldName := c.ArgString("field")
if fieldName == "" {
fieldName = c.ArgString("_field")
}
if fieldName == "" {
return ValCount{}, errors.New("Sum(): field required")
}
@ -1229,7 +1232,12 @@ func (e *executor) executeDistinct(ctx context.Context, qcx *Qcx, index string,
func (e *executor) executeMin(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shards []uint64, opt *execOptions) (_ ValCount, err error) {
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executeMin")
defer span.Finish()
if field := c.Args["field"]; field == "" {
field := c.Args["field"]
if field == "" {
field = c.ArgString("_field")
}
if field == "" {
return ValCount{}, errors.New("Min(): field required")
}
@ -1265,7 +1273,11 @@ func (e *executor) executeMax(ctx context.Context, qcx *Qcx, index string, c *pq
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executeMax")
defer span.Finish()
if field := c.Args["field"]; field == "" {
field := c.ArgString("field")
if field == "" {
field = c.ArgString("_field")
}
if field == "" {
return ValCount{}, errors.New("Max(): field required")
}
@ -1889,7 +1901,13 @@ func (e *executor) executeSumCountShard(ctx context.Context, qcx *Qcx, index str
filter = row
}
fieldName, _ := c.Args["field"].(string)
fieldName := c.ArgString("field")
if fieldName == "" {
fieldName = c.ArgString("_field")
}
if fieldName == "" {
return ValCount{}, errors.New("Sum(): field required")
}
field := e.Holder.Field(index, fieldName)
if field == nil {
@ -1940,7 +1958,13 @@ func (e *executor) executeMinShard(ctx context.Context, qcx *Qcx, index string,
filter = row
}
fieldName, _ := c.Args["field"].(string)
fieldName, ok := c.Args["field"].(string)
if !ok || fieldName == "" {
fieldName = c.ArgString("_field")
}
if fieldName == "" {
return ValCount{}, errors.New("Min(): field required")
}
field := e.Holder.Field(index, fieldName)
if field == nil {
@ -1969,7 +1993,13 @@ func (e *executor) executeMaxShard(ctx context.Context, qcx *Qcx, index string,
filter = row
}
fieldName, _ := c.Args["field"].(string)
fieldName := c.ArgString("field")
if fieldName == "" {
fieldName = c.ArgString("_field")
}
if fieldName == "" {
return ValCount{}, errors.New("Max(): field required")
}
field := e.Holder.Field(index, fieldName)
if field == nil {

View file

@ -1581,6 +1581,42 @@ func TestExecutor_Execute_MinMax(t *testing.T) {
t.Fatalf("unexpected max result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Min", func(t *testing.T) {
pql = fmt.Sprintf(`Min(field="%s")`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: test.set, Count: 1}) {
t.Fatalf("unexpected min result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Max", func(t *testing.T) {
pql = fmt.Sprintf(`Max(field="%s")`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: test.set, Count: 1}) {
t.Fatalf("unexpected max result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Min", func(t *testing.T) {
pql = fmt.Sprintf(`Min(%s)`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: test.set, Count: 1}) {
t.Fatalf("unexpected min result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Max", func(t *testing.T) {
pql = fmt.Sprintf(`Max(%s)`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: test.set, Count: 1}) {
t.Fatalf("unexpected max result, test %d: %s", i, spew.Sdump(result))
}
})
})
}
})
@ -1695,6 +1731,24 @@ func TestExecutor_Execute_MinMax(t *testing.T) {
t.Fatalf("unexpected max result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Min", func(t *testing.T) {
pql = fmt.Sprintf(`Min(%s)`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &test.exp, Count: 1}) {
t.Fatalf("unexpected min result, test %d: %s", i, spew.Sdump(result))
}
})
t.Run("Max", func(t *testing.T) {
pql = fmt.Sprintf(`Max(%s)`, fld)
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: pql}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &test.exp, Count: 1}) {
t.Fatalf("unexpected max result, test %d: %s", i, spew.Sdump(result))
}
})
})
}
})
@ -2024,6 +2078,22 @@ func TestExecutor_Execute_Sum(t *testing.T) {
}
})
t.Run("NoFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(field="foo")`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: 200, Count: 5}) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("NoFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(foo)`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: 200, Count: 5}) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("WithFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(Row(x=0), field=foo)`}); err != nil {
t.Fatal(err)
@ -2031,6 +2101,14 @@ func TestExecutor_Execute_Sum(t *testing.T) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("WithFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(foo, Row(x=0))`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{Val: 80, Count: 2}) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
})
t.Run("Decimal", func(t *testing.T) {
@ -2049,6 +2127,23 @@ func TestExecutor_Execute_Sum(t *testing.T) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("NoFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(dec)`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 700007, Scale: 3}, Count: 3}) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("WithFilter", func(t *testing.T) {
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(dec, Row(x=0))`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 500005, Scale: 3}, Count: 2}) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
})
})

View file

@ -335,7 +335,8 @@ var stringOrInt64 stringOrInt64Type
var allowField = callInfo{
allowUnknown: false,
prototypes: map[string]interface{}{
"field": "",
"_field": "",
"field": "",
},
}

View file

@ -14,8 +14,11 @@ Call <- "Set" {p.startCall("Set")} open col comma args (comma timestamp)? close
/ "Store" {p.startCall("Store")} open Call comma arg close {p.endCall()}
/ "TopN" {p.startCall("TopN")} open posfield (comma allargs)? close {p.endCall()}
/ "TopK" {p.startCall("TopK")} open posfield (comma allargs)? close {p.endCall()}
/ "Percentile" {p.startCall("Percentile")} open posfield (comma allargs)? close {p.endCall()}
/ "Percentile" {p.startCall("Percentile")} open posfield (comma allargs)? close {p.endCall()}
/ "Rows" {p.startCall("Rows")} open posfield (comma allargs)? close {p.endCall()}
/ "Min" {p.startCall("Min")} open posfield (comma allargs)? close {p.endCall()}
/ "Max" {p.startCall("Max")} open posfield (comma allargs)? close {p.endCall()}
/ "Sum" {p.startCall("Sum")} open posfield (comma allargs)? close {p.endCall()}
/ "Range" {p.startCall("Range")} open field eq value comma 'from='? {p.addField("from")} timestampfmt {p.addVal(text)} comma 'to='? sp {p.addField("to")} timestampfmt {p.addVal(text)} close {p.endCall()}
/ < IDENT > { p.startCall(text) } open allargs comma? close { p.endCall() }
allargs <- Call (comma Call)* (comma args)? / args / sp

File diff suppressed because it is too large Load diff

View file

@ -710,13 +710,58 @@ func TestPQLDeepEquality(t *testing.T) {
},
},
}},
{
name: "Sum",
call: "Sum(f)",
exp: &Call{
Name: "Sum",
Args: map[string]interface{}{
"_field": "f",
},
}},
{
name: "Sum",
call: "Sum(field=f)",
exp: &Call{
Name: "Sum",
Args: map[string]interface{}{
"field": "f",
"_field": "f",
},
}},
{
name: "Max",
call: "Max(f)",
exp: &Call{
Name: "Max",
Args: map[string]interface{}{
"_field": "f",
},
}},
{
name: "Max",
call: "Max(field=f)",
exp: &Call{
Name: "Max",
Args: map[string]interface{}{
"_field": "f",
},
}},
{
name: "Min",
call: "Min(f)",
exp: &Call{
Name: "Min",
Args: map[string]interface{}{
"_field": "f",
},
}},
{
name: "Min",
call: "Min(field=f)",
exp: &Call{
Name: "Min",
Args: map[string]interface{}{
"_field": "f",
},
}},
{
@ -740,6 +785,18 @@ func TestPQLDeepEquality(t *testing.T) {
{Name: "Row"},
},
}},
{
name: "SumChild",
call: "Sum(f, Row())",
exp: &Call{
Name: "Sum",
Args: map[string]interface{}{
"_field": "f",
},
Children: []*Call{
{Name: "Row"},
},
}},
{
name: "MinChild",
call: "Min(Row(), field=f)",