mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 17:15:56 +00:00
add translation code for GroupBy "previous" arg
This commit is contained in:
parent
61089981a2
commit
f94cd8ae7d
2 changed files with 179 additions and 0 deletions
57
executor.go
57
executor.go
|
|
@ -2006,6 +2006,8 @@ func (e *executor) translateCall(index string, idx *Index, c *pql.Call) error {
|
|||
} else if c.Name == "Rows" {
|
||||
fieldName = callArgString(c, "field")
|
||||
rowKey = "previous"
|
||||
} else if c.Name == "GroupBy" {
|
||||
return errors.Wrap(e.translateGroupByCall(index, idx, c), "translating GroupBy")
|
||||
} else {
|
||||
colKey = "col"
|
||||
fieldName = callArgString(c, "field")
|
||||
|
|
@ -2067,6 +2069,61 @@ func (e *executor) translateCall(index string, idx *Index, c *pql.Call) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *executor) translateGroupByCall(index string, idx *Index, c *pql.Call) error {
|
||||
if c.Name != "GroupBy" {
|
||||
panic("translateGroupByCall called with '" + c.Name + "'")
|
||||
}
|
||||
|
||||
for _, child := range c.Children {
|
||||
if err := e.translateCall(index, idx, child); err != nil {
|
||||
return errors.Wrapf(err, "translating %s", child)
|
||||
}
|
||||
}
|
||||
|
||||
prev, ok := c.Args["previous"]
|
||||
if !ok {
|
||||
return nil // nothing else to be translated
|
||||
}
|
||||
previous, ok := prev.([]interface{})
|
||||
if !ok {
|
||||
return errors.Errorf("'previous' argument must be list, but got %T", prev)
|
||||
}
|
||||
if len(c.Children) != len(previous) {
|
||||
return errors.Errorf("mismatched lengths for previous: %d and children: %d in %s", len(previous), len(c.Children), c)
|
||||
}
|
||||
|
||||
fields := make([]*Field, len(c.Children))
|
||||
for i, child := range c.Children {
|
||||
fieldname := callArgString(child, "field")
|
||||
field := idx.Field(fieldname)
|
||||
if field == nil {
|
||||
return errors.Wrapf(ErrFieldNotFound, "getting field '%s' from '%s'", fieldname, child)
|
||||
}
|
||||
fields[i] = field
|
||||
}
|
||||
|
||||
for i, field := range fields {
|
||||
prev := previous[i]
|
||||
if field.keys() {
|
||||
prevStr, ok := prev.(string)
|
||||
if !ok {
|
||||
return errors.New("prev value must be a string when field 'keys' option enabled")
|
||||
}
|
||||
ids, err := e.TranslateStore.TranslateRowsToUint64(index, field.Name(), []string{prevStr})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "translating row key '%s'", prevStr)
|
||||
}
|
||||
previous[i] = ids[0]
|
||||
} else {
|
||||
if prevStr, ok := prev.(string); ok {
|
||||
return errors.Errorf("got string row val '%s' in 'previous' for field %s which doesn't use string keys", prevStr, field.Name())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *executor) translateResult(index string, idx *Index, call *pql.Call, result interface{}) (interface{}, error) {
|
||||
switch result := result.(type) {
|
||||
case *Row:
|
||||
|
|
|
|||
122
executor_internal_test.go
Normal file
122
executor_internal_test.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package pilosa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pilosa/pilosa/pql"
|
||||
)
|
||||
|
||||
func TestExecutor_TranslateGroupByCall(t *testing.T) {
|
||||
e := &executor{
|
||||
Holder: NewHolder(),
|
||||
}
|
||||
e.Holder.Path, _ = ioutil.TempDir("", "")
|
||||
err := e.Holder.Open()
|
||||
if err != nil {
|
||||
t.Fatalf("opening holder: %v", err)
|
||||
}
|
||||
|
||||
e.TranslateStore = e.Holder.translateFile
|
||||
tf, _ := ioutil.TempFile("", "")
|
||||
e.Holder.translateFile.Path = tf.Name()
|
||||
err = e.Holder.translateFile.Open()
|
||||
if err != nil {
|
||||
t.Fatalf("opening translateFile: %v", err)
|
||||
}
|
||||
|
||||
idx, err := e.Holder.CreateIndex("i", IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("creating index: %v", err)
|
||||
}
|
||||
|
||||
_, erra := idx.CreateField("ak", OptFieldKeys())
|
||||
_, errb := idx.CreateField("b")
|
||||
_, errc := idx.CreateField("ck", OptFieldKeys())
|
||||
if erra != nil || errb != nil || errc != nil {
|
||||
t.Fatalf("creating fields %v, %v, %v", erra, errb, errc)
|
||||
}
|
||||
|
||||
_, erra = e.TranslateStore.TranslateRowsToUint64("i", "ak", []string{"la"})
|
||||
_, errb = e.TranslateStore.TranslateRowsToUint64("i", "ck", []string{"ha"})
|
||||
if erra != nil || errb != nil {
|
||||
t.Fatalf("translating rows %v, %v", erra, errb)
|
||||
}
|
||||
|
||||
query, err := pql.ParseString(`GroupBy(Rows(field=ak), Rows(field=b), Rows(field=ck), previous=["la", 0, "ha"])`)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing query: %v", err)
|
||||
}
|
||||
c := query.Calls[0]
|
||||
err = e.translateGroupByCall("i", idx, c)
|
||||
if err != nil {
|
||||
t.Fatalf("translating call: %v", err)
|
||||
}
|
||||
if len(c.Args["previous"].([]interface{})) != 3 {
|
||||
t.Fatalf("unexpected length for 'previous' arg %v", c.Args["previous"])
|
||||
}
|
||||
for i, v := range c.Args["previous"].([]interface{}) {
|
||||
if !isInt(v) {
|
||||
t.Fatalf("expected all items in previous to be ints, but '%v' at index %d is %[1]T", v, i)
|
||||
}
|
||||
}
|
||||
|
||||
errTests := []struct {
|
||||
pql string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
pql: `GroupBy(Rows(field=notfound), previous=1)`,
|
||||
err: "'previous' argument must be list",
|
||||
},
|
||||
{
|
||||
pql: `GroupBy(Rows(field=ak), previous=["la", 0])`,
|
||||
err: "mismatched lengths",
|
||||
},
|
||||
{
|
||||
pql: `GroupBy(Rows(field=ak), previous=[1])`,
|
||||
err: "prev value must be a string",
|
||||
},
|
||||
{
|
||||
pql: `GroupBy(Rows(field=notfound), previous=[1])`,
|
||||
err: ErrFieldNotFound.Error(),
|
||||
},
|
||||
// TODO: an unknown key will actually allocate an id. this is probably bad.
|
||||
// {
|
||||
// pql: `GroupBy(Rows(field=ak), previous=["zoop"])`,
|
||||
// err: "translating row key '",
|
||||
// },
|
||||
{
|
||||
pql: `GroupBy(Rows(field=b), previous=["la"])`,
|
||||
err: "which doesn't use string keys",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range errTests {
|
||||
t.Run(fmt.Sprintf("#%d_%s", i, test.err), func(t *testing.T) {
|
||||
query, err := pql.ParseString(test.pql)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing query: %v", err)
|
||||
}
|
||||
c := query.Calls[0]
|
||||
err = e.translateGroupByCall("i", idx, c)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, but translated call is '%s", c)
|
||||
}
|
||||
if !strings.Contains(err.Error(), test.err) {
|
||||
t.Fatalf("expected '%s', got '%v'", test.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func isInt(a interface{}) bool {
|
||||
switch a.(type) {
|
||||
case int, int64, uint, uint64:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue