2727import org .apache .doris .nereids .jobs .joinorder .hypergraph .node .StructInfoNode ;
2828import org .apache .doris .nereids .memo .Group ;
2929import org .apache .doris .nereids .memo .GroupExpression ;
30+ import org .apache .doris .nereids .rules .exploration .mv .ComparisonResult ;
3031import org .apache .doris .nereids .rules .exploration .mv .LogicalCompatibilityContext ;
3132import org .apache .doris .nereids .rules .rewrite .PushDownFilterThroughJoin ;
3233import org .apache .doris .nereids .trees .expressions .Alias ;
4445
4546import com .google .common .base .Preconditions ;
4647import com .google .common .collect .ImmutableList ;
48+ import com .google .common .collect .ImmutableMap ;
4749import com .google .common .collect .ImmutableSet ;
4850import com .google .common .collect .Lists ;
51+ import com .google .common .collect .Sets ;
4952
5053import java .util .ArrayList ;
5154import java .util .BitSet ;
5255import java .util .HashMap ;
56+ import java .util .HashSet ;
5357import java .util .List ;
5458import java .util .Map ;
59+ import java .util .Map .Entry ;
60+ import java .util .Optional ;
5561import java .util .Set ;
5662import java .util .stream .Collectors ;
57- import javax .annotation .Nullable ;
5863
5964/**
6065 * The graph is a join graph, whose node is the leaf plan and edge is a join operator.
@@ -267,11 +272,11 @@ private void makeFilterConflictRules(JoinEdge joinEdge) {
267272 filterEdges .forEach (e -> {
268273 if (LongBitmap .isSubset (e .getReferenceNodes (), leftSubNodes )
269274 && !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_LEFT .contains (joinEdge .getJoinType ())) {
270- e .addRejectJoin (joinEdge );
275+ e .addRejectEdge (joinEdge );
271276 }
272277 if (LongBitmap .isSubset (e .getReferenceNodes (), rightSubNodes )
273278 && !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_RIGHT .contains (joinEdge .getJoinType ())) {
274- e .addRejectJoin (joinEdge );
279+ e .addRejectEdge (joinEdge );
275280 }
276281 });
277282 }
@@ -288,19 +293,23 @@ private void makeJoinConflictRules(JoinEdge edgeB) {
288293 JoinEdge childA = joinEdges .get (i );
289294 if (!JoinType .isAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
290295 leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getLeftSubNodes (joinEdges ));
296+ childA .addRejectEdge (edgeB );
291297 }
292298 if (!JoinType .isLAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
293299 leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getRightSubNodes (joinEdges ));
300+ childA .addRejectEdge (edgeB );
294301 }
295302 }
296303
297304 for (int i = rightSubTreeEdges .nextSetBit (0 ); i >= 0 ; i = rightSubTreeEdges .nextSetBit (i + 1 )) {
298305 JoinEdge childA = joinEdges .get (i );
299306 if (!JoinType .isAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
300307 rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getRightSubNodes (joinEdges ));
308+ childA .addRejectEdge (edgeB );
301309 }
302310 if (!JoinType .isRAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
303311 rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getLeftSubNodes (joinEdges ));
312+ childA .addRejectEdge (edgeB );
304313 }
305314 }
306315 edgeB .setLeftExtendedNodes (leftRequired );
@@ -592,57 +601,75 @@ public int edgeSize() {
592601 * compare hypergraph
593602 *
594603 * @param viewHG the compared hyper graph
595- * @return null represents not compatible, or return some expression which can
596- * be pull up from this hyper graph
604+ * @return Comparison result
597605 */
598- public @ Nullable List <Expression > isLogicCompatible (HyperGraph viewHG , LogicalCompatibilityContext ctx ) {
599- Map <Edge , Edge > queryToView = constructEdgeMap (viewHG , ctx .getQueryToViewEdgeExpressionMapping ());
606+ public ComparisonResult isLogicCompatible (HyperGraph viewHG , LogicalCompatibilityContext ctx ) {
607+ // 1 try to construct a map which can be mapped from edge to edge
608+ Map <Edge , Edge > queryToView = constructMapWithNode (viewHG , ctx .getQueryToViewNodeIDMapping ());
600609
601- // All edge in view must have a mapped edge in query
602- if (queryToView .size () != viewHG .edgeSize ()) {
603- return null ;
610+ // 2. compare them by expression and extract residual expr
611+ ComparisonResult .Builder builder = new ComparisonResult .Builder ();
612+ ComparisonResult edgeCompareRes = compareEdgesWithExpr (queryToView , ctx .getQueryToViewEdgeExpressionMapping ());
613+ if (edgeCompareRes .isInvalid ()) {
614+ return ComparisonResult .INVALID ;
604615 }
616+ builder .addComparisonResult (edgeCompareRes );
605617
606- boolean allMatch = queryToView .entrySet ().stream ()
607- .allMatch (entry ->
608- compareEdgeWithNode (entry .getKey (), entry .getValue (), ctx .getQueryToViewNodeIDMapping ()));
609- if (!allMatch ) {
610- return null ;
618+ // 3. pull join edge of view is no sense, so reject them
619+ if (!queryToView .values ().containsAll (viewHG .joinEdges )) {
620+ return ComparisonResult .INVALID ;
611621 }
612622
613- // join edges must be identical
614- boolean isJoinIdentical = joinEdges . stream ()
615- . allMatch ( queryToView :: containsKey );
616- if (! isJoinIdentical ) {
617- return null ;
623+ // 4. process residual edges
624+ List < Expression > residualQueryJoin =
625+ processOrphanEdges ( Sets . difference ( Sets . newHashSet ( joinEdges ), queryToView . keySet ()) );
626+ if (residualQueryJoin == null ) {
627+ return ComparisonResult . INVALID ;
618628 }
629+ builder .addQueryExpressions (residualQueryJoin );
619630
620- // extract all top filters
621- List <FilterEdge > residualFilterEdges = filterEdges .stream ()
622- .filter (e -> !queryToView .containsKey (e ))
623- .collect (ImmutableList .toImmutableList ());
624- if (residualFilterEdges .stream ().anyMatch (e -> !e .isTopFilter ())) {
625- return null ;
631+ List <Expression > residualQueryFilter =
632+ processOrphanEdges (Sets .difference (Sets .newHashSet (filterEdges ), queryToView .keySet ()));
633+ if (residualQueryFilter == null ) {
634+ return ComparisonResult .INVALID ;
626635 }
627- return residualFilterEdges .stream ()
628- .flatMap (e -> e .getExpressions ().stream ())
629- .collect (ImmutableList .toImmutableList ());
636+ builder .addQueryExpressions (residualQueryFilter );
637+
638+ List <Expression > residualViewFilter =
639+ processOrphanEdges (
640+ Sets .difference (Sets .newHashSet (viewHG .filterEdges ), Sets .newHashSet (queryToView .values ())));
641+ if (residualViewFilter == null ) {
642+ return ComparisonResult .INVALID ;
643+ }
644+ builder .addViewExpressions (residualViewFilter );
645+
646+ return builder .build ();
630647 }
631648
632- private Map <Edge , Edge > constructEdgeMap (HyperGraph viewHG , Map <Expression , Expression > exprMap ) {
633- Map <Expression , Edge > exprToEdge = constructExprMap (viewHG );
634- Map <Edge , Edge > queryToView = new HashMap <>();
635- joinEdges .stream ()
636- .filter (e -> !e .getExpressions ().isEmpty ()
637- && exprMap .containsKey (e .getExpression (0 ))
638- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
639- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
640- filterEdges .stream ()
641- .filter (e -> !e .getExpressions ().isEmpty ()
642- && exprMap .containsKey (e .getExpression (0 ))
643- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
644- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
645- return queryToView ;
649+ private List <Expression > processOrphanEdges (Set <Edge > edges ) {
650+ List <Expression > expressions = new ArrayList <>();
651+ for (Edge edge : edges ) {
652+ if (!edge .canPullUp ()) {
653+ return null ;
654+ }
655+ expressions .addAll (edge .getExpressions ());
656+ }
657+ return expressions ;
658+ }
659+
660+ private Map <Edge , Edge > constructMapWithNode (HyperGraph viewHG , Map <Integer , Integer > nodeMap ) {
661+ // TODO use hash map to reduce loop
662+ Map <Edge , Edge > joinEdgeMap = joinEdges .stream ().map (qe -> {
663+ Optional <JoinEdge > viewEdge = viewHG .joinEdges .stream ()
664+ .filter (ve -> compareEdgeWithNode (qe , ve , nodeMap )).findFirst ();
665+ return Pair .of (qe , viewEdge );
666+ }).filter (e -> e .second .isPresent ()).collect (ImmutableMap .toImmutableMap (p -> p .first , p -> p .second .get ()));
667+ Map <Edge , Edge > filterEdgeMap = filterEdges .stream ().map (qe -> {
668+ Optional <FilterEdge > viewEdge = viewHG .filterEdges .stream ()
669+ .filter (ve -> compareEdgeWithNode (qe , ve , nodeMap )).findFirst ();
670+ return Pair .of (qe , viewEdge );
671+ }).filter (e -> e .second .isPresent ()).collect (ImmutableMap .toImmutableMap (p -> p .first , p -> p .second .get ()));
672+ return ImmutableMap .<Edge , Edge >builder ().putAll (joinEdgeMap ).putAll (filterEdgeMap ).build ();
646673 }
647674
648675 private boolean compareEdgeWithNode (Edge t , Edge o , Map <Integer , Integer > nodeMap ) {
@@ -685,24 +712,40 @@ private boolean compareNodeMap(long bitmap1, long bitmap2, Map<Integer, Integer>
685712 return bitmap2 == newBitmap1 ;
686713 }
687714
688- private boolean compareEdgeWithExpr (Edge t , Edge o , Map <Expression , Expression > expressionMap ) {
689- if (t .getExpressions ().size () != o .getExpressions ().size ()) {
690- return false ;
691- }
692- int size = t .getExpressions ().size ();
693- for (int i = 0 ; i < size ; i ++) {
694- if (!expressionMap .get (t .getExpression (i )).equals (o .getExpression (i ))) {
695- return false ;
715+ private ComparisonResult compareEdgesWithExpr (Map <Edge , Edge > queryToViewedgeMap ,
716+ Map <Expression , Expression > queryToView ) {
717+ ComparisonResult .Builder builder = new ComparisonResult .Builder ();
718+ for (Entry <Edge , Edge > e : queryToViewedgeMap .entrySet ()) {
719+ ComparisonResult res = compareEdgeWithExpr (e .getKey (), e .getValue (), queryToView );
720+ if (res .isInvalid ()) {
721+ return ComparisonResult .INVALID ;
696722 }
723+ builder .addComparisonResult (res );
697724 }
698- return true ;
725+ return builder . build () ;
699726 }
700727
701- private Map <Expression , Edge > constructExprMap (HyperGraph hyperGraph ) {
702- Map <Expression , Edge > exprToEdge = new HashMap <>();
703- hyperGraph .joinEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
704- hyperGraph .filterEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
705- return exprToEdge ;
728+ private ComparisonResult compareEdgeWithExpr (Edge query , Edge view , Map <Expression , Expression > queryToView ) {
729+ Set <? extends Expression > queryExprSet = query .getExpressionSet ();
730+ Set <? extends Expression > viewExprSet = view .getExpressionSet ();
731+
732+ Set <Expression > equalViewExpr = new HashSet <>();
733+ List <Expression > residualQueryExpr = new ArrayList <>();
734+ for (Expression queryExpr : queryExprSet ) {
735+ if (queryToView .containsKey (queryExpr ) && viewExprSet .contains (queryToView .get (queryExpr ))) {
736+ equalViewExpr .add (queryToView .get (queryExpr ));
737+ } else {
738+ residualQueryExpr .add (queryExpr );
739+ }
740+ }
741+ List <Expression > residualViewExpr = ImmutableList .copyOf (Sets .difference (viewExprSet , equalViewExpr ));
742+ if (!residualViewExpr .isEmpty () && !view .canPullUp ()) {
743+ return ComparisonResult .INVALID ;
744+ }
745+ if (!residualQueryExpr .isEmpty () && !query .canPullUp ()) {
746+ return ComparisonResult .INVALID ;
747+ }
748+ return new ComparisonResult (residualQueryExpr , residualViewExpr );
706749 }
707750
708751 /**
0 commit comments