Class MarkJoin
- All Implemented Interfaces:
NamedWriteable,Writeable,PostAnalysisVerificationAware,PostOptimizationVerificationAware,Resolvable,ExecutesOn,ExecutesOn.Coordinator,SortAgnostic,SortPreserving
field IN (subquery) when the IN is embedded in an arbitrary boolean expression — typically
as a child of OR where SemiJoin / AntiJoin's row-filtering shape is not applicable.
Unlike SemiJoin, this operator preserves every left row and adds a single boolean mark attribute holding the value of
field IN (subquery) for that row, with full three-valued logic:
TRUE— left key matches at least one row in the subqueryFALSE— no match, left key non-NULL, subquery had no NULLsNULL— left key is NULL, OR no match but the subquery contains NULL(s)
OR/AND/NOT operators are evaluated by the standard expression machinery.
Like SemiJoin, the right side is an independent subquery executed first; once its result arrives as a LocalRelation,
AbstractSubqueryJoin.inlineData(org.elasticsearch.xpack.esql.plan.logical.join.AbstractSubqueryJoin, org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation, int, org.elasticsearch.compute.data.BlockFactory, java.util.concurrent.atomic.AtomicReference<org.elasticsearch.compute.data.Page>) routes the dedup result into the buildFilterPathPlan(org.elasticsearch.compute.data.Block, org.elasticsearch.xpack.esql.core.type.DataType, org.elasticsearch.xpack.esql.core.expression.Attribute, org.elasticsearch.xpack.esql.core.tree.Source, boolean) / buildHashJoinPathPlan(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan, org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation, org.elasticsearch.xpack.esql.plan.logical.join.JoinConfig, org.elasticsearch.xpack.esql.core.expression.Attribute, org.elasticsearch.xpack.esql.core.tree.Source, boolean)
hooks, which substitute an Eval that materializes the mark.
-
Nested Class Summary
Nested classes/interfaces inherited from class org.elasticsearch.xpack.esql.plan.logical.join.AbstractSubqueryJoin
AbstractSubqueryJoin.LogicalPlanTupleNested classes/interfaces inherited from class org.elasticsearch.xpack.esql.plan.logical.LogicalPlan
LogicalPlan.StageNested classes/interfaces inherited from class org.elasticsearch.xpack.esql.core.tree.Node
Node.NodeStringFormatNested classes/interfaces inherited from interface org.elasticsearch.xpack.esql.plan.logical.ExecutesOn
ExecutesOn.Coordinator, ExecutesOn.ExecuteLocation, ExecutesOn.RemoteNested classes/interfaces inherited from interface org.elasticsearch.xpack.esql.capabilities.PostOptimizationVerificationAware
PostOptimizationVerificationAware.CoordinatorOnlyNested classes/interfaces inherited from interface org.elasticsearch.common.io.stream.Writeable
Writeable.Reader<V>, Writeable.Writer<V> -
Field Summary
Fields inherited from class org.elasticsearch.xpack.esql.plan.logical.join.Join
ENTRY, UNSUPPORTED_TYPESFields inherited from class org.elasticsearch.xpack.esql.core.tree.Node
TO_STRING_MAX_LINES, TO_STRING_MAX_WIDTH -
Constructor Summary
ConstructorsConstructorDescriptionMarkJoin(Source source, LogicalPlan left, LogicalPlan right, List<Attribute> leftFields, List<Attribute> rightFields, Attribute markAttribute) MarkJoin(Source source, LogicalPlan left, LogicalPlan right, JoinConfig config, Attribute markAttribute) -
Method Summary
Modifier and TypeMethodDescriptionprotected LogicalPlanbuildEmptyRightSidePlan(Source source) Build the terminal plan when the right side has zero rows.protected LogicalPlanbuildFilterPathPlan(Block dedupKeys, DataType keyType, Attribute leftField, Source source, boolean rightHadNulls) Build the terminal plan for the small-dedup "filter" path.protected LogicalPlanbuildHashJoinPathPlan(LogicalPlan leftSide, LocalRelation deduplicatedData, JoinConfig leftJoinConfig, Attribute sentinelAttr, Source source, boolean rightHadNulls) Build the terminal plan for the large-dedup "hash-join" path.protected LogicalPlanbuildShortCircuitPlan(Source source, boolean allRightNull) Build the terminal plan when the right side contains a NULL value that forces the predicate to a constant for every left row.computeOutputExpressions(List<? extends NamedExpression> left, List<? extends NamedExpression> right) booleanprotected booleanWhether the hash-join path needs to drop NULL-keyed left rows before the join.inthashCode()info()Normally, you want to use one of the staticcreatemethods to implement this.replaceChildren(LogicalPlan left, LogicalPlan right) Methods inherited from class org.elasticsearch.xpack.esql.plan.logical.join.AbstractSubqueryJoin
expressionsResolved, firstSubPlan, getWriteableName, inlineData, inListFromDedupKeys, isSubqueryJoinUnsupported, newMainPlan, postAnalysisVerification, sentinelFilterCondition, shortCircuitOnAnyRightNull, wrapInExpression, writeToMethods inherited from class org.elasticsearch.xpack.esql.plan.logical.join.Join
computeOutputExpressions, config, executesOn, getRightToSerialize, isRemote, leftReferences, makeReference, output, postOptimizationVerification, resolved, rightOutputFields, rightReferences, withConfigMethods inherited from class org.elasticsearch.xpack.esql.plan.logical.BinaryPlan
left, replaceChildren, replaceLeft, replaceRight, rightMethods inherited from class org.elasticsearch.xpack.esql.plan.logical.LogicalPlan
analyzed, childrenResolved, optimized, preAnalyzed, preOptimized, setAnalyzed, setOptimized, setPreAnalyzed, setPreOptimizedMethods inherited from class org.elasticsearch.xpack.esql.plan.QueryPlan
computeExpressions, computeReferences, expressions, forEachExpression, forEachExpression, forEachExpressionDown, forEachExpressionUp, inputSet, outputSet, references, transformExpressionsDown, transformExpressionsDown, transformExpressionsOnly, transformExpressionsOnly, transformExpressionsOnlyUp, transformExpressionsUp, transformExpressionsUpMethods inherited from class org.elasticsearch.xpack.esql.core.tree.Node
allMatch, anyMatch, children, collect, collect, collect, collectFirstChildren, collectLeaves, doCollectFirst, forEachDown, forEachDown, forEachDownMayReturnEarly, forEachProperty, forEachPropertyDown, forEachPropertyOnly, forEachPropertyUp, forEachUp, forEachUp, nodeName, nodeProperties, nodeString, nodeString, propertiesToString, replaceChildrenSameSize, source, sourceLocation, sourceText, toString, toString, toString, transformChildren, transformChildren, transformDown, transformDown, transformDown, transformDown, transformDownSkipBranch, transformNodeProps, transformPropertiesDown, transformPropertiesOnly, transformPropertiesUp, transformUp, transformUp, transformUpMethods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, waitMethods inherited from interface org.elasticsearch.xpack.esql.plan.logical.ExecutesOn.Coordinator
executesOnMethods inherited from interface org.elasticsearch.xpack.esql.capabilities.PostAnalysisVerificationAware
postAnalysisVerification
-
Constructor Details
-
MarkJoin
public MarkJoin(Source source, LogicalPlan left, LogicalPlan right, JoinConfig config, Attribute markAttribute) -
MarkJoin
public MarkJoin(Source source, LogicalPlan left, LogicalPlan right, List<Attribute> leftFields, List<Attribute> rightFields, Attribute markAttribute)
-
-
Method Details
-
markAttribute
-
info
Description copied from class:NodeNormally, you want to use one of the staticcreatemethods to implement this.For
QueryPlans, it is very important that the properties contain all of the expressions and references relevant to this node, and that all the properties are used in the provided constructor; otherwise query plan transformations likeQueryPlan#transformExpressionsOnly(Function)will not have an effect. -
replaceChildren
- Overrides:
replaceChildrenin classJoin
-
computeOutputExpressions
public List<NamedExpression> computeOutputExpressions(List<? extends NamedExpression> left, List<? extends NamedExpression> right) - Overrides:
computeOutputExpressionsin classAbstractSubqueryJoin
-
buildEmptyRightSidePlan
Description copied from class:AbstractSubqueryJoinBuild the terminal plan when the right side has zero rows.x IN ()≡ FALSE for SEMI / MARK;x NOT IN ()≡ TRUE for ANTI. MarkJoin produces an Eval that sets the mark attribute to FALSE.- Specified by:
buildEmptyRightSidePlanin classAbstractSubqueryJoin
-
buildShortCircuitPlan
Description copied from class:AbstractSubqueryJoinBuild the terminal plan when the right side contains a NULL value that forces the predicate to a constant for every left row. For SEMI this only fires when every right value is NULL (allRightNull == true) and producesFilter(FALSE). For ANTI any NULL on the right is fatal, so it overridesAbstractSubqueryJoin.shortCircuitOnAnyRightNull()and likewise producesFilter(FALSE). MarkJoin does not short-circuit on any NULL — it keeps the row counts unchanged and emitsEval($m = NULL)when every right value is NULL.- Overrides:
buildShortCircuitPlanin classAbstractSubqueryJoin
-
buildFilterPathPlan
protected LogicalPlan buildFilterPathPlan(Block dedupKeys, DataType keyType, Attribute leftField, Source source, boolean rightHadNulls) Description copied from class:AbstractSubqueryJoinBuild the terminal plan for the small-dedup "filter" path.dedupKeyscontains the BlockHash-deduplicated distinct values from the right side and may include a single NULL position at index 0 if any input position was NULL or multi-valued (BlockHash collapses all NULL inputs into its reserved group 0).rightHadNullsis equivalent todedupKeys.isNull(0)and is passed through for documentation / hash-join-path symmetry. SEMI returnsFilter(In(...)), ANTI returnsFilter(NOT In(...))(ANTI never reaches this path withrightHadNulls = true— it short-circuits earlier), and MarkJoin builds anEvalwhose IN list is the dedup positions verbatim, relying onIn's three-valued semantics to compute the mark.- Overrides:
buildFilterPathPlanin classAbstractSubqueryJoin
-
buildHashJoinPathPlan
protected LogicalPlan buildHashJoinPathPlan(LogicalPlan leftSide, LocalRelation deduplicatedData, JoinConfig leftJoinConfig, Attribute sentinelAttr, Source source, boolean rightHadNulls) Description copied from class:AbstractSubqueryJoinBuild the terminal plan for the large-dedup "hash-join" path. The deduplicated key column has already been wrapped in aLocalRelationalongside a constant TRUE sentinel column. SEMI/ANTI add a sentinel filter and Project that drops the right-side column; MarkJoin instead computes the mark via a CASE expression and projects the mark column out alongside the original left output.- Overrides:
buildHashJoinPathPlanin classAbstractSubqueryJoin
-
filterNullLeftKeysBeforeHashJoin
protected boolean filterNullLeftKeysBeforeHashJoin()Description copied from class:AbstractSubqueryJoinWhether the hash-join path needs to drop NULL-keyed left rows before the join. SEMI/ANTI filter on the sentinel after the join; ANTI in particular would otherwise keep NULL-keyed rows (sentinel NULL → "no match") even thoughNULL NOT IN (...)should yield NULL. MARK keeps every left row and handles the NULL-key case explicitly inside the CASE expression that produces the mark, so it suppresses the filter.- Overrides:
filterNullLeftKeysBeforeHashJoinin classAbstractSubqueryJoin
-
hashCode
public int hashCode() -
equals
-