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

molecule.sql.mysql.query.QueryExprSet_mysql.scala Maven / Gradle / Ivy

The newest version!
package molecule.sql.mysql.query

import molecule.core.query.Model2Query
import molecule.sql.core.query.{QueryExprSet, SqlQueryBase}


trait QueryExprSet_mysql
  extends QueryExprSet
    with LambdasSet_mysql { self: Model2Query with SqlQueryBase =>

  // attr ----------------------------------------------------------------------

  override protected def setAttr[T](
    col: String, res: ResSet[T], mandatory: Boolean
  ): Unit = {
    if (mandatory) {
      select -= col
      selectWithOrder(col, res.tpeDb, "JSON_ARRAYAGG")
      groupByCols -= col
      having += "COUNT(*) > 0"
      aggregate = true
      mandatoryCast(res, mandatory)
    }
  }

  override protected def setOptAttr[T](col: String, res: ResSet[T]): Unit = {
    select -= col
    selectWithOrder(col, res.tpeDb, "JSON_ARRAYAGG", optional = true)
    groupByCols -= col
    aggregate = true
    castStrategy.replace((row: RS, paramIndex: Int) =>
      res.json2optArray(row.getString(paramIndex)).map(_.toSet)
    )
  }

  override protected def setHas[T](
    col: String, set: Set[T], res: ResSet[T], one2json: T => String, mandatory: Boolean
  ): Unit = {
    def containsSet(set: Set[T]): String = {
      val jsonValues = set.map(one2json).mkString(", ")
      s"JSON_CONTAINS($col, JSON_ARRAY($jsonValues))"
    }
    if (mandatory) {
      select -= col
      // We need this to coalesce Sets
      selectWithOrder(col, res.tpeDb, "JSON_ARRAYAGG", optional = true)
      groupByCols -= col
      aggregate = true
      mandatoryCast(res, mandatory)
    }
    set.size match {
      case 0 => where += (("FALSE", ""))
      case 1 => where += (("", s"JSON_CONTAINS($col, JSON_ARRAY(${one2json(set.head)}))"))
      case _ => where += (("", set.map(v => containsSet(Set(v))).mkString("(", " OR\n   ", ")")))
    }
  }

  override protected def setHasNo[T](
    col: String, set: Set[T], res: ResSet[T], one2json: T => String, mandatory: Boolean
  ): Unit = {
    def notContains(v: T): String = s"NOT JSON_CONTAINS($col, JSON_ARRAY(${one2json(v)}))"
    def notContainsSet(set: Set[T]): String = {
      val jsonValues = set.map(one2json).mkString(", ")
      s"NOT JSON_CONTAINS($col, JSON_ARRAY($jsonValues))"
    }
    if (mandatory) {
      select -= col
      selectWithOrder(col, res.tpeDb, "JSON_ARRAYAGG", optional = true)
      groupByCols -= col
      aggregate = true
      mandatoryCast(res, mandatory)
    }
    set.size match {
      case 0 => ()
      case 1 => where += (("", notContains(set.head)))
      case _ => where += (("", set.map(v => notContainsSet(Set(v))).mkString("(", " AND\n   ", ")")))
    }
  }


  // filter attribute ----------------------------------------------------------

  override protected def setFilterHas[T](
    col: String, filterAttr: String, res: ResSet[T], mandatory: Boolean
  ): Unit = {
    where += (("", hasClause(col, filterAttr, res)))
    mandatoryCast(res, mandatory)
  }

  override protected def setFilterHasNo[T](
    col: String, filterAttr: String, res: ResSet[T], mandatory: Boolean
  ): Unit = {
    if (mandatory) {
      val i = getIndex
      select -= col
      select += s"JSON_ARRAYAGG(t_$i.vs)"
      having += "COUNT(*) > 0"
      aggregate = true
      groupByCols -= col
      val tpeDb = res.tpeDb
      tempTables += s"JSON_TABLE($col, '$$[*]' COLUMNS (vs $tpeDb PATH '$$')) t_$i"
      mandatoryCast(res, true)
    }
    where += (("", s"NOT ${hasClause(col, filterAttr, res)}"))
  }


  // helpers -------------------------------------------------------------------

  private def selectWithOrder(
    col: String,
    tpeDb: String,
    fn: String,
    distinct: String = "",
    optional: Boolean = false
  ): Unit = {
    val i  = getIndex
    val vs = s"t_$i.vs"
    if (orderBy.nonEmpty && orderBy.last._3 == col) {
      // order by aggregate alias instead
      val alias = col.replace('.', '_') + "_" + fn.toLowerCase
      select += s"$fn($distinct$vs) $alias"
      val (level, _, _, dir) = orderBy.last
      orderBy.remove(orderBy.size - 1)
      orderBy += ((level, 1, alias, dir))
    } else {
      select += s"$fn($distinct$vs)"
    }
    if (optional) {
      joins += ((
        "LEFT OUTER JOIN",
        s"JSON_TABLE($col, '$$[*]' COLUMNS (vs $tpeDb PATH '$$')) t_$i",
        "",
        List("true")
      ))

    } else {
      tempTables +=
        s"""JSON_TABLE(
           |    IF($col IS NULL, '[null]', $col),
           |    '$$[*]' COLUMNS (vs $tpeDb PATH '$$')
           |  ) t_$i""".stripMargin
    }
  }

  private def mandatoryCast[T](res: ResSet[T], mandatory: Boolean): Unit = {
    if (mandatory) {
      castStrategy.replace((row: RS, paramIndex: Int) =>
        res.json2array(row.getString(paramIndex)).toSet
      )
    }
  }

  private def hasClause[T](col: String, filterAttr: String, res: ResSet[T]): String = {
    res.tpe match {
      case "BigInt" =>
        s"JSON_CONTAINS($col, JSON_ARRAY(CAST($filterAttr AS CHAR)))"

      case "BigDecimal" =>
        // Compare Decimals, not Strings.
        // String representation of filterAttr pads 0's so we can't use that
        // against the truncated String representations in the json array
        s"""(
           |    SELECT count(_v) > 0
           |    FROM
           |      JSON_TABLE(
           |        $col, '$$[*]'
           |        COLUMNS(_v varchar(65) path '$$')
           |      ) AS alias
           |    WHERE CONVERT(_v, DECIMAL(65, 30)) = $filterAttr
           |  )"""

      case _ => s"JSON_CONTAINS($col, JSON_ARRAY($filterAttr))"
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy