/* Apply constant folding. Remove any constant terms. */ func (this *DNF) VisitOr(expr *expression.Or) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } // Constant folding var terms expression.Expressions for _, term := range expr.Operands() { val := term.Value() if val == nil { if terms == nil { terms = make(expression.Expressions, 0, len(expr.Operands())) } terms = append(terms, term) continue } if val.Truth() { return expression.TRUE_EXPR, nil } } if len(terms) == 0 { return expression.FALSE_EXPR, nil } if len(terms) < len(expr.Operands()) { expr = expression.NewOr(terms...) } return expr, nil }
func (this *DNF) VisitNot(expr *expression.Not) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } var exp expression.Expression = expr switch operand := expr.Operand().(type) { case *expression.Not: exp = operand.Operand() case *expression.And: operands := make(expression.Expressions, len(operand.Operands())) for i, op := range operand.Operands() { operands[i] = expression.NewNot(op) } exp = expression.NewOr(operands...) case *expression.Or: operands := make(expression.Expressions, len(operand.Operands())) for i, op := range operand.Operands() { operands[i] = expression.NewNot(op) } and := expression.NewAnd(operands...) return this.VisitAnd(and) case *expression.Eq: exp = expression.NewOr(expression.NewLT(operand.First(), operand.Second()), expression.NewLT(operand.Second(), operand.First())) case *expression.LT: exp = expression.NewLE(operand.Second(), operand.First()) case *expression.LE: exp = expression.NewLT(operand.Second(), operand.First()) default: return expr, nil } return exp, exp.MapChildren(this) }
func (this *DNF) VisitIn(expr *expression.In) (interface{}, error) { err := expr.MapChildren(this) if err != nil { return nil, err } a, ok := expr.Second().(*expression.ArrayConstruct) if !ok { return expr, nil } first := expr.First() operands := make(expression.Expressions, len(a.Operands())) for i, op := range a.Operands() { operands[i] = expression.NewEq(first, op) } return expression.NewOr(operands...), nil }
/* Bounded DNF, to mitigate combinatorial worst-case. Internally apply Disjunctive Normal Form. Convert ANDs of ORs to ORs of ANDs. For example: (A OR B) AND C => (A AND C) OR (B AND C) */ func applyDNF(expr *expression.And, level int) expression.Expression { na := len(expr.Operands()) if na > 4 { return expr } for i, aterm := range expr.Operands() { switch aterm := aterm.(type) { case *expression.Or: no := len(aterm.Operands()) if no*na > 8 { return expr } oterms := make(expression.Expressions, no) for j, oterm := range aterm.Operands() { aterms := make(expression.Expressions, na) for ii, atrm := range expr.Operands() { if ii == i { aterms[ii] = oterm } else { aterms[ii] = atrm } } if level > 2 { oterms[j] = expression.NewAnd(aterms...) } else { oterms[j] = applyDNF(expression.NewAnd(aterms...), level+1) } } rv := expression.NewOr(oterms...) return rv } } return expr }
func TestConverter(t *testing.T) { s1 := NewJSConverter().Visit( expression.NewLT(constant("a"), constant("b"))) s2 := "(\"a\" < \"b\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit( expression.NewBetween(constant("a"), constant("b"), constant("c"))) s2 = "(\"a\" > \"b\" && \"a\" < \"c\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewAdd( expression.NewSub(constant("a"), constant("b")), expression.NewDiv(constant("a"), constant("b")))) s2 = "((\"a\" - \"b\") + (\"a\" / \"b\"))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewLength(constant("abc"))) s2 = "\"abc\".length" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewUpper(constant("abc"))) s2 = "\"abc\".toUpperCase()" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewStrToMillis(constant("Wed, 09 Aug 1995 00:00:00"))) s2 = "Date.parse(\"Wed, 09 Aug 1995 00:00:00\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewContains(constant("dfgabc"), constant("abc"))) s2 = "\"dfgabc\".indexOf(\"abc\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewSubstr(constant("dfgabc"), constant(1), constant(4))) s2 = "\"dfgabc\".substring(1,4)" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewAdd(expression.NewContains(constant("dfgabc"), constant("abc")), expression.NewSubstr(constant("dfgabc"), constant(1), constant(4)))) s2 = "(\"dfgabc\".indexOf(\"abc\") + \"dfgabc\".substring(1,4))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc := expression.NewIdentifier("bucket") m1 := expression.NewField(doc, expression.NewFieldName("id", false)) m2 := expression.NewField(doc, expression.NewFieldName("type", false)) s1 = NewJSConverter().Visit(expression.NewOr( expression.NewUpper(m1), expression.NewLower(m2))) s2 = "(`bucket`.`id`.toUpperCase() || `bucket`.`type`.toLowerCase())" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc = expression.NewIdentifier("bucket") m1 = expression.NewField(doc, expression.NewFieldName("geo", false)) m2 = expression.NewField(m1, expression.NewFieldName("accuracy", false)) s1 = NewJSConverter().Visit(m2) s2 = "`bucket`.`geo`.`accuracy`" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc = expression.NewIdentifier("bucket") m1 = expression.NewField(doc, expression.NewElement(expression.NewFieldName("address", false), constant(0))) s1 = NewJSConverter().Visit(m1) s2 = "`bucket`.`address`[0]" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewLength(expression.NewElement(doc, expression.NewFieldName("type", false)))) s2 = "`bucket`[`type`].length" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } }