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

quasar.physical.mongodb.planner.FuncHandler.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014–2017 SlamData Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package quasar.physical.mongodb.planner

import slamdata.Predef._
import quasar.physical.mongodb.{Bson, BsonCodec, BsonVersion}
import quasar.physical.mongodb.expression._
import quasar.qscript.{MapFuncsDerived => D,  _}, MapFuncsCore._
import quasar.qscript.rewrites.{Coalesce => _}

import matryoshka._
import matryoshka.data._
import matryoshka.implicits._
import matryoshka.patterns._
import scalaz.{Divide => _, Split => _, _}, Scalaz._
import simulacrum.typeclass

@typeclass trait FuncHandler[IN[_]] {

  def handleOpsCore[EX[_]: Functor](v: BsonVersion)
    (implicit e32: ExprOpCoreF :<: EX)
      : IN ~> OptionFree[EX, ?]

  def handleOps3_4[EX[_]: Functor](v: BsonVersion)
    (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX)
      : IN ~> OptionFree[EX, ?]

  def handleOps3_4_4[EX[_]: Functor](v: BsonVersion)
    (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX, e344: ExprOp3_4_4F :<: EX)
      : IN ~> OptionFree[EX, ?]

  def handle3_2(v: BsonVersion)
     : IN ~> OptionFree[Expr3_2, ?] =
    λ[IN ~> OptionFree[Expr3_2, ?]]{f =>
      val h = handleOpsCore[Expr3_2](v)
      h(f)
    }

  def handle3_4(v: BsonVersion)
     : IN ~> OptionFree[Expr3_4, ?] =
    λ[IN ~> OptionFree[Expr3_4, ?]]{f =>
      val h34 = handleOps3_4[Expr3_4](v)
      val h = handleOpsCore[Expr3_4](v)
      h34(f) orElse h(f)
    }

  def handle3_4_4(v: BsonVersion)
     : IN ~> OptionFree[Expr3_4_4, ?] =
    λ[IN ~> OptionFree[Expr3_4_4, ?]]{f =>
      val h344 = handleOps3_4_4[Expr3_4_4](v)
      val h34 = handleOps3_4[Expr3_4_4](v)
      val h = handleOpsCore[Expr3_4_4](v)
      h344(f) orElse h34(f) orElse h(f)
    }
}

object FuncHandler {

  implicit def mapFuncCore[T[_[_]]: BirecursiveT]: FuncHandler[MapFuncCore[T, ?]] =
    new FuncHandler[MapFuncCore[T, ?]] {

      def handleOpsCore[EX[_]: Functor]
        (v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX)
          : MapFuncCore[T, ?] ~> OptionFree[EX, ?] =
        new (MapFuncCore[T, ?] ~> OptionFree[EX, ?]) {

          implicit def hole[D](d: D): Free[EX, D] = Free.pure(d)

          def apply[A](mfc: MapFuncCore[T, A]): OptionFree[EX, A] = {

            val fp = new ExprOpCoreF.fixpoint[Free[EX, A], EX](Free.roll)
            import fp._
            import FormatSpecifier._

            def partial(mfc: MapFuncCore[T, A]): OptionFree[EX, A] = mfc.some collect {
              case Undefined()           => $literal(Bson.Undefined)
              case Add(a1, a2)           => $add(a1, a2)
              case Multiply(a1, a2)      => $multiply(a1, a2)
              case Subtract(a1, a2)      => $subtract(a1, a2)
              case Divide(a1, a2)        =>
                // NB: It’s apparently intential that division by zero crashes
                //     the query in MongoDB. See
                //     https://jira.mongodb.org/browse/SERVER-29410
                // TODO: It would be nice if we would be able to generate simply
                //       $divide(a1, a2) for $literal denominators, but the type
                //       of a2 is generic so we can't check it here.
                $cond($eq(a2, $literal(Bson.Int32(0))),
                  $cond($eq(a1, $literal(Bson.Int32(0))),
                    $literal(Bson.Dec(Double.NaN)),
                    $cond($gt(a1, $literal(Bson.Int32(0))),
                      $literal(Bson.Dec(Double.PositiveInfinity)),
                      $literal(Bson.Dec(Double.NegativeInfinity)))),
                  $divide(a1, a2))
              case Modulo(a1, a2)        => $mod(a1, a2)
              case Negate(a1)            => $multiply($literal(Bson.Int32(-1)), a1)
              case MapFuncsCore.Eq(a1, a2)   => $eq(a1, a2)
              case Neq(a1, a2)           => $neq(a1, a2)
              case Lt(a1, a2)            => $lt(a1, a2)
              case Lte(a1, a2)           => $lte(a1, a2)
              case Gt(a1, a2)            => $gt(a1, a2)
              case Gte(a1, a2)           => $gte(a1, a2)

              // FIXME: this is valid for strings only

              case Lower(a1)             => $toLower(a1)
              case Upper(a1)             => $toUpper(a1)
              case Substring(a1, a2, a3) => $substr(a1, a2, a3)
              case Cond(a1, a2, a3)      => $cond(a1, a2, a3)

              case Or(a1, a2)            => $or(a1, a2)
              case And(a1, a2)           => $and(a1, a2)
              case Not(a1)               => $not(a1)

              case Null(a1) =>
                $cond($eq(a1, $literal(Bson.Text("null"))),
                  $literal(Bson.Null),
                  $literal(Bson.Undefined))

              case Bool(a1) =>
                $cond($eq(a1, $literal(Bson.Text("true"))),
                  $literal(Bson.Bool(true)),
                  $cond($eq(a1, $literal(Bson.Text("false"))),
                    $literal(Bson.Bool(false)),
                    $literal(Bson.Undefined)))

              case ExtractCentury(a1) =>
                $trunc($divide($add($year(a1), $literal(Bson.Int32(99))), $literal(Bson.Int32(100))))
              case ExtractDayOfMonth(a1) => $dayOfMonth(a1)
              case ExtractDecade(a1) => $trunc($divide($year(a1), $literal(Bson.Int32(10))))
              case ExtractDayOfWeek(a1) => $subtract($dayOfWeek(a1), $literal(Bson.Int32(1)))
              case ExtractDayOfYear(a1) => $dayOfYear(a1)
              case ExtractEpoch(a1) =>
                $divide(
                  $subtract(a1, $literal(Bson.Date(0))),
                  $literal(Bson.Int32(1000)))
              case ExtractHour(a1) => $hour(a1)
              case ExtractIsoDayOfWeek(a1) =>
                $cond($eq($dayOfWeek(a1), $literal(Bson.Int32(1))),
                  $literal(Bson.Int32(7)),
                  $subtract($dayOfWeek(a1), $literal(Bson.Int32(1))))
              // TODO: case ExtractIsoYear(a1) =>
              case ExtractMicroseconds(a1) =>
                $multiply(
                  $add(
                    $multiply($second(a1), $literal(Bson.Int32(1000))),
                    $millisecond(a1)),
                  $literal(Bson.Int32(1000)))
              case ExtractMillennium(a1) =>
                $trunc($divide($add($year(a1), $literal(Bson.Int32(999))), $literal(Bson.Int32(1000))))
              case ExtractMilliseconds(a1) =>
                $add(
                  $multiply($second(a1), $literal(Bson.Int32(1000))),
                  $millisecond(a1))
              case ExtractMinute(a1) => $minute(a1)
              case ExtractMonth(a1) => $month(a1)
              case ExtractQuarter(a1) =>
                $trunc(
                  $add(
                    $divide(
                      $subtract($month(a1), $literal(Bson.Int32(1))),
                      $literal(Bson.Int32(3))),
                    $literal(Bson.Int32(1))))
              case ExtractSecond(a1) =>
                $add($second(a1), $divide($millisecond(a1), $literal(Bson.Int32(1000))))
              case ExtractWeek(a1) => $week(a1)
              case ExtractYear(a1) => $year(a1)

              case ToTimestamp(a1) =>
               $add($literal(Bson.Date(0)), a1)

              case Between(a1, a2, a3)   => $and($lte(a2, a1), $lte(a1, a3))
              case TimeOfDay(a1) =>
                $dateToString(Hour :: ":" :: Minute :: ":" :: Second :: "." :: Millisecond :: FormatString.empty, a1)
              case Power(a1, a2) => $pow(a1, a2)
              case ProjectIndex(a1, a2) => $arrayElemAt(a1, a2)
              case MakeArray(a1) => $arrayLit(List(a1))
              case ConcatArrays(a1, a2) =>
                $let(ListMap(DocVar.Name("a1") -> a1, DocVar.Name("a2") -> a2),
                  $cond($and($isArray($field("$a1")), $isArray($field("$a2"))),
                    $concatArrays(List($field("$a1"), $field("$a2"))),
                    $concat($field("$a1"), $field("$a2"))))
              case TypeOf(a1) => mkTypeOf(a1, $isArray)
            }

            partial(mfc) orElse (mfc match {
              case Constant(v1)  => v1.cataM(BsonCodec.fromEJson(v)).toOption.map($literal(_))
              case _             => None
            })
          }
        }

      def handleOps3_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX)
          : MapFuncCore[T, ?] ~> OptionFree[EX, ?] =
        new (MapFuncCore[T, ?] ~> OptionFree[EX, ?]){
          implicit def hole[D](d: D): Free[EX, D] = Free.pure(d)

          def apply[A](mfc: MapFuncCore[T, A]): OptionFree[EX, A] = {
            val fp32  = new ExprOpCoreF.fixpoint[Free[EX, A], EX](Free.roll)
            val fp34  = new ExprOp3_4F.fixpoint[Free[EX, A], EX](Free.roll)

            import fp32._, fp34._

            mfc.some collect {
              case Split(a1, a2) => $split(a1, a2)
              case Substring(a1, a2, a3) =>
                $cond($or(
                    $lt(a2, $literal(Bson.Int32(0))),
                    $gt(a2, $strLenCP(a1))),
                  $literal(Bson.Text("")),
                  $cond(
                    $lt(a3, $literal(Bson.Int32(0))),
                    $substrCP(a1, a2, $strLenCP(a1)),
                    $substrCP(a1, a2, a3)))
              case Length(a1) => $strLenCP(a1)
            }
          }
        }

      def handleOps3_4_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX, e344: ExprOp3_4_4F :<: EX)
          : MapFuncCore[T, ?] ~> OptionFree[EX, ?] =
        new (MapFuncCore[T, ?] ~> OptionFree[EX, ?]){

          def apply[A](mfc: MapFuncCore[T, A]): OptionFree[EX, A] = None
        }

    }

  def mapFuncDerived[T[_[_]]: CorecursiveT]
      : FuncHandler[MapFuncDerived[T, ?]] =
    new FuncHandler[MapFuncDerived[T, ?]] {
      def emptyDerived[T[_[_]], F[_]]: MapFuncDerived[T, ?] ~> OptionFree[F, ?] =
         λ[MapFuncDerived[T, ?] ~> OptionFree[F, ?]] { _ => None }

      def handleOpsCore[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] =
        new (MapFuncDerived[T, ?] ~> OptionFree[EX, ?]){
          implicit def hole[D](d: D): Free[EX, D] = Free.pure(d)

          def apply[A](fa: MapFuncDerived[T, A]): OptionFree[EX, A] = {
            val fp = new ExprOpCoreF.fixpoint[Free[EX, A], EX](Free.roll)
            import fp._

            fa.some collect {
              case D.Abs(a1)       => $abs(a1)
              case D.Ceil(a1)      => $ceil(a1)
              case D.Floor(a1)     => $floor(a1)
              case D.Trunc(a1)     => $trunc(a1)
            }
          }
        }

      def handleOps3_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] =
        emptyDerived

      def handleOps3_4_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX, e344: ExprOp3_4_4F :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] =
        emptyDerived
    }

  implicit def mapFuncDerivedUnhandled[T[_[_]]: CorecursiveT]
    (implicit core: FuncHandler[MapFuncCore[T, ?]])
      : FuncHandler[MapFuncDerived[T, ?]] =
    new FuncHandler[MapFuncDerived[T, ?]] {
      val derived = mapFuncDerived

      private def handleUnhandled[F[_]]
        (derived: MapFuncDerived[T, ?] ~> OptionFree[F, ?], core: MapFuncCore[T, ?] ~> OptionFree[F, ?])
          : MapFuncDerived[T, ?] ~> OptionFree[F, ?] =
            new (MapFuncDerived[T, ?] ~> OptionFree[F, ?]) {
              def apply[A](f: MapFuncDerived[T, A]): OptionFree[F, A] = {
                val alg: AlgebraM[Option, CoEnv[A, MapFuncCore[T,?], ?], Free[F,A]] =
                  _.run.fold[OptionFree[F, A]](x => Free.point(x).some, core(_).map(_.join))
                derived(f)
                  .orElse(Free.roll(ExpandMapFunc.mapFuncDerived[T, MapFuncCore[T, ?]].expand(f)).cataM(alg))
            }
          }

      def handleOpsCore[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] =
        handleUnhandled(derived.handleOpsCore(v), core.handleOpsCore(v))

      def handleOps3_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] = {

        val hCore = λ[MapFuncCore[T, ?] ~> OptionFree[EX, ?]]{f =>
          val h34 = core.handleOps3_4[EX](v)
          val h = core.handleOpsCore[EX](v)
          h34(f) orElse h(f)
        }
        handleUnhandled(derived.handleOps3_4(v), hCore)
      }

      def handleOps3_4_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX, e344: ExprOp3_4_4F :<: EX)
          : MapFuncDerived[T, ?] ~> OptionFree[EX, ?] = {

        val hCore = λ[MapFuncCore[T, ?] ~> OptionFree[EX, ?]]{f =>
          val h344 = core.handleOps3_4_4[EX](v)
          val h34 = core.handleOps3_4[EX](v)
          val h = core.handleOpsCore[EX](v)
          h344(f) orElse h34(f) orElse h(f)
        }
        handleUnhandled(derived.handleOps3_4_4(v), hCore)
      }
    }

  implicit def mapFuncCoproduct[F[_], G[_]]
      (implicit F: FuncHandler[F], G: FuncHandler[G])
      : FuncHandler[Coproduct[F, G, ?]] =
    new FuncHandler[Coproduct[F, G, ?]] {
      def handleOpsCore[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX)
          : Coproduct[F, G, ?] ~> OptionFree[EX, ?] =
        λ[Coproduct[F, G, ?] ~> OptionFree[EX, ?]](_.run.fold(
          F.handleOpsCore[EX](v).apply _,
          G.handleOpsCore[EX](v).apply _
        ))

      def handleOps3_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX)
          : Coproduct[F, G, ?] ~> OptionFree[EX, ?] =
        λ[Coproduct[F, G, ?] ~> OptionFree[EX, ?]](_.run.fold(
          F.handleOps3_4[EX](v).apply _,
          G.handleOps3_4[EX](v).apply _
        ))

      def handleOps3_4_4[EX[_]: Functor](v: BsonVersion)
        (implicit e32: ExprOpCoreF :<: EX, e34: ExprOp3_4F :<: EX, e344: ExprOp3_4_4F :<: EX)
          : Coproduct[F, G, ?] ~> OptionFree[EX, ?] =
        λ[Coproduct[F, G, ?] ~> OptionFree[EX, ?]](_.run.fold(
          F.handleOps3_4_4[EX](v).apply _,
          G.handleOps3_4_4[EX](v).apply _
        ))

    }

  def handle3_2[F[_]: FuncHandler](v: BsonVersion): F ~> OptionFree[Expr3_2, ?] =
    FuncHandler[F].handle3_2(v)

  def handle3_4[F[_]: FuncHandler](v: BsonVersion): F ~> OptionFree[Expr3_4, ?] =
    FuncHandler[F].handle3_4(v)

  def handle3_4_4[F[_]: FuncHandler](v: BsonVersion): F ~> OptionFree[Expr3_4_4, ?] =
    FuncHandler[F].handle3_4_4(v)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy