|
@@ -5,22 +5,26 @@
|
|
|
*/
|
|
|
package org.elasticsearch.xpack.eql.parser;
|
|
|
|
|
|
+import org.antlr.v4.runtime.tree.ParseTree;
|
|
|
import org.elasticsearch.common.unit.TimeValue;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.EventFilterContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.IntegerLiteralContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.JoinContext;
|
|
|
+import org.elasticsearch.xpack.eql.parser.EqlBaseParser.JoinKeysContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.JoinTermContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.NumberContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.SequenceContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.SequenceParamsContext;
|
|
|
import org.elasticsearch.xpack.eql.parser.EqlBaseParser.SequenceTermContext;
|
|
|
+import org.elasticsearch.xpack.eql.parser.EqlBaseParser.SubqueryContext;
|
|
|
import org.elasticsearch.xpack.eql.plan.logical.Join;
|
|
|
import org.elasticsearch.xpack.eql.plan.logical.KeyedFilter;
|
|
|
import org.elasticsearch.xpack.eql.plan.logical.Sequence;
|
|
|
import org.elasticsearch.xpack.eql.plan.physical.LocalRelation;
|
|
|
import org.elasticsearch.xpack.ql.expression.Attribute;
|
|
|
+import org.elasticsearch.xpack.ql.expression.EmptyAttribute;
|
|
|
import org.elasticsearch.xpack.ql.expression.Expression;
|
|
|
-import org.elasticsearch.xpack.ql.expression.FieldAttribute;
|
|
|
+import org.elasticsearch.xpack.ql.expression.Expressions;
|
|
|
import org.elasticsearch.xpack.ql.expression.Literal;
|
|
|
import org.elasticsearch.xpack.ql.expression.Order;
|
|
|
import org.elasticsearch.xpack.ql.expression.UnresolvedAttribute;
|
|
@@ -33,7 +37,6 @@ import org.elasticsearch.xpack.ql.plan.logical.Project;
|
|
|
import org.elasticsearch.xpack.ql.plan.logical.UnresolvedRelation;
|
|
|
import org.elasticsearch.xpack.ql.tree.Source;
|
|
|
import org.elasticsearch.xpack.ql.type.DataTypes;
|
|
|
-import org.elasticsearch.xpack.ql.type.UnsupportedEsField;
|
|
|
import org.elasticsearch.xpack.ql.util.CollectionUtils;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
@@ -41,11 +44,11 @@ import java.util.List;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
import static java.util.Collections.emptyList;
|
|
|
-import static java.util.Collections.singletonList;
|
|
|
|
|
|
public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
|
|
|
private final UnresolvedRelation RELATION = new UnresolvedRelation(Source.EMPTY, null, "", false, "");
|
|
|
+ private final EmptyAttribute UNSPECIFIED_FIELD = new EmptyAttribute(Source.EMPTY);
|
|
|
|
|
|
public LogicalPlanBuilder(ParserParams params) {
|
|
|
super(params);
|
|
@@ -55,6 +58,10 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
return new UnresolvedAttribute(Source.EMPTY, params.fieldTimestamp());
|
|
|
}
|
|
|
|
|
|
+ private Attribute fieldTieBreaker() {
|
|
|
+ return params.fieldTieBreaker() != null ? new UnresolvedAttribute(Source.EMPTY, params.fieldTieBreaker()) : UNSPECIFIED_FIELD;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public LogicalPlan visitEventQuery(EqlBaseParser.EventQueryContext ctx) {
|
|
|
return new Project(source(ctx), visitEventFilter(ctx.eventFilter()), emptyList());
|
|
@@ -77,9 +84,17 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
}
|
|
|
|
|
|
Filter filter = new Filter(source, RELATION, condition);
|
|
|
- // add implicit sorting - when pipes are added, this would better sit there (as a default pipe)
|
|
|
- Order order = new Order(source, fieldTimestamp(), Order.OrderDirection.ASC, Order.NullsPosition.FIRST);
|
|
|
- OrderBy orderBy = new OrderBy(source, filter, singletonList(order));
|
|
|
+ List<Order> orders = new ArrayList<>(2);
|
|
|
+
|
|
|
+ // TODO: add implicit sorting - when pipes are added, this would better sit there (as a default pipe)
|
|
|
+ orders.add(new Order(source, fieldTimestamp(), Order.OrderDirection.ASC, Order.NullsPosition.FIRST));
|
|
|
+ // make sure to add the tieBreaker as well
|
|
|
+ Attribute tieBreaker = fieldTieBreaker();
|
|
|
+ if (Expressions.isPresent(tieBreaker)) {
|
|
|
+ orders.add(new Order(source, tieBreaker, Order.OrderDirection.ASC, Order.NullsPosition.FIRST));
|
|
|
+ }
|
|
|
+
|
|
|
+ OrderBy orderBy = new OrderBy(source, filter, orders);
|
|
|
return orderBy;
|
|
|
}
|
|
|
|
|
@@ -118,22 +133,30 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
until = defaultUntil(source);
|
|
|
}
|
|
|
|
|
|
- return new Join(source, queries, until, fieldTimestamp());
|
|
|
+ return new Join(source, queries, until, fieldTimestamp(), fieldTieBreaker());
|
|
|
}
|
|
|
|
|
|
private KeyedFilter defaultUntil(Source source) {
|
|
|
// no until declared means no results
|
|
|
- // create a dummy keyed filter
|
|
|
- String notUsed = "<not-used>";
|
|
|
- Attribute tsField = new FieldAttribute(source, notUsed, new UnsupportedEsField(notUsed, notUsed));
|
|
|
- return new KeyedFilter(source, new LocalRelation(source, emptyList()), emptyList(), tsField);
|
|
|
+ return new KeyedFilter(source, new LocalRelation(source, emptyList()), emptyList(), UNSPECIFIED_FIELD, UNSPECIFIED_FIELD);
|
|
|
}
|
|
|
|
|
|
public KeyedFilter visitJoinTerm(JoinTermContext ctx, List<Attribute> joinKeys) {
|
|
|
- List<Attribute> keys = CollectionUtils.combine(joinKeys, visitJoinKeys(ctx.by));
|
|
|
- LogicalPlan eventQuery = visitEventFilter(ctx.subquery().eventFilter());
|
|
|
- LogicalPlan child = new Project(source(ctx), eventQuery, CollectionUtils.combine(keys, fieldTimestamp()));
|
|
|
- return new KeyedFilter(source(ctx), child, keys, fieldTimestamp());
|
|
|
+ return keyedFilter(joinKeys, ctx, ctx.by, ctx.subquery());
|
|
|
+ }
|
|
|
+
|
|
|
+ private KeyedFilter keyedFilter(List<Attribute> joinKeys, ParseTree ctx, JoinKeysContext joinCtx, SubqueryContext subqueryCtx) {
|
|
|
+ List<Attribute> keys = CollectionUtils.combine(joinKeys, visitJoinKeys(joinCtx));
|
|
|
+ LogicalPlan eventQuery = visitEventFilter(subqueryCtx.eventFilter());
|
|
|
+
|
|
|
+ List<Attribute> output = CollectionUtils.combine(keys, fieldTimestamp());
|
|
|
+ Attribute fieldTieBreaker = fieldTieBreaker();
|
|
|
+ if (Expressions.isPresent(fieldTieBreaker)) {
|
|
|
+ output = CollectionUtils.combine(output, fieldTieBreaker);
|
|
|
+ }
|
|
|
+ LogicalPlan child = new Project(source(ctx), eventQuery, output);
|
|
|
+
|
|
|
+ return new KeyedFilter(source(ctx), child, keys, fieldTimestamp(), fieldTieBreaker());
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -176,7 +199,7 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
until = defaultUntil(source);
|
|
|
}
|
|
|
|
|
|
- return new Sequence(source, queries, until, maxSpan, fieldTimestamp());
|
|
|
+ return new Sequence(source, queries, until, maxSpan, fieldTimestamp(), fieldTieBreaker());
|
|
|
}
|
|
|
|
|
|
public KeyedFilter visitSequenceTerm(SequenceTermContext ctx, List<Attribute> joinKeys) {
|
|
@@ -184,10 +207,7 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|
|
throw new ParsingException(source(ctx.FORK()), "sequence fork is unsupported");
|
|
|
}
|
|
|
|
|
|
- List<Attribute> keys = CollectionUtils.combine(joinKeys, visitJoinKeys(ctx.by));
|
|
|
- LogicalPlan eventQuery = visitEventFilter(ctx.subquery().eventFilter());
|
|
|
- LogicalPlan child = new Project(source(ctx), eventQuery, CollectionUtils.combine(keys, fieldTimestamp()));
|
|
|
- return new KeyedFilter(source(ctx), child, keys, fieldTimestamp());
|
|
|
+ return keyedFilter(joinKeys, ctx, ctx.by, ctx.subquery());
|
|
|
}
|
|
|
|
|
|
@Override
|