From bc034f0bd3b49b2111c90f332b760e3b8750b22f Mon Sep 17 00:00:00 2001 From: To-om Date: Sun, 24 Feb 2019 10:49:57 +0100 Subject: [PATCH 1/7] Add type safe projection --- .../scala/gremlin/scala/GremlinScala.scala | 28 +++++++++++++- .../scala/gremlin/scala/ProjectSpec.scala | 38 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index f35f9ec1..608988ff 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -13,7 +13,8 @@ import java.util.{ Map => JMap, Collection => JCollection, Iterator => JIterator, - Set => JSet + Set => JSet, + UUID } import java.util.stream.{Stream => JStream} @@ -116,6 +117,31 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { otherProjectKeys: String*): GremlinScala.Aux[JMap[String, A], Labels] = GremlinScala[JMap[String, A], Labels](traversal.project(projectKey, otherProjectKeys: _*)) + def project[H <: HList]( + builder: ProjectionBuilder[End, HNil] ⇒ ProjectionBuilder[End, H]): GremlinScala[H] = + builder(new ProjectionBuilder(Nil, scala.Predef.identity, _ ⇒ HNil)).build(this) + + class ProjectionBuilder[T, H <: HList] private[gremlin] ( + labels: Seq[String], + addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], + buildResult: JMap[String, Any] ⇒ H) { + + def apply[U, HR <: HList](f: GremlinScala[T] ⇒ GremlinScala[U])( + implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = { + val label = UUID.randomUUID().toString + new ProjectionBuilder[T, HR](labels :+ label, + addBy.andThen(_.by(f(__[T]()).traversal)), + map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) + } + + def and[U, HR <: HList](f: GremlinScala[T] ⇒ GremlinScala[U])( + implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = apply(f) + + private[gremlin] def build(g: GremlinScala[T]): GremlinScala[H] = { + GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) + } + } + /** You might think that predicate should be `GremlinScala[End] => GremlinScala[Boolean]`, * but that's not how tp3 works: e.g. `.value(Age).is(30)` returns `30`, not `true` */ diff --git a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala new file mode 100644 index 00000000..01edbfae --- /dev/null +++ b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala @@ -0,0 +1,38 @@ +package gremlin.scala + +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory +import org.scalatest.{Matchers, WordSpec} +import shapeless.HNil + +class ProjectSpec extends WordSpec with Matchers { + def graph: ScalaGraph = TinkerFactory.createModern.asScala() + + "project steps" should { + "provide type safe result" in { + val result = graph + .V() + .out("created") + .project(_(_.value(Key[String]("name"))) + .and(_.in("created").count())) + .toList() + + result shouldBe List( + "lop" :: 3 :: HNil, + "lop" :: 3 :: HNil, + "lop" :: 3 :: HNil, + "ripple" :: 1 :: HNil + ) + } + + "provide other type safe result" in { + val result = graph + .V() + .has(Key("name").of("marko")) + .project(_(_.outE().count()) + .and(_.inE().count())) + .head() + + result shouldBe (3 :: 0 :: HNil) + } + } +} From 4e99805656724fdf4e1c80439c17c4c048312719 Mon Sep 17 00:00:00 2001 From: To-om Date: Sun, 24 Feb 2019 19:21:17 +0100 Subject: [PATCH 2/7] Use By instead of function --- .../src/main/scala/gremlin/scala/GremlinScala.scala | 10 +++++----- .../src/test/scala/gremlin/scala/ProjectSpec.scala | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index 608988ff..417abe25 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -126,16 +126,16 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], buildResult: JMap[String, Any] ⇒ H) { - def apply[U, HR <: HList](f: GremlinScala[T] ⇒ GremlinScala[U])( + def apply[U, HR <: HList](by: By[U])( implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = { val label = UUID.randomUUID().toString new ProjectionBuilder[T, HR](labels :+ label, - addBy.andThen(_.by(f(__[T]()).traversal)), - map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) + addBy.andThen(by.apply), + map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) } - def and[U, HR <: HList](f: GremlinScala[T] ⇒ GremlinScala[U])( - implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = apply(f) + def and[U, HR <: HList](by: By[U])( + implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = apply(by) private[gremlin] def build(g: GremlinScala[T]): GremlinScala[H] = { GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) diff --git a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala index 01edbfae..8072d414 100644 --- a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala +++ b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala @@ -12,8 +12,8 @@ class ProjectSpec extends WordSpec with Matchers { val result = graph .V() .out("created") - .project(_(_.value(Key[String]("name"))) - .and(_.in("created").count())) + .project(_(By(Key[String]("name"))) + .and(By(__.in("created").count()))) .toList() result shouldBe List( @@ -28,8 +28,8 @@ class ProjectSpec extends WordSpec with Matchers { val result = graph .V() .has(Key("name").of("marko")) - .project(_(_.outE().count()) - .and(_.inE().count())) + .project(_(By(__.outE().count())) + .and(By(__.inE().count()))) .head() result shouldBe (3 :: 0 :: HNil) From f50d1a0be8ac0dfe90f610b702f584a867707a5a Mon Sep 17 00:00:00 2001 From: To-om Date: Sun, 24 Feb 2019 19:55:37 +0100 Subject: [PATCH 3/7] Remove useless type parameter T in ProjectionBuilder Make initial ProjectionBuilder instanciable outside gremlin.scala. --- .../scala/gremlin/scala/GremlinScala.scala | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index 417abe25..ce55fb83 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -118,29 +118,8 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { GremlinScala[JMap[String, A], Labels](traversal.project(projectKey, otherProjectKeys: _*)) def project[H <: HList]( - builder: ProjectionBuilder[End, HNil] ⇒ ProjectionBuilder[End, H]): GremlinScala[H] = - builder(new ProjectionBuilder(Nil, scala.Predef.identity, _ ⇒ HNil)).build(this) - - class ProjectionBuilder[T, H <: HList] private[gremlin] ( - labels: Seq[String], - addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], - buildResult: JMap[String, Any] ⇒ H) { - - def apply[U, HR <: HList](by: By[U])( - implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = { - val label = UUID.randomUUID().toString - new ProjectionBuilder[T, HR](labels :+ label, - addBy.andThen(by.apply), - map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) - } - - def and[U, HR <: HList](by: By[U])( - implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[T, HR] = apply(by) - - private[gremlin] def build(g: GremlinScala[T]): GremlinScala[H] = { - GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) - } - } + builder: ProjectionBuilder[HNil] ⇒ ProjectionBuilder[H]): GremlinScala[H] = + builder(ProjectionBuilder()).build(this) /** You might think that predicate should be `GremlinScala[End] => GremlinScala[Boolean]`, * but that's not how tp3 works: e.g. `.value(Age).is(30)` returns `30`, not `true` @@ -1053,3 +1032,28 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { travs.map(_.apply(start).traversal) } + +class ProjectionBuilder[H <: HList] private[gremlin] ( + labels: Seq[String], + addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], + buildResult: JMap[String, Any] ⇒ H) { + + def apply[U, HR <: HList](by: By[U])( + implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[HR] = { + val label = UUID.randomUUID().toString + new ProjectionBuilder[HR](labels :+ label, + addBy.andThen(by.apply), + map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) + } + + def and[U, HR <: HList](by: By[U])( + implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[HR] = apply(by) + + private[gremlin] def build(g: GremlinScala[_]): GremlinScala[H] = { + GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) + } +} + +object ProjectionBuilder { + def apply() = new ProjectionBuilder[HNil](Nil, scala.Predef.identity, _ ⇒ HNil) +} From f2d3dce808f4bed0077b2382fe27ca21005e789f Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 26 Feb 2019 10:34:26 +0100 Subject: [PATCH 4/7] Replace HList by Tuple --- .../scala/gremlin/scala/GremlinScala.scala | 24 ++++++++++--------- .../scala/gremlin/scala/ProjectSpec.scala | 11 ++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index ce55fb83..39cd7b2c 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -33,7 +33,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.{Bytecode, Path, Scope, Tr import org.apache.tinkerpop.gremlin.structure.{Direction, T} import shapeless.{::, HList, HNil} import shapeless.ops.hlist.{IsHCons, Mapper, Prepend, RightFolder, ToTraversable, Tupler} +import shapeless.ops.tuple.{Prepend => TuplePrepend} import shapeless.ops.product.ToHList +import shapeless.syntax.std.tuple._ import scala.concurrent.duration.FiniteDuration import scala.reflect.runtime.{universe => ru} import scala.collection.{immutable, mutable} @@ -117,8 +119,8 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { otherProjectKeys: String*): GremlinScala.Aux[JMap[String, A], Labels] = GremlinScala[JMap[String, A], Labels](traversal.project(projectKey, otherProjectKeys: _*)) - def project[H <: HList]( - builder: ProjectionBuilder[HNil] ⇒ ProjectionBuilder[H]): GremlinScala[H] = + def project[H <: Product]( + builder: ProjectionBuilder[Nil.type] ⇒ ProjectionBuilder[H]): GremlinScala[H] = builder(ProjectionBuilder()).build(this) /** You might think that predicate should be `GremlinScala[End] => GremlinScala[Boolean]`, @@ -1033,27 +1035,27 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { } -class ProjectionBuilder[H <: HList] private[gremlin] ( +class ProjectionBuilder[T <: Product] private[gremlin] ( labels: Seq[String], addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], - buildResult: JMap[String, Any] ⇒ H) { + buildResult: JMap[String, Any] ⇒ T) { - def apply[U, HR <: HList](by: By[U])( - implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[HR] = { + def apply[U, TR <: Product](by: By[U])( + implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = { val label = UUID.randomUUID().toString - new ProjectionBuilder[HR](labels :+ label, + new ProjectionBuilder[TR](labels :+ label, addBy.andThen(by.apply), map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) } - def and[U, HR <: HList](by: By[U])( - implicit prepend: Prepend.Aux[H, U :: HNil, HR]): ProjectionBuilder[HR] = apply(by) + def and[U, TR <: Product](by: By[U]) + (implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = apply(by) - private[gremlin] def build(g: GremlinScala[_]): GremlinScala[H] = { + private[gremlin] def build(g: GremlinScala[_]): GremlinScala[T] = { GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) } } object ProjectionBuilder { - def apply() = new ProjectionBuilder[HNil](Nil, scala.Predef.identity, _ ⇒ HNil) + def apply() = new ProjectionBuilder[Nil.type](Nil, scala.Predef.identity, _ ⇒ Nil) } diff --git a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala index 8072d414..a47e72f7 100644 --- a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala +++ b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala @@ -2,7 +2,6 @@ package gremlin.scala import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory import org.scalatest.{Matchers, WordSpec} -import shapeless.HNil class ProjectSpec extends WordSpec with Matchers { def graph: ScalaGraph = TinkerFactory.createModern.asScala() @@ -17,10 +16,10 @@ class ProjectSpec extends WordSpec with Matchers { .toList() result shouldBe List( - "lop" :: 3 :: HNil, - "lop" :: 3 :: HNil, - "lop" :: 3 :: HNil, - "ripple" :: 1 :: HNil + ("lop", 3), + ("lop",3), + ("lop",3), + ("ripple", 1) ) } @@ -32,7 +31,7 @@ class ProjectSpec extends WordSpec with Matchers { .and(By(__.inE().count()))) .head() - result shouldBe (3 :: 0 :: HNil) + result shouldBe (3, 0) } } } From a141bb488b431a50acbcac56091a37b06c4501d3 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 27 Feb 2019 21:37:50 +1300 Subject: [PATCH 5/7] add test for 'simple' case, but not happy yet also formatting and restructure of test --- .../scala/gremlin/scala/GremlinScala.scala | 4 +- .../scala/gremlin/scala/ProjectSpec.scala | 66 +++++++++++-------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index 39cd7b2c..06621ca5 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -1048,8 +1048,8 @@ class ProjectionBuilder[T <: Product] private[gremlin] ( map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) } - def and[U, TR <: Product](by: By[U]) - (implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = apply(by) + def and[U, TR <: Product](by: By[U])( + implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = apply(by) private[gremlin] def build(g: GremlinScala[_]): GremlinScala[T] = { GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) diff --git a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala index a47e72f7..8a829fea 100644 --- a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala +++ b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala @@ -4,34 +4,46 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory import org.scalatest.{Matchers, WordSpec} class ProjectSpec extends WordSpec with Matchers { - def graph: ScalaGraph = TinkerFactory.createModern.asScala() - "project steps" should { - "provide type safe result" in { - val result = graph - .V() + "projecting by one thing" in { + // val result: String = //fails + // val result: (String) = //fails + val result: Tuple1[String] = + // val result = + graph.V + .has(name.of("marko")) + .project(_(By(name))) + .head + println(result) //(marko) + println(result.getClass) // scala.Tuple1 + } + + "projecting by two traversals" in { + val result: (java.lang.Long, java.lang.Long) = + graph.V + .has(name.of("marko")) + .project(_(By(__.outE.count)).and(By(__.inE.count))) + .head + + result shouldBe (3, 0) + } + + "projecting by property and traversal" in { + val result: List[(String, java.lang.Long)] = + graph.V .out("created") - .project(_(By(Key[String]("name"))) - .and(By(__.in("created").count()))) - .toList() - - result shouldBe List( - ("lop", 3), - ("lop",3), - ("lop",3), - ("ripple", 1) - ) - } - - "provide other type safe result" in { - val result = graph - .V() - .has(Key("name").of("marko")) - .project(_(By(__.outE().count())) - .and(By(__.inE().count()))) - .head() - - result shouldBe (3, 0) - } + .project(_(By(name)).and(By(__.in("created").count))) + .toList + + result shouldBe List( + ("lop", 3), + ("lop", 3), + ("lop", 3), + ("ripple", 1) + ) } + + def graph: ScalaGraph = TinkerFactory.createModern.asScala + val name = Key[String]("name") + } From 8d47c49d534b2aec871ceb4735aff8ec9a269a66 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 28 Feb 2019 06:47:25 +1300 Subject: [PATCH 6/7] remove non-sensical test --- .../src/test/scala/gremlin/scala/ProjectSpec.scala | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala index 8a829fea..9713e5a0 100644 --- a/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala +++ b/gremlin-scala/src/test/scala/gremlin/scala/ProjectSpec.scala @@ -5,19 +5,6 @@ import org.scalatest.{Matchers, WordSpec} class ProjectSpec extends WordSpec with Matchers { - "projecting by one thing" in { - // val result: String = //fails - // val result: (String) = //fails - val result: Tuple1[String] = - // val result = - graph.V - .has(name.of("marko")) - .project(_(By(name))) - .head - println(result) //(marko) - println(result.getClass) // scala.Tuple1 - } - "projecting by two traversals" in { val result: (java.lang.Long, java.lang.Long) = graph.V From 53ac07af586a96df7425c0f9749e6c40ce41d4e7 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 28 Feb 2019 09:46:33 +1300 Subject: [PATCH 7/7] refactor --- .../scala/gremlin/scala/GremlinScala.scala | 31 ++----------------- .../gremlin/scala/ProjectionBuilder.scala | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 gremlin-scala/src/main/scala/gremlin/scala/ProjectionBuilder.scala diff --git a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala index 06621ca5..55cfa9c2 100644 --- a/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala +++ b/gremlin-scala/src/main/scala/gremlin/scala/GremlinScala.scala @@ -13,8 +13,7 @@ import java.util.{ Map => JMap, Collection => JCollection, Iterator => JIterator, - Set => JSet, - UUID + Set => JSet } import java.util.stream.{Stream => JStream} @@ -33,7 +32,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.{Bytecode, Path, Scope, Tr import org.apache.tinkerpop.gremlin.structure.{Direction, T} import shapeless.{::, HList, HNil} import shapeless.ops.hlist.{IsHCons, Mapper, Prepend, RightFolder, ToTraversable, Tupler} -import shapeless.ops.tuple.{Prepend => TuplePrepend} import shapeless.ops.product.ToHList import shapeless.syntax.std.tuple._ import scala.concurrent.duration.FiniteDuration @@ -120,7 +118,7 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { GremlinScala[JMap[String, A], Labels](traversal.project(projectKey, otherProjectKeys: _*)) def project[H <: Product]( - builder: ProjectionBuilder[Nil.type] ⇒ ProjectionBuilder[H]): GremlinScala[H] = + builder: ProjectionBuilder[Nil.type] => ProjectionBuilder[H]): GremlinScala[H] = builder(ProjectionBuilder()).build(this) /** You might think that predicate should be `GremlinScala[End] => GremlinScala[Boolean]`, @@ -1034,28 +1032,3 @@ class GremlinScala[End](val traversal: GraphTraversal[_, End]) { travs.map(_.apply(start).traversal) } - -class ProjectionBuilder[T <: Product] private[gremlin] ( - labels: Seq[String], - addBy: GraphTraversal[_, JMap[String, Any]] ⇒ GraphTraversal[_, JMap[String, Any]], - buildResult: JMap[String, Any] ⇒ T) { - - def apply[U, TR <: Product](by: By[U])( - implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = { - val label = UUID.randomUUID().toString - new ProjectionBuilder[TR](labels :+ label, - addBy.andThen(by.apply), - map ⇒ buildResult(map) :+ map.get(label).asInstanceOf[U]) - } - - def and[U, TR <: Product](by: By[U])( - implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = apply(by) - - private[gremlin] def build(g: GremlinScala[_]): GremlinScala[T] = { - GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) - } -} - -object ProjectionBuilder { - def apply() = new ProjectionBuilder[Nil.type](Nil, scala.Predef.identity, _ ⇒ Nil) -} diff --git a/gremlin-scala/src/main/scala/gremlin/scala/ProjectionBuilder.scala b/gremlin-scala/src/main/scala/gremlin/scala/ProjectionBuilder.scala new file mode 100644 index 00000000..700d5a0a --- /dev/null +++ b/gremlin-scala/src/main/scala/gremlin/scala/ProjectionBuilder.scala @@ -0,0 +1,31 @@ +package gremlin.scala + +import java.util.{Map => JMap, UUID} +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal +import shapeless.ops.tuple.{Prepend => TuplePrepend} +import shapeless.syntax.std.tuple._ + +class ProjectionBuilder[T <: Product] private[gremlin] ( + labels: Seq[String], + addBy: GraphTraversal[_, JMap[String, Any]] => GraphTraversal[_, JMap[String, Any]], + buildResult: JMap[String, Any] => T) { + + def apply[U, TR <: Product](by: By[U])( + implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = { + val label = UUID.randomUUID().toString + new ProjectionBuilder[TR](labels :+ label, + addBy.andThen(by.apply), + map => buildResult(map) :+ map.get(label).asInstanceOf[U]) + } + + def and[U, TR <: Product](by: By[U])( + implicit prepend: TuplePrepend.Aux[T, Tuple1[U], TR]): ProjectionBuilder[TR] = apply(by) + + private[gremlin] def build(g: GremlinScala[_]): GremlinScala[T] = { + GremlinScala(addBy(g.traversal.project(labels.head, labels.tail: _*))).map(buildResult) + } +} + +object ProjectionBuilder { + def apply() = new ProjectionBuilder[Nil.type](Nil, scala.Predef.identity, _ => Nil) +}