From 3782c3ac14238172fc9a12e8fd447202daa4e0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Podg=C3=B3rski?= Date: Fri, 26 Jun 2020 01:08:30 +0200 Subject: [PATCH] Support '=' condition for int/decimal fields --- executor.go | 36 +++++++++++---- executor_test.go | 115 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 9 deletions(-) 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.