<commit_before>package ingraph.compiler.cypher2qplan

import ingraph.compiler.cypher2qplan.builders.AttributeBuilder
import ingraph.model.expr.ElementAttribute
import ingraph.model.{expr, qplan}
import org.apache.spark.sql.catalyst.analysis.UnresolvedAttribute
import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
import org.apache.spark.sql.catalyst.{expressions => cExpr}
import org.apache.spark.sql.types.BooleanType

import scala.collection.mutable
import scala.collection.mutable.ListBuffer

object QPlanBeautifier {
  def beautifyResolvedQPlan(resolvedQueryPlan: qplan.QNode): qplan.QNode = {
    // should there be other rule sets (partial functions), combine them using orElse,
    // e.g. pfunc1 orElse pfunc2
    /*
     * FIXME: douplicated transform call is to allow for antijoin beautification.
     * This should be done dynamically
     */
    val beautiful = resolvedQueryPlan.transform(commonBeautifier).transform(commonBeautifier)

    beautiful.asInstanceOf[qplan.QNode]
  }

  def beautifyUnresolvedQPlan(unresolvedQueryPlan: qplan.QNode): qplan.QNode = {
    // should there be other rule sets (partial functions), combine them using orElse,
    // e.g. pfunc1 orElse pfunc2
    val beautiful = unresolvedQueryPlan.transform(commonBeautifier)

    beautiful.asInstanceOf[qplan.QNode]
  }

  /**
    * These are the structural beautifier rules that applies to all QPlans,
    * irrespectively whether it is resolved or not.
    *
    * Note: most rules will will be common.
    */
  val commonBeautifier: PartialFunction[LogicalPlan, LogicalPlan] = {
    case qplan.Join(qplan.Dual(), o2) => o2
      // FIXME: projection should be introduced by the compiler, and also match it here
    case qplan.Selection(cExpr.Not(e), qplan.LeftOuterJoin(base, filter)) if checkForAntijoin(e, filter) => qplan.AntiJoin(base, filter)
    case qplan.Selection(cExpr.Not(e), qplan.LeftOuterJoin(filter, base)) if checkForAntijoin(e, filter) => qplan.AntiJoin(base, filter)
    case qplan.Selection(cExpr.Literal(true, BooleanType), child) => child
    case qplan.Selection(condition, child) => qplan.Selection(condition.transform(expressionResolver), child)
  }

  val expressionResolver: PartialFunction[Expression, Expression] = {
    case cExpr.And(cExpr.Literal(true, BooleanType), r) => r
    case cExpr.And(l, cExpr.Literal(true, BooleanType)) => l
    case cExpr.Or(cExpr.Literal(false, BooleanType), r) => r
    case cExpr.Or(l, cExpr.Literal(false, BooleanType)) => l
  }

  /**
    * This checks whether the expression and the filter forms a combination which is candidate for antijoin.
    *
    * Checks performed:
    *  1. expression if of the form AND_i{IS NOT NULL attr_i}. Note: the negation is already matched at an outer level
    *  2. filter is a chain of the form Expand*GetVertices, where * stands for 0, 1 or more occurences
    *  3. attr_i in (1) correspond to those in the chain of (2)<commit_msg>Fix naming of expressionBeautifier<commit_after>package ingraph.compiler.cypher2qplan

import ingraph.compiler.cypher2qplan.builders.AttributeBuilder
import ingraph.model.expr.ElementAttribute
import ingraph.model.{expr, qplan}
import org.apache.spark.sql.catalyst.analysis.UnresolvedAttribute
import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
import org.apache.spark.sql.catalyst.{expressions => cExpr}
import org.apache.spark.sql.types.BooleanType

import scala.collection.mutable
import scala.collection.mutable.ListBuffer

object QPlanBeautifier {
  def beautifyResolvedQPlan(resolvedQueryPlan: qplan.QNode): qplan.QNode = {
    // should there be other rule sets (partial functions), combine them using orElse,
    // e.g. pfunc1 orElse pfunc2
    /*
     * FIXME: douplicated transform call is to allow for antijoin beautification.
     * This should be done dynamically
     */
    val beautiful = resolvedQueryPlan.transform(commonBeautifier).transform(commonBeautifier)

    beautiful.asInstanceOf[qplan.QNode]
  }

  def beautifyUnresolvedQPlan(unresolvedQueryPlan: qplan.QNode): qplan.QNode = {
    // should there be other rule sets (partial functions), combine them using orElse,
    // e.g. pfunc1 orElse pfunc2
    val beautiful = unresolvedQueryPlan.transform(commonBeautifier)

    beautiful.asInstanceOf[qplan.QNode]
  }

  /**
    * These are the structural beautifier rules that applies to all QPlans,
    * irrespectively whether it is resolved or not.
    *
    * Note: most rules will will be common.
    */
  val commonBeautifier: PartialFunction[LogicalPlan, LogicalPlan] = {
    case qplan.Join(qplan.Dual(), o2) => o2
      // FIXME: projection should be introduced by the compiler, and also match it here
    case qplan.Selection(cExpr.Not(e), qplan.LeftOuterJoin(base, filter)) if checkForAntijoin(e, filter) => qplan.AntiJoin(base, filter)
    case qplan.Selection(cExpr.Not(e), qplan.LeftOuterJoin(filter, base)) if checkForAntijoin(e, filter) => qplan.AntiJoin(base, filter)
    case qplan.Selection(cExpr.Literal(true, BooleanType), child) => child
    case qplan.Selection(condition, child) => qplan.Selection(condition.transform(expressionBeautifier), child)
  }

  val expressionBeautifier: PartialFunction[Expression, Expression] = {
    case cExpr.And(cExpr.Literal(true, BooleanType), r) => r
    case cExpr.And(l, cExpr.Literal(true, BooleanType)) => l
    case cExpr.Or(cExpr.Literal(false, BooleanType), r) => r
    case cExpr.Or(l, cExpr.Literal(false, BooleanType)) => l
  }

  /**
    * This checks whether the expression and the filter forms a combination which is candidate for antijoin.
    *
    * Checks performed:
    *  1. expression if of the form AND_i{IS NOT NULL attr_i}. Note: the negation is already matched at an outer level
    *  2. filter is a chain of the form Expand*GetVertices, where * stands for 0, 1 or more occurences
    *  3. attr_i in (1) correspond to those in the chain of (2)
{'fraction_non_alphanumeric': 0.09632951336987211, 'fraction_numerical': 0.004982561036372696, 'mean_word_length': 4.209342560553633, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5014449', 'n_tokens_mistral': 1870, 'n_tokens_neox': 1823, 'n_words': 510}