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

quasar.physical.mongodb.reshape.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

import scala.Predef.$conforms
import slamdata.Predef._
import quasar.fp._
import quasar.fp.ski._
import quasar._
import quasar.physical.mongodb.accumulator._
import quasar.physical.mongodb.expression._

import scala.collection.immutable.Iterable

import matryoshka._
import matryoshka.data.Fix
import matryoshka.implicits._
import scalaz._, Scalaz._

final case class Grouped[EX[_]](value: ListMap[BsonField.Name, AccumOp[Fix[EX]]]) {
  def bson(implicit ev0: ExprOpOps.Uni[EX], ev1: Functor[EX]) =
    Bson.Doc(value.map(t => t._1.asText -> groupBson(t._2)))

  def rewriteRefs(f: PartialFunction[DocVar, DocVar])
      (implicit ev0: ExprOpOps.Uni[EX], ev1: Functor[EX]): Grouped[EX] =
    Grouped(value.transform((_, v) => rewriteGroupRefs(v)(f)))
}
object Grouped {

  def grouped[EX[_]](shape: (String, AccumOp[Fix[EX]])*) =
    Grouped(ListMap(shape.map { case (k, v) => BsonField.Name(k) -> v}: _*))

  implicit def GroupedRenderTree[EX[_]: Functor](implicit ev: ExprOpOps.Uni[EX])
      : RenderTree[Grouped[EX]] = new RenderTree[Grouped[EX]] {
    val GroupedNodeType = List("Grouped")

    def render(grouped: Grouped[EX]) =
      NonTerminal(GroupedNodeType, None,
        (grouped.value.map { case (name, expr) => Terminal("Name" :: GroupedNodeType, Some(name.bson.toJs.pprint(0) + " -> " + groupBson(expr).toJs.pprint(0))) } ).toList)
  }
}

final case class Reshape[EX[_]](value: ListMap[BsonField.Name, Reshape.Shape[EX]]) {
  import Reshape.Shape

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def bson(implicit ops: ExprOpOps.Uni[EX], ev: Functor[EX]): Bson.Doc = Bson.Doc(value.map {
    case (field, either) => field.asText -> either.fold(_.bson, _.cata(ops.bson))
  })

  private def projectSeq(fs: NonEmptyList[BsonField.Name]): Option[Shape[EX]] =
    fs.foldLeftM[Option, Shape[EX]](-\/(this))((rez, leaf) =>
      rez.fold(
        r => leaf match {
          case n @ BsonField.Name(_) => r.get(n)
          case _                     => None
        },
        κ(None)))

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def rewriteRefs(applyVar: PartialFunction[DocVar, DocVar])(implicit
      ops: ExprOpOps.Uni[EX],
      ev0: ExprOpCoreF :<: EX,
      ev1: Functor[EX]): Reshape[EX] =
    Reshape(value.transform((k, v) => v.bimap(
      _.rewriteRefs(applyVar),
      x => (x match {
        case $include() => (new ExprOpCoreF.fixpoint[Fix[EX], EX](_.embed)).$var(DocField(k))
        case _          => x
      }).cata(ops.rewriteRefs(applyVar)))))

  def \ (f: BsonField): Option[Shape[EX]] = projectSeq(f.flatten)

  def get(field: BsonField): Option[Shape[EX]] =
    field.flatten.foldLeftM[Option, Shape[EX]](
      -\/(this))(
      (rez, elem) => rez.fold(_.value.get(elem), κ(None)))

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def find(expr: Fix[EX]): Option[BsonField] =
    value.foldLeft[Option[BsonField]](
      None) {
      case (acc, (field, value)) =>
        acc.orElse(value.fold(
          _.find(expr).map(field \ _),
          ex => if (ex == expr) field.some else None))
    }

  def set(field: BsonField, newv: Shape[EX]): Reshape[EX] = {
    def getOrDefault(o: Option[Shape[EX]]): Reshape[EX] =
      o.cata(
        _.fold(ι, κ(Reshape.emptyDoc[EX])),
        Reshape.emptyDoc[EX])

    @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements", "org.wartremover.warts.Recursion"))
    def set0(cur: Reshape[EX], els: List[BsonField.Name]): Reshape[EX] = els match {
      case Nil => ??? // TODO: Refactor els to be NonEmptyList
      case (x @ BsonField.Name(_)) :: Nil => Reshape(cur.value + (x -> newv))
      case (x @ BsonField.Name(_)) :: xs =>
        Reshape(cur.value + (x -> -\/(set0(getOrDefault(cur.value.get(x)), xs))))
    }

    set0(this, field.flatten.toList)
  }
}

object Reshape {
  type Shape[EX[_]] = Reshape[EX] \/ Fix[EX]

  def reshape(shape: (String, Shape[ExprOp])*) =
    Reshape(ListMap(shape.map { case (k, v) => BsonField.Name(k) -> v}: _*))

  def emptyDoc[EX[_]] = Reshape[EX](ListMap())

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def getAll[EX[_]](r: Reshape[EX]): List[(BsonField, Fix[EX])] = {
    def getAll0(f0: BsonField, e: Shape[EX]) = e.fold(
      r => getAll(r).map { case (f, e) => (f0 \ f) -> e },
      e => (f0 -> e) :: Nil)

    r.value.toList.map { case (f, e) => getAll0(f, e) }.flatten
  }

  def setAll[EX[_]](r: Reshape[EX], fvs: Iterable[(BsonField, Shape[EX])]) =
    fvs.foldLeft(r) {
      case (r0, (field, value)) => r0.set(field, value)
    }

  def mergeMaps[A, B](lmap: ListMap[A, B], rmap: ListMap[A, B]):
      Option[ListMap[A, B]] =
    if ((lmap.keySet & rmap.keySet).forall(k => lmap.get(k) == rmap.get(k)))
      Some(lmap ++ rmap)
    else None

  def merge[EX[_]](r1: Reshape[EX], r2: Reshape[EX]): Option[Reshape[EX]] = {
    val lmap = Reshape.getAll(r1).map(t => t._1 -> \/-(t._2)).toListMap
    val rmap = Reshape.getAll(r2).map(t => t._1 -> \/-(t._2)).toListMap
    if ((lmap.keySet & rmap.keySet).forall(k => lmap.get(k) == rmap.get(k)))
      Some(Reshape.setAll(
        r1,
        Reshape.getAll(r2).map(t => t._1 -> \/-(t._2))))
    else None
  }

  private val ProjectNodeType = List("Project")

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  private[mongodb] def renderReshape[EX[_]: Functor](shape: Reshape[EX])(implicit ops: ExprOpOps.Uni[EX]): List[RenderedTree] = {
    def renderField(field: BsonField, value: Shape[EX]) = {
      val (label, typ) = field.bson.toJs.pprint(0) -> "Name"
      value match {
        case -\/ (shape)  => NonTerminal(typ :: ProjectNodeType, Some(label), renderReshape(shape))
        case  \/-(exprOp) => Terminal(typ :: ProjectNodeType, Some(label + " -> " + exprOp.cata(ops.bson).toJs.pprint(0)))
      }
    }

    shape.value.map((renderField(_, _)).tupled).toList
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy