private static void sortSemijoinFilters(OptimizeTezProcContext procCtx,
      ListMultimap<FilterOperator, SemijoinOperatorInfo> globalReductionFactorMap) throws SemanticException {
    for (Entry<FilterOperator, Collection<SemijoinOperatorInfo>> e : globalReductionFactorMap.asMap().entrySet()) {
      FilterOperator filterOp = e.getKey();
      Collection<SemijoinOperatorInfo> semijoinInfos = e.getValue();

      ExprNodeDesc pred = filterOp.getConf().getPredicate();
      if (FunctionRegistry.isOpAnd(pred)) {
        LinkedHashSet<ExprNodeDesc> allPreds = new LinkedHashSet<>(pred.getChildren());
        List<ExprNodeDesc> betweenPreds = new ArrayList<>();
        List<ExprNodeDesc> inBloomFilterPreds = new ArrayList<>();
        // We check whether we can find semijoin predicates
        for (SemijoinOperatorInfo roi : semijoinInfos) {
          for (ExprNodeDesc expr : pred.getChildren()) {
            if (FunctionRegistry.isOpBetween(expr) &&
                expr.getChildren().get(2) instanceof ExprNodeDynamicValueDesc) {
              // BETWEEN in SJ
              String dynamicValueIdFromExpr = ((ExprNodeDynamicValueDesc) expr.getChildren().get(2))
                  .getDynamicValue().getId();
              List<String> dynamicValueIdsFromMap = procCtx.parseContext.getRsToRuntimeValuesInfoMap()
                  .get(roi.rsOperator).getDynamicValueIDs();
              for (String dynamicValueIdFromMap : dynamicValueIdsFromMap) {
                if (dynamicValueIdFromExpr.equals(dynamicValueIdFromMap)) {
                  betweenPreds.add(expr);
                  allPreds.remove(expr);
                  break;
                }
              }
            } else if (FunctionRegistry.isOpInBloomFilter(expr) &&
                expr.getChildren().get(1) instanceof ExprNodeDynamicValueDesc) {
              // IN_BLOOM_FILTER in SJ
              String dynamicValueIdFromExpr = ((ExprNodeDynamicValueDesc) expr.getChildren().get(1))
                  .getDynamicValue().getId();
              List<String> dynamicValueIdsFromMap = procCtx.parseContext.getRsToRuntimeValuesInfoMap()
                  .get(roi.rsOperator).getDynamicValueIDs();
              for (String dynamicValueIdFromMap : dynamicValueIdsFromMap) {
                if (dynamicValueIdFromExpr.equals(dynamicValueIdFromMap)) {
                  inBloomFilterPreds.add(expr);
                  allPreds.remove(expr);
                  break;
                }
              }
            }
          }
        }

        List<ExprNodeDesc> newAndArgs = new ArrayList<>(allPreds); // First rest of predicates
        newAndArgs.addAll(betweenPreds);  // Then sorted between predicates
        newAndArgs.addAll(inBloomFilterPreds); // Finally, sorted in bloom predicates

        ExprNodeDesc andExpr = ExprNodeGenericFuncDesc.newInstance(
            FunctionRegistry.getFunctionInfo("and").getGenericUDF(), newAndArgs);
        filterOp.getConf().setPredicate(andExpr);
      }
    }
  }
Sort semijoin filters depending on the benefit (computed depending on selectivity and cost)
that they provide. We create three blocks: first all normal predicates, second between clauses
for the min/max dynamic values, and finally the in bloom filter predicates. The intuition is
that evaluating the between clause will be cheaper than evaluating the bloom filter predicates.
Hence, after this method runs, normal predicates come first (possibly sorted by Calcite),
then we will have sorted between clauses, and finally sorted in bloom filter clauses.
ql/src/java/org/apache/hadoop/hive/ql/parse/TezCompiler.java