coalesce takes two conditions and turns them into one. it returns a bool to indicate if the returned condition is valid or if it should just continue using the original 2 conditions it is very difficult to coalesce conditions in a way that will always be more performant at the fetch layer consider:
(c1, c2 traceql.Condition)
| 43 | // - they are exactly the same |
| 44 | // - they will pull every span anyway. example: { span.foo = "bar" } >> { span.foo != "bar" } |
| 45 | func coalesce(c1, c2 traceql.Condition) (traceql.Condition, bool) { |
| 46 | // if the conditions are exactly the same then we can just return one of them |
| 47 | if c1.Attribute == c2.Attribute && |
| 48 | c1.Op == c2.Op && |
| 49 | operandsEqual(c1, c2) { |
| 50 | return c1, true |
| 51 | } |
| 52 | |
| 53 | // if the operations are != and = and the operands are the same this is going to pull every row. let's |
| 54 | // collapse to one condition with OpNone and no operands |
| 55 | if c1.Attribute == c2.Attribute && // attributes equal |
| 56 | (c1.Op == traceql.OpEqual && c2.Op == traceql.OpNotEqual || c1.Op == traceql.OpNotEqual && c2.Op == traceql.OpEqual) && // operands are != and = |
| 57 | operandsEqual(c1, c2) { // operands equal |
| 58 | return traceql.Condition{Attribute: c1.Attribute, Op: traceql.OpNone, Operands: nil}, true |
| 59 | } |
| 60 | |
| 61 | // if one of the operations is opnone we're already pulling every row. coalesce |
| 62 | if c1.Attribute == c2.Attribute && // attributes equal |
| 63 | (c1.Op == traceql.OpNone || c2.Op == traceql.OpNone) { // one operand is opnone |
| 64 | return traceql.Condition{Attribute: c1.Attribute, Op: traceql.OpNone, Operands: nil}, true |
| 65 | } |
| 66 | |
| 67 | return traceql.Condition{}, false |
| 68 | } |
| 69 | |
| 70 | func operandsEqual(c1, c2 traceql.Condition) bool { |
| 71 | if len(c1.Operands) != len(c2.Operands) { |
no test coverage detected