Add support for field != null Range query

This commit is contained in:
Travis 2017-10-05 15:02:30 -05:00
parent 07ae881967
commit 4304d341f7
No known key found for this signature in database
GPG key ID: 7F08008DFD9314C9
7 changed files with 44 additions and 3 deletions

View file

@ -715,7 +715,30 @@ func (e *Executor) executeFieldRangeSlice(ctx context.Context, index string, c *
fieldName, cond = k, vv
}
if cond.Op == pql.BETWEEN {
// EQ null (not implemented: flip frag.FieldNotNull with max ColumnID)
// NEQ null frag.FieldNotNull()
// BETWEEN a,b(in) BETWEEN/frag.FieldRangeBetween()
// BETWEEN a,b(out) BETWEEN/frag.FieldNotNull()
// EQ <int> frag.FieldRange
// NEQ <int> (not implemented: frag.FieldRange)
// Handle `!= null`.
if cond.Op == pql.NEQ && cond.Value == nil {
// Find field.
field := f.Field(fieldName)
if field == nil {
return nil, ErrFieldNotFound
}
// Retrieve fragment.
frag := e.Holder.Fragment(index, frame, ViewFieldPrefix+fieldName, slice)
if frag == nil {
return NewBitmap(), nil
}
return frag.FieldNotNull(field.BitDepth())
} else if cond.Op == pql.BETWEEN {
predicates, err := cond.IntSliceValue()
if err != nil {

View file

@ -764,6 +764,14 @@ func TestExecutor_Execute_FieldRange(t *testing.T) {
}
})
t.Run("NEQ", func(t *testing.T) {
if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=other, foo != null)`), nil, nil); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual([]uint64{0}, result[0].(*pilosa.Bitmap).Bits()) {
t.Fatalf("unexpected result: %s", spew.Sdump(result))
}
})
t.Run("LT", func(t *testing.T) {
if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo < 20)`), nil, nil); err != nil {
t.Fatal(err)

View file

@ -165,7 +165,7 @@ func (p *Parser) parseArgs() (map[string]interface{}, error) {
var op Token
switch tok, pos, lit := p.scanIgnoreWhitespace(); tok {
case ASSIGN:
case EQ, LT, LTE, GT, GTE, BETWEEN:
case EQ, NEQ, LT, LTE, GT, GTE, BETWEEN:
op = tok
default:
return nil, parseErrorf(pos, "expected equals sign or comparison operator, found %q", lit)

View file

@ -172,7 +172,7 @@ func TestParser_Parse(t *testing.T) {
// Parse with condition arguments.
t.Run("WithCondition", func(t *testing.T) {
q, err := pql.ParseString(`MyCall(key=foo, x == 12.25, y >= 100, z >< [4,8])`)
q, err := pql.ParseString(`MyCall(key=foo, x == 12.25, y >= 100, z >< [4,8], m != null)`)
if err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(q.Calls[0],
@ -183,6 +183,7 @@ func TestParser_Parse(t *testing.T) {
"x": &pql.Condition{Op: pql.EQ, Value: 12.25},
"y": &pql.Condition{Op: pql.GTE, Value: int64(100)},
"z": &pql.Condition{Op: pql.BETWEEN, Value: []interface{}{int64(4), int64(8)}},
"m": &pql.Condition{Op: pql.NEQ, Value: nil},
},
},
) {

View file

@ -66,6 +66,12 @@ func (s *Scanner) Scan() (tok Token, pos Pos, lit string) {
}
s.unread()
return ASSIGN, pos, string(ch)
case '!':
if next := s.read(); next == '=' {
return NEQ, pos, "!="
}
s.unread()
return ASSIGN, pos, string(ch)
case '<':
if next := s.read(); next == '=' {
return LTE, pos, "<="

View file

@ -38,6 +38,7 @@ func TestScanner_Scan(t *testing.T) {
{name: "ASSIGN", s: `=`, tok: pql.ASSIGN, lit: `=`},
{name: "EQ", s: `==`, tok: pql.EQ, lit: `==`},
{name: "NEQ", s: `!=`, tok: pql.NEQ, lit: `!=`},
{name: "LT", s: `<`, tok: pql.LT, lit: `<`},
{name: "LTE", s: `<=`, tok: pql.LTE, lit: `<=`},
{name: "GT", s: `>`, tok: pql.GT, lit: `>`},

View file

@ -39,6 +39,7 @@ const (
ASSIGN // =
EQ // ==
NEQ // !=
LT // <
LTE // <=
GT // >
@ -64,6 +65,7 @@ var tokens = [...]string{
ASSIGN: "=",
EQ: "==",
NEQ: "!=",
LT: "<",
LTE: "<=",
GT: ">",