diff --git a/README.md b/README.md index ad4c46a33..c59453af2 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,13 @@ lazy val yourProject = project.in(file("app")) Resolver.sonatypeRepo("releases") ), libraryDependencies ++= Seq( - "org.scalamolecule" %% "molecule" % "0.18.3", + "org.scalamolecule" %% "molecule" % "0.18.4", "com.datomic" % "datomic-free" % "0.9.5697" ), moleculeSchemas := Seq("app") // paths to your schema definition files... ) ``` -Molecule 0.18.3 for Scala 2.12.8 is available at +Molecule 0.18.4 for Scala 2.12.8 is available at [Sonatype](https://oss.sonatype.org/content/repositories/releases/org/scalamolecule/molecule_2.12/). diff --git a/build.sbt b/build.sbt index 1be19ba80..ccc9291e8 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ import sbt.compilerPlugin lazy val commonSettings = Defaults.coreDefaultSettings ++ Seq( organization := "org.scalamolecule", - version := "0.18.3", + version := "0.18.4", scalaVersion := "2.12.8", scalacOptions := Seq( "-feature", @@ -18,8 +18,7 @@ lazy val commonSettings = Defaults.coreDefaultSettings ++ Seq( // ,"-Ydebug" ), resolvers ++= Seq( - "datomic" at "http://files.datomic.com/maven", // free - // "datomic" at "https://my.datomic.com/repo", // pro + "datomic" at "http://files.datomic.com/maven", "clojars" at "http://clojars.org/repo", Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots"), @@ -29,8 +28,6 @@ lazy val commonSettings = Defaults.coreDefaultSettings ++ Seq( libraryDependencies ++= Seq( "org.scala-lang" % "scala-reflect" % scalaVersion.value, "com.datomic" % "datomic-free" % "0.9.5697", - // "com.datomic" % "datomic-pro" % "0.9.5783", - // "com.datomic" % "client-pro" % "0.8.20", "org.specs2" %% "specs2-core" % "4.2.0" % "test", compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.patch) ), @@ -105,6 +102,7 @@ lazy val publishSettings = Seq( licenses := Seq("Apache 2" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), scmInfo := Some(ScmInfo(url("https://github.com/scalamolecule/molecule"), "scm:git:git@github.com:scalamolecule/molecule.git")), credentials += Credentials(Path.userHome / ".sbt" / ".credentials"), + credentials += Credentials(Path.userHome / ".sbt" / "1.0" / "sonatype.sbt"), pomExtra := @@ -116,6 +114,7 @@ lazy val publishSettings = Seq( ) lazy val noPublishSettings = Seq( + skip in publish := true, publish := ((): Unit), publishLocal := ((): Unit), publishArtifact in(Compile, packageDoc) := false, diff --git a/core/src/main/scala/molecule/api/get/GetList.scala b/core/src/main/scala/molecule/api/get/GetList.scala index cc32e6cb9..c2bfc0f3b 100644 --- a/core/src/main/scala/molecule/api/get/GetList.scala +++ b/core/src/main/scala/molecule/api/get/GetList.scala @@ -45,8 +45,10 @@ trait GetList[Tpl] extends GetArray[Tpl] { self: Molecule[Tpl] => buf.toList } + def getListOf[T](implicit conn: Conn): List[T] = ??? - /** Get `List` of n rows as tuples matching molecule. + + /** Get `List` of n rows as tuples matching molecule. *

* Only n rows are type-casted. * {{{ diff --git a/core/src/main/scala/molecule/ast/query.scala b/core/src/main/scala/molecule/ast/query.scala index 010e4e9c1..ff86f34ab 100644 --- a/core/src/main/scala/molecule/ast/query.scala +++ b/core/src/main/scala/molecule/ast/query.scala @@ -1,6 +1,4 @@ package molecule.ast -import java.net.URI -import java.util.{Date, UUID} import molecule.transform.Query2String import molecule.util.Helpers diff --git a/core/src/main/scala/molecule/factory/Molecule_Factory.scala b/core/src/main/scala/molecule/factory/Molecule_Factory.scala index d6fe63bf2..da8be664a 100644 --- a/core/src/main/scala/molecule/factory/Molecule_Factory.scala +++ b/core/src/main/scala/molecule/factory/Molecule_Factory.scala @@ -49,10 +49,6 @@ trait Molecule_Factory2 { * * @group molecule * @param dsl User-defined DSL structure modelling the molecule - * @tparam Ns1 Internal builder pattern type - * @tparam Ns2 Internal builder pattern type - * @tparam In1_1 Internal builder pattern type - * @tparam In1_2 Internal builder pattern type * @tparam A Type of output attribute 1 (`name`: String) * @return Molecule of arity-1 typed to first attribute (Molecule01[A]) */ @@ -79,10 +75,6 @@ trait Molecule_Factory2 { * * @group molecule * @param dsl User-defined DSL structure modelling the molecule - * @tparam Ns2 Internal builder pattern type - * @tparam Ns3 Internal builder pattern type - * @tparam In1_2 Internal builder pattern type - * @tparam In1_3 Internal builder pattern type * @tparam A Type of output attribute 1 (`name`: String) * @tparam B Type of output attribute 2 (`age`: Int) * @return Molecule of arity-2 typed to two attributes (Molecule02[A, B]) diff --git a/core/src/main/scala/molecule/factory/Molecule_In_1_Factory.scala b/core/src/main/scala/molecule/factory/Molecule_In_1_Factory.scala index 0783b1d20..2c76ca5bf 100644 --- a/core/src/main/scala/molecule/factory/Molecule_In_1_Factory.scala +++ b/core/src/main/scala/molecule/factory/Molecule_In_1_Factory.scala @@ -56,10 +56,6 @@ trait Molecule_In_1_Factory2 { * * @group input1 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In1_1 Internal builder pattern type - * @tparam In1_2 Internal builder pattern type - * @tparam In2_1 Internal builder pattern type - * @tparam In2_2 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam A Type of output attribute 1 (`name`: String) * @return Input molecule ready to be resolved @@ -86,10 +82,6 @@ trait Molecule_In_1_Factory2 { * }}} * @group input1 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In1_2 Internal builder pattern type - * @tparam In1_3 Internal builder pattern type - * @tparam In2_2 Internal builder pattern type - * @tparam In2_3 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam A Type of output attribute 1 (`name`: String) * @tparam B Type of output attribute 2 (`score`: Int) diff --git a/core/src/main/scala/molecule/factory/Molecule_In_2_Factory.scala b/core/src/main/scala/molecule/factory/Molecule_In_2_Factory.scala index 763de6321..460a521da 100644 --- a/core/src/main/scala/molecule/factory/Molecule_In_2_Factory.scala +++ b/core/src/main/scala/molecule/factory/Molecule_In_2_Factory.scala @@ -56,10 +56,6 @@ trait Molecule_In_2_Factory2 { * * @group input2 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In2_1 Internal builder pattern type - * @tparam In2_2 Internal builder pattern type - * @tparam In3_1 Internal builder pattern type - * @tparam In3_2 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam I2 Type of input attribute 2 (`score`: Int) * @tparam A Type of output attribute 1 (`name`: String) @@ -89,10 +85,6 @@ trait Molecule_In_2_Factory2 { * }}} * @group input2 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In2_2 Internal builder pattern type - * @tparam In2_3 Internal builder pattern type - * @tparam In3_2 Internal builder pattern type - * @tparam In3_3 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam I2 Type of input attribute 2 (`score`: Int) * @tparam A Type of output attribute 1 (`name`: String) diff --git a/core/src/main/scala/molecule/factory/Molecule_In_3_Factory.scala b/core/src/main/scala/molecule/factory/Molecule_In_3_Factory.scala index 35fddbdf5..ce92a63c5 100644 --- a/core/src/main/scala/molecule/factory/Molecule_In_3_Factory.scala +++ b/core/src/main/scala/molecule/factory/Molecule_In_3_Factory.scala @@ -56,10 +56,6 @@ trait Molecule_In_3_Factory2 { * * @group input3 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In3_1 Internal builder pattern type - * @tparam In3_2 Internal builder pattern type - * @tparam In4_1 Internal builder pattern type - * @tparam In4_2 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam I2 Type of input attribute 2 (`score`: Int) * @tparam I3 Type of input attribute 3 (`flags`: Int) @@ -90,10 +86,6 @@ trait Molecule_In_3_Factory2 { * }}} * @group input3 * @param dsl User-defined DSL structure modelling the input molecule - * @tparam In3_2 Internal builder pattern type - * @tparam In3_3 Internal builder pattern type - * @tparam In4_2 Internal builder pattern type - * @tparam In4_3 Internal builder pattern type * @tparam I1 Type of input attribute 1 (`age`: Int) * @tparam I2 Type of input attribute 2 (`score`: Int) * @tparam I3 Type of input attribute 3 (`flags`: Int) diff --git a/core/src/main/scala/molecule/macros/Cast.scala b/core/src/main/scala/molecule/macros/Cast.scala index ed19b8d92..47898afd1 100644 --- a/core/src/main/scala/molecule/macros/Cast.scala +++ b/core/src/main/scala/molecule/macros/Cast.scala @@ -59,6 +59,20 @@ private[molecule] trait Cast extends TreeOps { case "java.net.URI" => (i: Int) => q"castAggrListVector[java.net.URI](row, $i)" case "java.util.UUID" => (i: Int) => q"castAggrListVector[java.util.UUID](row, $i)" } + def castAggrListVectorMany(tpe: String): Int => Tree = tpe match { + case "Int" => (i: Int) => q"castAggrListVectorManyInt(row, $i)" + case "Long" => (i: Int) => q"castAggrListVectorMany[Long](row, $i)" + case "Float" => (i: Int) => q"castAggrListVectorManyFloat(row, $i)" + case "Double" => (i: Int) => q"castAggrListVectorMany[Double](row, $i)" + case "String" => (i: Int) => q"castAggrListVectorMany[String](row, $i)" + case "BigInt" => (i: Int) => q"castAggrListVectorManyBigInt(row, $i)" + case "BigDecimal" => (i: Int) => q"castAggrListVectorManyBigDecimal(row, $i)" + case "java.util.Date" => (i: Int) => q"castAggrListVectorMany[java.util.Date](row, $i)" + case "Boolean" => (i: Int) => q"castAggrListVectorMany[Boolean](row, $i)" + case "java.net.URI" => (i: Int) => q"castAggrListVectorMany[java.net.URI](row, $i)" + case "java.util.UUID" => (i: Int) => q"castAggrListVectorMany[java.util.UUID](row, $i)" + } + def castAggrListHashSet(tpe: String): Int => Tree = tpe match { case "Int" => (i: Int) => q"castAggrListHashSetInt(row, $i)" case "Long" => (i: Int) => q"castAggrListHashSet[Long](row, $i)" @@ -72,6 +86,20 @@ private[molecule] trait Cast extends TreeOps { case "java.net.URI" => (i: Int) => q"castAggrListHashSet[java.net.URI](row, $i)" case "java.util.UUID" => (i: Int) => q"castAggrListHashSet[java.util.UUID](row, $i)" } + def castAggrListHashSetMany(tpe: String): Int => Tree = tpe match { + case "Int" => (i: Int) => q"castAggrListHashSetManyInt(row, $i)" + case "Long" => (i: Int) => q"castAggrListHashSetMany[Long](row, $i)" + case "Float" => (i: Int) => q"castAggrListHashSetManyFloat(row, $i)" + case "Double" => (i: Int) => q"castAggrListHashSetMany[Double](row, $i)" + case "String" => (i: Int) => q"castAggrListHashSetMany[String](row, $i)" + case "BigInt" => (i: Int) => q"castAggrListHashSetManyBigInt(row, $i)" + case "BigDecimal" => (i: Int) => q"castAggrListHashSetManyBigDecimal(row, $i)" + case "java.util.Date" => (i: Int) => q"castAggrListHashSetMany[java.util.Date](row, $i)" + case "Boolean" => (i: Int) => q"castAggrListHashSetMany[Boolean](row, $i)" + case "java.net.URI" => (i: Int) => q"castAggrListHashSetMany[java.net.URI](row, $i)" + case "java.util.UUID" => (i: Int) => q"castAggrListHashSetMany[java.util.UUID](row, $i)" + } + def castAggrListLazySeq(tpe: String): Int => Tree = tpe match { case "Int" => (i: Int) => q"castAggrListLazySeqInt(row, $i)" case "Long" => (i: Int) => q"castAggrListLazySeq[Long](row, $i)" @@ -85,6 +113,19 @@ private[molecule] trait Cast extends TreeOps { case "java.net.URI" => (i: Int) => q"castAggrListLazySeq[java.net.URI](row, $i)" case "java.util.UUID" => (i: Int) => q"castAggrListLazySeq[java.util.UUID](row, $i)" } + def castAggrListLazySeqMany(tpe: String): Int => Tree = tpe match { + case "Int" => (i: Int) => q"castAggrListLazySeqManyInt(row, $i)" + case "Long" => (i: Int) => q"castAggrListLazySeqMany[Long](row, $i)" + case "Float" => (i: Int) => q"castAggrListLazySeqManyFloat(row, $i)" + case "Double" => (i: Int) => q"castAggrListLazySeqMany[Double](row, $i)" + case "String" => (i: Int) => q"castAggrListLazySeqMany[String](row, $i)" + case "BigInt" => (i: Int) => q"castAggrListLazySeqManyBigInt(row, $i)" + case "BigDecimal" => (i: Int) => q"castAggrListLazySeqManyBigDecimal(row, $i)" + case "java.util.Date" => (i: Int) => q"castAggrListLazySeqMany[java.util.Date](row, $i)" + case "Boolean" => (i: Int) => q"castAggrListLazySeqMany[Boolean](row, $i)" + case "java.net.URI" => (i: Int) => q"castAggrListLazySeqMany[java.net.URI](row, $i)" + case "java.util.UUID" => (i: Int) => q"castAggrListLazySeqMany[java.util.UUID](row, $i)" + } def castAggrInt: Int => Tree = (i: Int) => q"row.get($i).asInstanceOf[Int]" def castAggrDouble: Int => Tree = (i: Int) => q"row.get($i).asInstanceOf[Double]" @@ -102,6 +143,19 @@ private[molecule] trait Cast extends TreeOps { case "java.net.URI" => (i: Int) => q"castOne[java.net.URI](row, $i)" case "java.util.UUID" => (i: Int) => q"castOne[java.util.UUID](row, $i)" } + def castAggrMany(tpe: String): Int => Tree = tpe match { + case "Int" => (i: Int) => q"castAggrManyInt(row, $i)" + case "Long" => (i: Int) => q"castAggrMany[Long](row, $i)" + case "Float" => (i: Int) => q"castAggrManyFloat(row, $i)" + case "Double" => (i: Int) => q"castAggrMany[Double](row, $i)" + case "String" => (i: Int) => q"castAggrMany[String](row, $i)" + case "BigInt" => (i: Int) => q"castAggrManyBigInt(row, $i)" + case "BigDecimal" => (i: Int) => q"castAggrManyBigDecimal(row, $i)" + case "java.util.Date" => (i: Int) => q"castAggrMany[java.util.Date](row, $i)" + case "Boolean" => (i: Int) => q"castAggrMany[Boolean](row, $i)" + case "java.net.URI" => (i: Int) => q"castAggrMany[java.net.URI](row, $i)" + case "java.util.UUID" => (i: Int) => q"castAggrMany[java.util.UUID](row, $i)" + } def castAggrVector(tpe: String): Int => Tree = tpe match { case "Int" => (i: Int) => q"castAggrVectorInt(row, $i)" @@ -116,19 +170,7 @@ private[molecule] trait Cast extends TreeOps { case "java.net.URI" => (i: Int) => q"castAggrVector[java.net.URI](row, $i)" case "java.util.UUID" => (i: Int) => q"castAggrVector[java.util.UUID](row, $i)" } - def castAggrLazySeq(tpe: String): Int => Tree = tpe match { - case "Int" => (i: Int) => q"castAggrLazySeqInt(row, $i)" - case "Long" => (i: Int) => q"castAggrLazySeq[Long](row, $i)" - case "Float" => (i: Int) => q"castAggrLazySeqFloat(row, $i)" - case "Double" => (i: Int) => q"castAggrLazySeq[Double](row, $i)" - case "String" => (i: Int) => q"castAggrLazySeq[String](row, $i)" - case "BigInt" => (i: Int) => q"castAggrLazySeqBigInt(row, $i)" - case "BigDecimal" => (i: Int) => q"castAggrLazySeqBigDecimal(row, $i)" - case "java.util.Date" => (i: Int) => q"castAggrLazySeq[java.util.Date](row, $i)" - case "Boolean" => (i: Int) => q"castAggrLazySeq[Boolean](row, $i)" - case "java.net.URI" => (i: Int) => q"castAggrLazySeq[java.net.URI](row, $i)" - case "java.util.UUID" => (i: Int) => q"castAggrLazySeq[java.util.UUID](row, $i)" - } + val castOptionalAttr: richTree => Int => Tree = (t: richTree) => if (t.card == 1) { diff --git a/core/src/main/scala/molecule/transform/CastHelpers.scala b/core/src/main/scala/molecule/transform/CastHelpers.scala index f30cd0471..836454ebb 100644 --- a/core/src/main/scala/molecule/transform/CastHelpers.scala +++ b/core/src/main/scala/molecule/transform/CastHelpers.scala @@ -133,96 +133,239 @@ trait CastHelpers[Tpl] extends Helpers { list } + // card many + + protected def castAggrListVectorManyInt(row: jList[_], i: Int): List[Set[Int]] = { + val it = row.get(i).asInstanceOf[PersistentVector].iterator + var set = Set.empty[Int] + while (it.hasNext) + set = set + it.next.asInstanceOf[jLong].toInt + List(set) + } + + protected def castAggrListVectorManyFloat(row: jList[_], i: Int): List[Set[Float]] = { + val it = row.get(i).asInstanceOf[PersistentVector].iterator + var set = Set.empty[Float] + while (it.hasNext) + set = set + it.next.asInstanceOf[jDouble].toFloat + List(set) + } + + protected def castAggrListVectorManyBigInt(row: jList[_], i: Int): List[Set[BigInt]] = { + val it = row.get(i).asInstanceOf[PersistentVector].iterator + var set = Set.empty[BigInt] + while (it.hasNext) + set = set + BigInt(it.next.toString) + List(set) + } + + protected def castAggrListVectorManyBigDecimal(row: jList[_], i: Int): List[Set[BigDecimal]] = { + val it = row.get(i).asInstanceOf[PersistentVector].iterator + var set = Set.empty[BigDecimal] + while (it.hasNext) + set = set + BigDecimal(it.next.asInstanceOf[java.math.BigDecimal].toString) + List(set) + } + + protected def castAggrListVectorMany[T](row: jList[_], i: Int): List[Set[T]] = { + val it = row.get(i).asInstanceOf[PersistentVector].iterator + var set = Set.empty[T] + while (it.hasNext) + set = set + it.next.asInstanceOf[T] + List(set) + } + // ------------------------------------ - protected def castAggrListLazySeqInt(row: jList[_], i: Int): List[Int] = { - val it = row.get(i).asInstanceOf[LazySeq].iterator + protected def castAggrListHashSetInt(row: jList[_], i: Int): List[Int] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator var list = List.empty[Int] while (it.hasNext) list = list :+ it.next.asInstanceOf[jLong].toInt list } - protected def castAggrListLazySeqFloat(row: jList[_], i: Int): List[Float] = { - val it = row.get(i).asInstanceOf[LazySeq].iterator + protected def castAggrListHashSetFloat(row: jList[_], i: Int): List[Float] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator var list = List.empty[Float] while (it.hasNext) list = list :+ it.next.asInstanceOf[jDouble].toFloat list } - protected def castAggrListLazySeqBigInt(row: jList[_], i: Int): List[BigInt] = { - val it = row.get(i).asInstanceOf[LazySeq].iterator + protected def castAggrListHashSetBigInt(row: jList[_], i: Int): List[BigInt] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator var list = List.empty[BigInt] while (it.hasNext) list = list :+ BigInt(it.next.toString) list } - protected def castAggrListLazySeqBigDecimal(row: jList[_], i: Int): List[BigDecimal] = { - val it = row.get(i).asInstanceOf[LazySeq].iterator + protected def castAggrListHashSetBigDecimal(row: jList[_], i: Int): List[BigDecimal] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator var list = List.empty[BigDecimal] while (it.hasNext) list = list :+ BigDecimal(it.next.asInstanceOf[java.math.BigDecimal].toString) list } - protected def castAggrListLazySeq[T](row: jList[_], i: Int): List[T] = { - val it = row.get(i).asInstanceOf[LazySeq].iterator + protected def castAggrListHashSet[T](row: jList[_], i: Int): List[T] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator var list = List.empty[T] while (it.hasNext) list = list :+ it.next.asInstanceOf[T] list } - // ------------------------------------ + // card many - protected def castAggrListHashSetInt(row: jList[_], i: Int): List[Int] = { + protected def castAggrListHashSetManyInt(row: jList[_], i: Int): List[Set[Int]] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + var set = Set.empty[Int] + while (it.hasNext) + set = set + it.next.asInstanceOf[jLong].toInt + List(set) + } + + protected def castAggrListHashSetManyFloat(row: jList[_], i: Int): List[Set[Float]] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + var set = Set.empty[Float] + while (it.hasNext) + set = set + it.next.asInstanceOf[jDouble].toFloat + List(set) + } + + protected def castAggrListHashSetManyBigInt(row: jList[_], i: Int): List[Set[BigInt]] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + var set = Set.empty[BigInt] + while (it.hasNext) + set = set + BigInt(it.next.toString) + List(set) + } + + protected def castAggrListHashSetManyBigDecimal(row: jList[_], i: Int): List[Set[BigDecimal]] = { + val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + var set = Set.empty[BigDecimal] + while (it.hasNext) + set = set + BigDecimal(it.next.asInstanceOf[java.math.BigDecimal].toString) + List(set) + } + + protected def castAggrListHashSetMany[T](row: jList[_], i: Int): List[Set[T]] = { val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + var set = Set.empty[T] + while (it.hasNext) + set = set + it.next.asInstanceOf[T] + List(set) + } + + // ------------------------------------ + + protected def castAggrListLazySeqInt(row: jList[_], i: Int): List[Int] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator var list = List.empty[Int] while (it.hasNext) list = list :+ it.next.asInstanceOf[jLong].toInt list } - protected def castAggrListHashSetFloat(row: jList[_], i: Int): List[Float] = { - val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + protected def castAggrListLazySeqFloat(row: jList[_], i: Int): List[Float] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator var list = List.empty[Float] while (it.hasNext) list = list :+ it.next.asInstanceOf[jDouble].toFloat list } - protected def castAggrListHashSetBigInt(row: jList[_], i: Int): List[BigInt] = { - val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + protected def castAggrListLazySeqBigInt(row: jList[_], i: Int): List[BigInt] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator var list = List.empty[BigInt] while (it.hasNext) list = list :+ BigInt(it.next.toString) list } - protected def castAggrListHashSetBigDecimal(row: jList[_], i: Int): List[BigDecimal] = { - val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + protected def castAggrListLazySeqBigDecimal(row: jList[_], i: Int): List[BigDecimal] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator var list = List.empty[BigDecimal] while (it.hasNext) list = list :+ BigDecimal(it.next.asInstanceOf[java.math.BigDecimal].toString) list } - protected def castAggrListHashSet[T](row: jList[_], i: Int): List[T] = { - val it = row.get(i).asInstanceOf[PersistentHashSet].iterator + protected def castAggrListLazySeq[T](row: jList[_], i: Int): List[T] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator var list = List.empty[T] while (it.hasNext) list = list :+ it.next.asInstanceOf[T] list } + // card many + + protected def castAggrListLazySeqManyInt(row: jList[_], i: Int): List[Set[Int]] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator + var set = Set.empty[Int] + while (it.hasNext) + set = set + it.next.asInstanceOf[jLong].toInt + List(set) + } + + protected def castAggrListLazySeqManyFloat(row: jList[_], i: Int): List[Set[Float]] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator + var set = Set.empty[Float] + while (it.hasNext) + set = set + it.next.asInstanceOf[jDouble].toFloat + List(set) + } + + protected def castAggrListLazySeqManyBigInt(row: jList[_], i: Int): List[Set[BigInt]] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator + var set = Set.empty[BigInt] + while (it.hasNext) + set = set + BigInt(it.next.toString) + List(set) + } + + protected def castAggrListLazySeqManyBigDecimal(row: jList[_], i: Int): List[Set[BigDecimal]] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator + var set = Set.empty[BigDecimal] + while (it.hasNext) + set = set + BigDecimal(it.next.asInstanceOf[java.math.BigDecimal].toString) + List(set) + } + + protected def castAggrListLazySeqMany[T](row: jList[_], i: Int): List[Set[T]] = { + val it = row.get(i).asInstanceOf[LazySeq].iterator + var set = Set.empty[T] + while (it.hasNext) + set = set + it.next.asInstanceOf[T] + List(set) + } + + // ------------------------------------ + + protected def castAggrManyInt(row: jList[_], i: Int): Set[Int] = + Set(row.get(i).asInstanceOf[jLong].toInt) + + protected def castAggrManyFloat(row: jList[_], i: Int): Set[Float] = + Set(row.get(i).asInstanceOf[jDouble].toFloat) + + protected def castAggrManyBigInt(row: jList[_], i: Int): Set[BigInt] = + Set(BigInt(row.get(i).toString)) + + protected def castAggrManyBigDecimal(row: jList[_], i: Int): Set[BigDecimal] = + Set(BigDecimal(row.get(i).asInstanceOf[java.math.BigDecimal].toString)) + + protected def castAggrMany[T](row: jList[_], i: Int): Set[T] = + Set(row.get(i).asInstanceOf[T]) + // ------------------------------------ protected def castAggrVectorInt(row: jList[_], i: Int): Int = row.get(i).asInstanceOf[PersistentVector].iterator.next.asInstanceOf[jLong].toInt - protected def castAggrVectorDouble(row: jList[_], i: Int): Float = + protected def castAggrVectorFloat(row: jList[_], i: Int): Float = row.get(i).asInstanceOf[PersistentVector].iterator.next.asInstanceOf[jDouble].toFloat protected def castAggrVectorBigInt(row: jList[_], i: Int): BigInt = @@ -235,22 +378,6 @@ trait CastHelpers[Tpl] extends Helpers { row.get(i).asInstanceOf[PersistentVector].iterator.next.asInstanceOf[T] - protected def castAggrLazySeqInt(row: jList[_], i: Int): Int = - row.get(i).asInstanceOf[LazySeq].iterator.next.asInstanceOf[jLong].toInt - - protected def castAggrLazySeqDouble(row: jList[_], i: Int): Float = - row.get(i).asInstanceOf[LazySeq].iterator.next.asInstanceOf[jDouble].toFloat - - protected def castAggrLazySeqBigInt(row: jList[_], i: Int): BigInt = - BigInt(row.get(i).asInstanceOf[LazySeq].iterator.next.toString) - - protected def castAggrLazySeqBigDecimal(row: jList[_], i: Int): BigDecimal = - BigDecimal(row.get(i).asInstanceOf[LazySeq].iterator.next.asInstanceOf[java.math.BigDecimal].toString) - - protected def castAggrLazySeq[T](row: jList[_], i: Int): T = - row.get(i).asInstanceOf[LazySeq].iterator.next.asInstanceOf[T] - - // Optional card one =========================================================================================== protected def castOptOneInt(row: jList[_], i: Int): Option[Int] = if (row.get(i) == null) { diff --git a/core/src/main/scala/molecule/transform/Dsl2Model.scala b/core/src/main/scala/molecule/transform/Dsl2Model.scala index 1b7502d3c..c6d40ae3d 100644 --- a/core/src/main/scala/molecule/transform/Dsl2Model.scala +++ b/core/src/main/scala/molecule/transform/Dsl2Model.scala @@ -904,8 +904,6 @@ private[molecule] trait Dsl2Model extends Cast with Json { Seq.empty[GenericValue] } - def bi2(tree: Tree, t: richTree): Value = bi(tree, t).headOption.getOrElse(NoValue) - def addAttrOrAggr(attr: String, t: richTree, tpeStr: String = "", apply: Boolean = false): Unit = { if (standard) { // x(81, attr) @@ -929,27 +927,39 @@ private[molecule] trait Dsl2Model extends Cast with Json { addSpecific(castAggrDouble, s"Double") addJson(jsonOneAttr, "Double", t.ns + "." + t.nameClean) + case "list" if t.card == 2 => + addSpecific(castAggrListVectorMany(tpeStr), s"List[$tpeStr]") + addJson(jsonAggrListVector, tpeStr, t.ns + "." + t.nameClean) + case "list" => addSpecific(castAggrListVector(tpeStr), s"List[$tpeStr]") addJson(jsonAggrListVector, tpeStr, t.ns + "." + t.nameClean) + case "listSet" if t.card == 2 => + addSpecific(castAggrListHashSetMany(tpeStr), s"List[$tpeStr]") // Ns.str.int(distinct).get + addJson(jsonAggrListVector, tpeStr, t.ns + "." + t.nameClean) + case "listSet" => addSpecific(castAggrListHashSet(tpeStr), s"List[$tpeStr]") // Ns.str.int(distinct).get addJson(jsonAggrListVector, tpeStr, t.ns + "." + t.nameClean) + case "listRand" if t.card == 2 => + addSpecific(castAggrListLazySeqMany(tpeStr), s"List[$tpeStr]") + addJson(jsonAggrListLazySeq, tpeStr, t.ns + "." + t.nameClean) + case "listRand" => addSpecific(castAggrListLazySeq(tpeStr), s"List[$tpeStr]") addJson(jsonAggrListLazySeq, tpeStr, t.ns + "." + t.nameClean) - case "lazySeqHead" => - addSpecific(castAggrLazySeq(tpeStr), tpeStr) - addJson(jsonAggrLazySeq, tpeStr, t.ns + "." + t.nameClean) - case "vectorHead" => addSpecific(castAggrVector(tpeStr), tpeStr) addJson(jsonAggrVector, tpeStr, t.ns + "." + t.nameClean) - case _ => + case "aggr" if t.card == 2 => + addSpecific(castAggrMany(tpeStr), tpeStr) + addJson(jsonAggr, tpeStr, t.ns + "." + t.nameClean) + + case "aggr" => addSpecific(castAggr(tpeStr), tpeStr) addJson(jsonAggr, tpeStr, t.ns + "." + t.nameClean) } @@ -1045,12 +1055,12 @@ private[molecule] trait Dsl2Model extends Cast with Json { case "None" => Fn("not") case "unify" if t.name.last == '_' => Fn("unify") case "unify" => abort(s"Can only unify on tacit attributes. Please add underscore to attribute: `${t.name}_(unify)`") - case "min" => standard = false; aggrType = "vectorHead"; aggr("min", Some(1)) - case "max" => standard = false; aggrType = "vectorHead"; aggr("max", Some(1)) - case "rand" => standard = false; aggrType = "lazySeqHead"; aggr("rand", Some(1)) + case "min" => standard = false; aggrType = "aggr"; aggr("min") + case "max" => standard = false; aggrType = "aggr"; aggr("max") + case "rand" => standard = false; aggrType = "aggr"; aggr("rand") case "sample" => standard = false; aggrType = "vectorHead"; aggr("sample", Some(1)) - case "sum" => standard = false; aggr("sum") - case "median" => standard = false; aggr("median") + case "sum" => standard = false; aggrType = "aggr"; aggr("sum") + case "median" => standard = false; aggrType = "aggr"; aggr("median") case "distinct" => standard = false; aggrType = "listSet"; Distinct case "count" => standard = false; aggrType = "Int"; aggr("count") case "countDistinct" => standard = false; aggrType = "Int"; aggr("count-distinct") @@ -1316,6 +1326,10 @@ private[molecule] trait Dsl2Model extends Cast with Json { case Atom(ns, attr, _, 2, Distinct, _, _, _) => abort(s"`Distinct` keyword not supported for card many attributes like `:$ns/$attr` (card many values already returned as Sets of distinct values).") + case Atom(ns, attr, _, 3 | 4, Fn(fn, _), _, _, _) => + if (!Seq("not", "unify").contains(fn)) + abort("Map attributes not allowed to use `unify` or aggregate expressions.") + case Atom(ns, attr, _, _, _, _, _, _) => if (beforeFirstAttr) { if (generics.nonEmpty) diff --git a/core/src/main/scala/molecule/transform/Model2Query.scala b/core/src/main/scala/molecule/transform/Model2Query.scala index 2a13ecd6e..c3fe40944 100644 --- a/core/src/main/scala/molecule/transform/Model2Query.scala +++ b/core/src/main/scala/molecule/transform/Model2Query.scala @@ -15,7 +15,7 @@ import molecule.util.{Debug, Helpers} * Custom DSL molecule --> Model --> Query --> Datomic query string * * @see [[http://www.scalamolecule.org/dev/transformation/]] - **/ + * */ object Model2Query extends Helpers { val x = Debug("Model2Query", 1, 19) @@ -717,6 +717,9 @@ object Model2Query extends Helpers { case Ge(arg) => q.findD(v2).enum(e, a, v).compareTo(">=", a, v2, Val(arg), 1) case Lt(arg) => q.findD(v2).enum(e, a, v).compareTo("<", a, v2, Val(arg), 1) case Le(arg) => q.findD(v2).enum(e, a, v).compareTo("<=", a, v2, Val(arg), 1) + case Fn(fn, Some(i)) => q.find(fn, Seq(i), v2).enum(e, a, v) + case Fn(fn, _) if coalesce(fn) => q.find(fn, Nil, v2).enum(e, a, v).widh(e) + case Fn(fn, _) => q.find(fn, Nil, v2).enum(e, a, v) case other => abort(s"Unresolved cardinality-many enum Atom:\nAtom : $a\nElement: $other") } } @@ -741,6 +744,9 @@ object Model2Query extends Helpers { case Ge(arg) => q.find(v2).enum(e, a, v).compareTo(">=", a, v2, Val(arg), 1) case Lt(arg) => q.find(v2).enum(e, a, v).compareTo("<", a, v2, Val(arg), 1) case Le(arg) => q.find(v2).enum(e, a, v).compareTo("<=", a, v2, Val(arg), 1) + case Fn(fn, Some(i)) => q.find(fn, Seq(i), v2).enum(e, a, v) + case Fn(fn, _) if coalesce(fn) => q.find(fn, Nil, v2).enum(e, a, v).widh(e) + case Fn(fn, _) => q.find(fn, Nil, v2).enum(e, a, v) case other => abort(s"Unresolved cardinality-one enum Atom:\nAtom : $a\nElement: $other") } } @@ -837,6 +843,8 @@ object Model2Query extends Helpers { case Lt(arg) => q.findD(v).where(e, a, v).compareTo("<", a, v, Val(arg)) case Le(arg) => q.findD(v).where(e, a, v).compareTo("<=", a, v, Val(arg)) case And(args) => q.findD(v).whereAnd(e, a, v, args, u(t, v)) + case Fn(fn, Some(i)) => q.find(fn, Seq(i), v).where(e, a, v) + case Fn(fn, _) if coalesce(fn) => q.find(fn, Nil, v).where(e, a, v).widh(e) case Fn(fn, _) => q.find(fn, Nil, v).where(e, a, v) case Fulltext(args) => q.findD(v).where(e, a, v).orRules(e, a, args, "", true) case other => abort(s"Unresolved cardinality-many Atom:\nAtom : $a\nElement: $other") diff --git a/coretests/src/test/scala/molecule/coretests/expression/Aggregates.scala b/coretests/src/test/scala/molecule/coretests/expression/Aggregates.scala index a49f3a166..df3d318d2 100644 --- a/coretests/src/test/scala/molecule/coretests/expression/Aggregates.scala +++ b/coretests/src/test/scala/molecule/coretests/expression/Aggregates.scala @@ -3,14 +3,14 @@ package molecule.coretests.expression import molecule.api.out3._ import molecule.coretests.util.dsl.coreTest._ import molecule.coretests.util.CoreSpec +import molecule.util.expectCompileError class Aggregates extends CoreSpec { - class ManySetup extends CoreSetup { + class AggregateSetup extends CoreSetup { - val (a, b, c, d) = ("a", "b", "c", "d") - - // We pair cardinality many attribute values with card-one's too to be able to group by cardinality one values + // We pair cardinality many attribute values with card-one's + // too to be able to group by cardinality one values Ns.str.strs insert List( ("str1", Set("a", "b")), ("str2", Set("b", "c")), @@ -29,14 +29,14 @@ class Aggregates extends CoreSpec { (3L, Set(2L, 4L))) Ns.float.floats insert List( - (1.0f, Set(1.0f, 2.0f)), - (2.0f, Set(2.0f, 3.0f)), - (3.0f, Set(2.5f, 4.0f))) + (1.1f, Set(1.1f, 2.2f)), + (2.2f, Set(2.2f, 3.3f)), + (3.3f, Set(3.3f, 4.4f))) Ns.double.doubles insert List( - (1.0, Set(1.0, 2.0)), - (2.0, Set(2.0, 3.0)), - (3.0, Set(2.5, 4.0))) + (1.1, Set(1.1, 2.2)), + (2.2, Set(2.2, 3.3)), + (3.3, Set(3.3, 4.4))) // Set of boolean values maybe not so useful Ns.bool.bools insert List( @@ -59,13 +59,338 @@ class Aggregates extends CoreSpec { (uri3, Set(uri2, uri4))) Ns.enum.enums insert List( - ("enum1", Set("enum1", "enum2")), - ("enum2", Set("enum2", "enum3")), - ("enum3", Set("enum2", "enum4"))) + (enum1, Set(enum1, enum2)), + (enum2, Set(enum2, enum3)), + (enum3, Set(enum2, enum4))) + + Ns.bigInt.bigInts insert List( + (bigInt1, Set(bigInt1, bigInt2)), + (bigInt2, Set(bigInt2, bigInt3)), + (bigInt3, Set(bigInt2, bigInt4))) + + Ns.bigDec.bigDecs insert List( + (bigDec1, Set(bigDec1, bigDec2)), + (bigDec2, Set(bigDec2, bigDec3)), + (bigDec3, Set(bigDec2, bigDec4))) + } + + + "sum" in new AggregateSetup { + Ns.int(sum).get.head === (1 + 2 + 3) + Ns.long(sum).get.head === 6L + Ns.float(sum).get.head === 6.6f + Ns.double(sum).get.head === 6.6000000000000005 // Datomic fault? + } + + + "median" in new AggregateSetup { + Ns.int(median).get.head === 2 + Ns.long(median).get.head === 2L + Ns.float(median).get.head === 2.2f + Ns.double(median).get.head === 2.2 + } + + + "avg" in new AggregateSetup { + Ns.int(avg).get.head === (1 + 2 + 3) / 3 + Ns.long(avg).get.head === 2 + Ns.float(avg).get.head === 2.200000007947286 // Double value is returned. Use double attr for precision + Ns.double(avg).get.head === 2.2 + } + + + "variance" in new AggregateSetup { + Ns.int(variance).get.head === 0.6666666666666666 + Ns.long(variance).get.head === 0.6666666666666666 + Ns.float(variance).get.head === 0.806666614214581 // Double value is returned. Use double attr for precision + Ns.double(variance).get.head === 0.8066666666666665 } - "Distinct" in new CoreSetup { + "stddev" in new AggregateSetup { + Ns.int(stddev).get.head === 0.816496580927726 + Ns.long(stddev).get.head === 0.816496580927726 + Ns.float(stddev).get.head === 0.8981462098203059 // Double value is returned. Use double attr for precision + Ns.double(stddev).get.head === 0.8981462390204986 + } + + + "min" in new AggregateSetup { + + // card one + + Ns.str(min).get.head === "str1" + Ns.int(min).get.head === 1 + Ns.long(min).get.head === 1L + Ns.float(min).get.head === 1.1f + Ns.double(min).get.head === 1.1 + Ns.bool(min).get.head === false + Ns.date(min).get.head === date1 + Ns.uuid(min).get.head === uuid1 + Ns.uri(min).get.head === uri1 + Ns.enum(min).get.head === enum1 + Ns.bigInt(min).get.head === bigInt1 + Ns.bigDec(min).get.head === bigDec1 + + Ns.str(min(2)).get.head === List("str1", "str2") + Ns.int(min(2)).get.head === List(1, 2) + Ns.long(min(2)).get.head === List(1L, 2L) + Ns.float(min(2)).get.head === List(1.1f, 2.2f) + Ns.double(min(2)).get.head === List(1.1, 2.2) + Ns.bool(min(2)).get.head === List(false, true) + Ns.date(min(2)).get.head === List(date1, date2) + Ns.uuid(min(2)).get.head === List(uuid1, uuid2) + Ns.uri(min(2)).get.head === List(uri1, uri2) + Ns.enum(min(2)).get.head === List(enum1, enum2) + Ns.bigInt(min(2)).get.head === List(bigInt1, bigInt2) + Ns.bigDec(min(2)).get.head === List(bigDec1, bigDec2) + + + // card many + + Ns.strs(min).get.head === Set("a") + Ns.ints(min).get.head === Set(1) + Ns.longs(min).get.head === Set(1L) + Ns.floats(min).get.head === Set(1.1f) + Ns.doubles(min).get.head === Set(1.1) + Ns.bools(min).get.head === Set(false) + Ns.dates(min).get.head === Set(date1) + Ns.uuids(min).get.head === Set(uuid1) + Ns.uris(min).get.head === Set(uri1) + Ns.enums(min).get.head === Set(enum1) + Ns.bigInts(min).get.head === Set(bigInt1) + Ns.bigDecs(min).get.head === Set(bigDec1) + + Ns.strs(min(2)).get.head === List(Set("a", "b")) + Ns.ints(min(2)).get.head === List(Set(1, 2)) + Ns.longs(min(2)).get.head === List(Set(1L, 2L)) + Ns.floats(min(2)).get.head === List(Set(1.1f, 2.2f)) + Ns.doubles(min(2)).get.head === List(Set(1.1, 2.2)) + Ns.bools(min(2)).get.head === List(Set(false, true)) + Ns.dates(min(2)).get.head === List(Set(date1, date2)) + Ns.uuids(min(2)).get.head === List(Set(uuid1, uuid2)) + Ns.uris(min(2)).get.head === List(Set(uri1, uri2)) + Ns.enums(min(2)).get.head === List(Set(enum1, enum2)) + Ns.bigInts(min(2)).get.head === List(Set(bigInt1, bigInt2)) + Ns.bigDecs(min(2)).get.head === List(Set(bigDec1, bigDec2)) + + + // combinations + + Ns.int(min).ints.get === List( + (1, Set(1, 4, 3, 2)) + ) + + Ns.int(min(2)).ints.get === List( + (List(1, 1), Set(1, 4, 3, 2)) + ) + + + Ns.int.ints(min).get === List( + (1, Set(1)), + (2, Set(2)), + (3, Set(2)) + ) + + Ns.int.ints(min(2)).get === List( + (1, List(Set(1, 2))), + (2, List(Set(2, 3))), + (3, List(Set(2, 4))) + ) + + + Ns.int(min).ints(min).get === List( + (1, Set(1)) + ) + + Ns.int(min(2)).ints(min).get === List( + (List(1, 1), Set(1)) + ) + + Ns.int(min).ints(min(2)).get === List( + (1, List(Set(1, 2))) + ) + + Ns.int(min(2)).ints(min(2)).get === List( + (List(1, 1), List(Set(1, 2))) + ) + } + + + "max" in new AggregateSetup { + + // card one + + Ns.str(max).get.head === "str4" + Ns.int(max).get.head === 3 + Ns.long(max).get.head === 3L + Ns.float(max).get.head === 3.3f + Ns.double(max).get.head === 3.3 + Ns.bool(max).get.head === true + Ns.date(max).get.head === date3 + Ns.uuid(max).get.head === uuid3 + Ns.uri(max).get.head === uri3 + Ns.enum(max).get.head === enum3 + Ns.bigInt(max).get.head === bigInt3 + Ns.bigDec(max).get.head === bigDec3 + + Ns.str(max(2)).get.head === List("str4", "str3") + Ns.int(max(2)).get.head === List(3, 2) + Ns.long(max(2)).get.head === List(3L, 2L) + Ns.float(max(2)).get.head === List(3.3f, 2.2f) + Ns.double(max(2)).get.head === List(3.3, 2.2) + Ns.bool(max(2)).get.head === List(true, false) + Ns.date(max(2)).get.head === List(date3, date2) + Ns.uuid(max(2)).get.head === List(uuid3, uuid2) + Ns.uri(max(2)).get.head === List(uri3, uri2) + Ns.enum(max(2)).get.head === List(enum3, enum2) + Ns.bigInt(max(2)).get.head === List(bigInt3, bigInt2) + Ns.bigDec(max(2)).get.head === List(bigDec3, bigDec2) + + + // card many + + Ns.strs(max).get.head === Set("d") + Ns.ints(max).get.head === Set(4) + Ns.longs(max).get.head === Set(4L) + Ns.floats(max).get.head === Set(4.4f) + Ns.doubles(max).get.head === Set(4.4) + Ns.bools(max).get.head === Set(true) + Ns.dates(max).get.head === Set(date4) + Ns.uuids(max).get.head === Set(uuid4) + Ns.uris(max).get.head === Set(uri4) + Ns.enums(max).get.head === Set(enum4) + Ns.bigInts(max).get.head === Set(bigInt4) + Ns.bigDecs(max).get.head === Set(bigDec4) + + Ns.strs(max(2)).get.head === List(Set("d", "c")) + Ns.ints(max(2)).get.head === List(Set(4, 3)) + Ns.longs(max(2)).get.head === List(Set(4L, 3L)) + Ns.floats(max(2)).get.head === List(Set(4.4f, 3.3f)) + Ns.doubles(max(2)).get.head === List(Set(4.4, 3.3)) + Ns.bools(max(2)).get.head === List(Set(true, false)) + Ns.dates(max(2)).get.head === List(Set(date4, date3)) + Ns.uuids(max(2)).get.head === List(Set(uuid4, uuid3)) + Ns.uris(max(2)).get.head === List(Set(uri4, uri3)) + Ns.enums(max(2)).get.head === List(Set(enum4, enum3)) + Ns.bigInts(max(2)).get.head === List(Set(bigInt4, bigInt3)) + Ns.bigDecs(max(2)).get.head === List(Set(bigDec4, bigDec3)) + + + // combinations + + Ns.int(max).ints.get === List( + (3, Set(1, 4, 3, 2)) + ) + + Ns.int(max(2)).ints.get === List( + (List(3, 3), Set(1, 4, 3, 2)) + ) + + + Ns.int.ints(max).get === List( + (1, Set(2)), + (2, Set(3)), + (3, Set(4)) + ) + + Ns.int.ints(max(2)).get === List( + (1, List(Set(1, 2))), + (2, List(Set(2, 3))), + (3, List(Set(2, 4))) + ) + + + Ns.int(max).ints(max).get === List( + (3, Set(4)) + ) + + Ns.int(max(2)).ints(max).get === List( + (List(3, 3), Set(4)) + ) + + Ns.int(max).ints(max(2)).get === List( + (3, List(Set(4, 3))) + ) + + Ns.int(max(2)).ints(max(2)).get === List( + (List(3, 3), List(Set(4, 3))) + ) + } + + val strs = Seq("str1", "str2", "str3", "str4") + val ints = Seq(1, 2, 3) + val longs = Seq(1L, 2L, 3L) + val floats = Seq(1.1f, 2.2f, 3.3f) + val doubles = Seq(1.1, 2.2, 3.3) + val bools = Seq(false, true) + val dates = Seq(date1, date2, date3) + val uuids = Seq(uuid1, uuid2, uuid3) + val uris = Seq(uri1, uri2, uri3) + val enums = Seq(enum1, enum2, enum3) + val bigInts = Seq(bigInt1, bigInt2, bigInt3) + val bigDecs = Seq(bigDec1, bigDec2, bigDec3) + + + "rand" in new AggregateSetup { + strs.contains(Ns.str(rand).get.head) === true + ints.contains(Ns.int(rand).get.head) === true + longs.contains(Ns.long(rand).get.head) === true + floats.contains(Ns.float(rand).get.head) === true + doubles.contains(Ns.double(rand).get.head) === true + bools.contains(Ns.bool(rand).get.head) === true + dates.contains(Ns.date(rand).get.head) === true + uuids.contains(Ns.uuid(rand).get.head) === true + uris.contains(Ns.uri(rand).get.head) === true + enums.contains(Ns.enum(rand).get.head) === true + bigInts.contains(Ns.bigInt(rand).get.head) === true + bigDecs.contains(Ns.bigDec(rand).get.head) === true + + strs.intersect(Ns.str(rand(2)).get.head).nonEmpty === true + ints.intersect(Ns.int(rand(2)).get.head).nonEmpty === true + longs.intersect(Ns.long(rand(2)).get.head).nonEmpty === true + floats.intersect(Ns.float(rand(2)).get.head).nonEmpty === true + doubles.intersect(Ns.double(rand(2)).get.head).nonEmpty === true + bools.intersect(Ns.bool(rand(2)).get.head).nonEmpty === true + dates.intersect(Ns.date(rand(2)).get.head).nonEmpty === true + uuids.intersect(Ns.uuid(rand(2)).get.head).nonEmpty === true + uris.intersect(Ns.uri(rand(2)).get.head).nonEmpty === true + enums.intersect(Ns.enum(rand(2)).get.head).nonEmpty === true + bigInts.intersect(Ns.bigInt(rand(2)).get.head).nonEmpty === true + bigDecs.intersect(Ns.bigDec(rand(2)).get.head).nonEmpty === true + } + + + "sample" in new AggregateSetup { + strs.contains(Ns.str(sample).get.head) === true + ints.contains(Ns.int(sample).get.head) === true + longs.contains(Ns.long(sample).get.head) === true + floats.contains(Ns.float(sample).get.head) === true + doubles.contains(Ns.double(sample).get.head) === true + bools.contains(Ns.bool(sample).get.head) === true + dates.contains(Ns.date(sample).get.head) === true + uuids.contains(Ns.uuid(sample).get.head) === true + uris.contains(Ns.uri(sample).get.head) === true + enums.contains(Ns.enum(sample).get.head) === true + bigInts.contains(Ns.bigInt(sample).get.head) === true + bigDecs.contains(Ns.bigDec(sample).get.head) === true + + strs.intersect(Ns.str(sample(2)).get.head).size === 2 + ints.intersect(Ns.int(sample(2)).get.head).size === 2 + longs.intersect(Ns.long(sample(2)).get.head).size === 2 + floats.intersect(Ns.float(sample(2)).get.head).size === 2 + doubles.intersect(Ns.double(sample(2)).get.head).size === 2 + bools.intersect(Ns.bool(sample(2)).get.head).size === 2 + dates.intersect(Ns.date(sample(2)).get.head).size === 2 + uuids.intersect(Ns.uuid(sample(2)).get.head).size === 2 + uris.intersect(Ns.uri(sample(2)).get.head).size === 2 + enums.intersect(Ns.enum(sample(2)).get.head).size === 2 + bigInts.intersect(Ns.bigInt(sample(2)).get.head).size === 2 + bigDecs.intersect(Ns.bigDec(sample(2)).get.head).size === 2 + } + + + "distinct" in new CoreSetup { Ns.str.int insert List( ("a", 1), @@ -100,6 +425,22 @@ class Aggregates extends CoreSpec { (3, List("b")) ) + Ns.str(distinct).int(distinct).get === List( + (List("a", "b"), List(1, 3, 2)), + ) + } + + + "count, countDistinct" in new CoreSetup { + + Ns.str.int insert List( + ("a", 1), + ("b", 2), + ("b", 2), + ("b", 3) + ) + Ns.int(4).save + Ns.str.int(count).get.sortBy(_._1) === List( ("a", 1), ("b", 3) @@ -120,10 +461,6 @@ class Aggregates extends CoreSpec { (3, 1) ) - Ns.str(distinct).int(distinct).get === List( - (List("a", "b"), List(1, 3, 2)), - ) - Ns.str(count).str(countDistinct).get === List( (4, 2), ) @@ -136,7 +473,19 @@ class Aggregates extends CoreSpec { Ns.int(count).get === List(5) Ns.int(countDistinct).get === List(4) - Ns.str_(Nil).int.get === List(4) } + + + "Map attributes can't use aggregates" in new AggregateSetup { + expectCompileError( + "m(Ns.intMap(min))", + "molecule.transform.exception.Dsl2ModelException: Map attributes not allowed to use `unify` or aggregate expressions." + ) + + expectCompileError( + """m(Ns.intMapK("a")(min))""", + "molecule.transform.exception.Dsl2ModelException: Map attributes not allowed to use `unify` or aggregate expressions." + ) + } } \ No newline at end of file diff --git a/examples/src/test/scala/molecule/examples/gremlin/gettingStarted/Friends.scala b/examples/src/test/scala/molecule/examples/gremlin/gettingStarted/Friends.scala index 13827f401..cee823e3b 100644 --- a/examples/src/test/scala/molecule/examples/gremlin/gettingStarted/Friends.scala +++ b/examples/src/test/scala/molecule/examples/gremlin/gettingStarted/Friends.scala @@ -172,7 +172,9 @@ class Friends extends MoleculeSpec { ) // Older friends of Vadas + Vadas age - Person.name.age.>=(30).Friends.e_(vadas).age.debugGet + Person.name.age.>=(30).Friends.e_(vadas).age.get === List( + ("peter", 35, 27) + ) // How many young friends does the older people have? Person.name.age.>=(30).Friends.e(count).age_.<(30).get === List( @@ -197,7 +199,7 @@ class Friends extends MoleculeSpec { List( ("vadas", List("marko", "peter")), ("josh", List("marko"))) - ) + ) ) // Marko's friends and their friends (excluding marko) diff --git a/project/releases/v0_18_4.md b/project/releases/v0_18_4.md new file mode 100644 index 000000000..7a0c23f24 --- /dev/null +++ b/project/releases/v0_18_4.md @@ -0,0 +1,5 @@ +# Aggregates for card-many attributes +_2019-05.13 v0.18.4_ + +- Card-many attribute values can now be aggregated. +