All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alpine.model.pack.multiple.sql.CombinerSQLTransformer.scala Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
/*
 * COPYRIGHT (C) 2016 Alpine Data Labs Inc. All Rights Reserved.
 */

package com.alpine.model.pack.multiple.sql

import com.alpine.model.pack.multiple.CombinerModel
import com.alpine.model.pack.util.SQLModifyUtil
import com.alpine.sql.{AliasGenerator, SQLGenerator}
import com.alpine.transformer.sql.{ColumnName, LayeredSQLExpressions, SQLTransformer}

import scala.collection.mutable

case class CombinerSQLTransformer(model: CombinerModel, sqlGenerator: SQLGenerator) extends SQLTransformer {

  private val transformers = model.models.map(_.model.sqlTransformer(sqlGenerator).get)

  override def getSQL: LayeredSQLExpressions = {
    val sqls = transformers.map(t => t.getSQL)
    val combinedSQL = CombinerSQLTransformer.combineLayeredSQLExpressions(sqlGenerator, sqls)
    LayeredSQLExpressions(
      combinedSQL.layers.take(combinedSQL.layers.length - 1) ++
        Seq((combinedSQL.layers.last zip outputColumnNames).map {
          case ((expression, dummyColumnName), realColumnName) => (expression, realColumnName)
        })
    )
  }

}

object CombinerSQLTransformer {
  def combineLayeredSQLExpressions(sqlGenerator: SQLGenerator, sqls: Seq[LayeredSQLExpressions]): LayeredSQLExpressions = {
    val maxDepth = sqls.map(s => s.layers.length).max
    val aliasGenerator = new AliasGenerator("column")
    val paddedSQL: Seq[LayeredSQLExpressions] = sqls.map(s => {
      val padding = maxDepth - s.layers.length
      if (padding == 0) {
        s
      } else {
        val nextLayer = s.layers.last
        val aliases = nextLayer.map(f => {
          ColumnName(aliasGenerator.getNextAlias)
        })
        val paddingLayer = aliases.map(e => (e.asColumnarSQLExpression(sqlGenerator), e))

        LayeredSQLExpressions(s.layers.take(s.layers.length - 1)
          ++ Seq(s.layers.last.map { case (expression, name) => expression } zip aliases)
          ++ Range(0, padding).map(i => paddingLayer)
        )
      }
    })

    // Could replace this with a fold to avoid mutability, but I think that would make it even harder to read.
    val columnNamesByLayer = Range(0, maxDepth).map(i => mutable.Set[ColumnName]())
    val renamedSQL = paddedSQL.map(s => {
      var aliasMap: Option[Map[ColumnName, ColumnName]] = None
      s.layers.zipWithIndex.map {
        case (layer, layerIndex) =>
          val columnNamesForThisLayer = columnNamesByLayer(layerIndex)
          val originalColumnNames = layer.map { case (expression, columnName) => columnName }

          // Replace the column names in the expressions using the map from the last layer.
          val newExpressions = if (aliasMap.nonEmpty) {
            layer.map {
              case (expression, name) => SQLModifyUtil.replaceColumnNames(expression, aliasMap.get, sqlGenerator)
            }
          } else {
            layer.map {
              case (expression, name) => expression
            }
          }

          val nameConflicts = columnNamesForThisLayer intersect originalColumnNames.toSet
          if (nameConflicts.isEmpty) {
            columnNamesForThisLayer ++= originalColumnNames
            aliasMap = None
            newExpressions zip originalColumnNames
          } else {
            val columnNamesToAvoid = columnNamesForThisLayer union originalColumnNames.toSet
            aliasMap = Some(nameConflicts.map(n => (n, {
              var alias = ColumnName(aliasGenerator.getNextAlias)
              while (columnNamesToAvoid.contains(alias)) {
                alias = ColumnName(aliasGenerator.getNextAlias)
              }
              alias
            })).toMap)
            columnNamesForThisLayer ++= aliasMap.get.values

            val newNames = originalColumnNames.map(x => aliasMap.get.get(x) match {
              case Some(name) => name
              case None => x
            })

            newExpressions zip newNames
          }
      }
    })

    LayeredSQLExpressions(Range(0, maxDepth).map(i => renamedSQL.flatMap(s => s(i))))
  }

  def make(model: CombinerModel, sqlGenerator: SQLGenerator): Option[CombinerSQLTransformer] = {
    val sqlTransformers = model.models.map(_.model.sqlTransformer(sqlGenerator))
    val canBeScoredInSQL = sqlTransformers.forall(_.isDefined)
    if (canBeScoredInSQL) {
      Some(new CombinerSQLTransformer(model, sqlGenerator))
    } else {
      None
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy