{"id":15402037,"url":"https://github.com/mpollmeier/gremlin-scala","last_synced_at":"2025-04-13T02:15:45.721Z","repository":{"id":6282136,"uuid":"7516189","full_name":"mpollmeier/gremlin-scala","owner":"mpollmeier","description":"[unmaintained] Scala wrapper for Apache TinkerPop 3 Graph DSL","archived":false,"fork":false,"pushed_at":"2022-10-20T09:54:59.000Z","size":2666,"stargazers_count":481,"open_issues_count":32,"forks_count":75,"subscribers_count":39,"default_branch":"master","last_synced_at":"2025-04-13T02:15:04.119Z","etag":null,"topics":["graph","graph-database","graphdb","gremlin","scala","type-safe"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mpollmeier.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-09T06:42:59.000Z","updated_at":"2025-03-24T12:43:28.000Z","dependencies_parsed_at":"2023-01-11T20:13:43.789Z","dependency_job_id":null,"html_url":"https://github.com/mpollmeier/gremlin-scala","commit_stats":null,"previous_names":[],"tags_count":191,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpollmeier%2Fgremlin-scala","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpollmeier%2Fgremlin-scala/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpollmeier%2Fgremlin-scala/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpollmeier%2Fgremlin-scala/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mpollmeier","download_url":"https://codeload.github.com/mpollmeier/gremlin-scala/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248654106,"owners_count":21140237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graph","graph-database","graphdb","gremlin","scala","type-safe"],"created_at":"2024-10-01T15:59:56.964Z","updated_at":"2025-04-13T02:15:45.678Z","avatar_url":"https://github.com/mpollmeier.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"![logo](https://github.com/mpollmeier/gremlin-scala/raw/master/doc/images/gremlin-scala-logo.png)\n\n[![Build Status](https://github.com/mpollmeier/gremlin-scala/workflows/release/badge.svg)](https://github.com/mpollmeier/gremlin-scala/actions?query=workflow%3Arelease)\n[![Join the chat at https://gitter.im/mpollmeier/gremlin-scala](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mpollmeier/gremlin-scala?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.michaelpollmeier/gremlin-scala_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.michaelpollmeier/gremlin-scala_2.13)\n[![scaladoc](http://www.javadoc.io/badge/com.michaelpollmeier/gremlin-scala_2.13.svg?color=blue\u0026label=scaladoc)](http://www.javadoc.io/doc/com.michaelpollmeier/gremlin-scala_2.13)\n\n## Gremlin-Scala for Apache Tinkerpop 3\n\nA wrapper to use [Apache Tinkerpop3](https://github.com/apache/incubator-tinkerpop) - a JVM graph traversal library - from Scala.\n\n* Beautiful DSL to create vertices and edges\n* Type safe traversals\n* Scala friendly function signatures\n* Minimal runtime overhead - only allocates additional instances if absolutely necessary\n* Nothing is hidden away, you can always easily access the underlying Gremlin-Java objects if needed, e.g. to access graph db specifics things like indexes\n\n### TOC\n\u003c!-- markdown-toc --maxdepth 2 --no-firsth1 README.md --\u003e\n- [Getting started](#getting-started)\n- [Using the sbt console](#using-the-sbt-console)\n- [Simple traversals](#simple-traversals)\n- [Vertices and edges with type safe properties](#vertices-and-edges-with-type-safe-properties)\n- [Compiler helps to eliminate invalid traversals](#compiler-helps-to-eliminate-invalid-traversals)\n- [Type safe traversals](#type-safe-traversals)\n- [A note on predicates](#a-note-on-predicates)\n- [Build a custom DSL on top of Gremlin-Scala](#build-a-custom-dsl-on-top-of-gremlin-scala)\n- [Common and useful steps](#common-and-useful-steps)\n- [Mapping vertices from/to case classes](#mapping-vertices-fromto-case-classes)\n- [More advanced traversals](#more-advanced-traversals)\n- [Serialise to and from files](#serialise-to-and-from-files)\n- [Help - it's open source!](#help---its-open-source)\n- [Why such a long version number?](#why-such-a-long-version-number)\n- [Talks](#talks)\n- [Further reading](#further-reading)\n- [Random things worth knowing](#random-things-worth-knowing)\n- [Releases](#releases)\n- [Breaking changes](#breaking-changes)\n\n### Getting started\nThe [examples project](https://github.com/mpollmeier/gremlin-scala-examples) comes with working examples for different graph databases. Typically you just need to add a dependency on `\"com.michaelpollmeier\" %% \"gremlin-scala\" % \"SOME_VERSION\"` and one for the graph db of your choice to your `build.sbt` (this readme assumes tinkergraph). The latest version is displayed at the top of this readme in the maven badge. \n\n### Using the sbt console\n* `sbt gremlin-scala/Test/console`\n```scala\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory\nimplicit val graph = TinkerFactory.createModern.asScala\n\nval name = Key[String](\"name\")\n\nval g = graph.traversal\ng.V.hasLabel(\"person\").value(name).toList\n// List(marko, vadas, josh, peter)\n```\n\n### Simple traversals\n\nThe below create traversals, which are lazy computations. To run a traversal, you can use e.g. `toSet`, `toList`, `head`, `headOption` etc. \n\n```scala\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.process.traversal.{Order, P}\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory\n\nimplicit val graph = TinkerFactory.createModern.asScala\nval g = graph.traversal\n\ng.V //all vertices\ng.E //all edges\n\ng.V(1).outE(\"knows\") //follow outgoing edges\ng.V(1).out(\"knows\") //follow outgoing edges to incoming vertex\n\nval weight = Key[Double](\"weight\")\nfor {\n  person \u003c- g.V.hasLabel(\"person\")\n  favorite \u003c- person.outE(\"likes\").order(By(weight, Order.decr)).limit(1).inV\n} yield (person, favorite.label)\n\n// remove all people over 30 from the g - also removes corresponding edges\nval Age = Key[Int](\"age\")\ng.V.hasLabel(\"person\").has(Age, P.gte(30)).drop.iterate\n```\n\nWarning: GremlinScala is _not_ a monad, because the underlying Tinkerpop GraphTraversal is not. I.e. while GremlinScala offers `map`, `flatMap` etc. and you can use it in a for comprehension for syntactic sugar, it does not fulfil all monad laws. \n\nMore working examples in [TraversalSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/TraversalSpec.scala).\n\n### Vertices and edges with type safe properties\n\n```scala\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph\nimport scala.language.postfixOps\nimplicit val graph = TinkerGraph.open.asScala\n\n// Keys for properties which can later be used for type safe traversals\nval Founded = Key[String](\"founded\")\nval Distance = Key[Int](\"distance\")\n\n// create labelled vertex\nval paris = graph + \"Paris\"\n\n// create vertex with typed properties\nval london = graph + (\"London\", Founded -\u003e \"43 AD\")\n\n// create labelled edges \nparis --- \"OneWayRoad\" --\u003e london\nparis \u003c-- \"OtherWayAround\" --- london\nparis \u003c-- \"Eurostar\" --\u003e london\n\n// create edge with typed properties\nparis --- (\"Eurostar\", Distance -\u003e 495) --\u003e london\n\n// type safe access to properties\nparis.out(\"Eurostar\").value(Founded).head //43 AD\nparis.outE(\"Eurostar\").value(Distance).head //495\nlondon.valueOption(Founded) //Some(43 AD)\nlondon.valueOption(Distance) //None\nparis.setProperty(Founded, \"300 BC\")\n\nval Name = Key[String](\"name\")\nval Age = Key[Int](\"age\")\n\nval v1 = graph + (\"person\", Name -\u003e \"marko\", Age -\u003e 29) asScala\n\nv1.keys // Set(Key(\"name\"), Key(\"age\"))\nv1.property(Name) // \"marko\"\nv1.valueMap // Map(\"name\" -\u003e \"marko\", \"age\" -\u003e 29)\nv1.valueMap(\"name\", \"age\") // Map(\"name\" -\u003e \"marko\", \"age\" -\u003e 29)\n```\n\nMore working examples in [SchemaSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/SchemaSpec.scala), [ArrowSyntaxSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/ArrowSyntaxSpec.scala) and [ElementSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/ElementSpec.scala).\n\n### Compiler helps to eliminate invalid traversals\nGremlin-Scala aims to helps you at compile time as much as possible. Take this simple example:\n\n```scala\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph\nimplicit val graph = TinkerGraph.open.asScala\nval g = graph.traversal\ng.V.outE.inV  //compiles\ng.V.outE.outE //does _not_ compile\n```\n\nIn Gremlin-Groovy there's nothing stopping you to create the second traversal - it will explode at runtime, as outgoing edges do not have outgoing edges. In Gremlin-Scala this simply doesn't compile.\n\n### Type safe traversals\nYou can label any step using `as(StepLabel)` and the compiler will infer the correct types for you in the select step using an HList (a type safe list, i.e. the compiler knows the types of the elements of the list). In Gremlin-Java and Gremlin-Groovy you get a `Map[String, Any]`, so you have to cast to the type you *think* it will be, which is ugly and error prone. For example:\n\n```scala\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph\ndef graph = TinkerFactory.createModern.asScala\nval g = graph.traversal\n\n// select all labelled steps\ng.V(1).as(\"a\").outE.as(\"b\").select.toList\n// returns a `(Vertex, Edge)` for each path\n\n// select subset of labelled steps\nval a = StepLabel[Vertex]()\nval b = StepLabel[Edge]()\nval c = StepLabel[Double]()\n\nval traversal = g.V(1).as(a).outE(\"created\").as(b).value(\"weight\").as(c)\n    \ntraversal.select((b, c)).head\n// returns a `(Edge, Double)`\n```\n\nMore working examples in [SelectSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/SelectSpec.scala). Kudos to [shapeless](https://github.com/milessabin/shapeless/) and Scala's sophisticated type system that made this possible. \n\nAs of 3.3.3.2 there is a typesafe `union` step that supports heterogeneous queries:\n```scala\nval traversal =\n  g.V(1).union(\n    _.join(_.outE)\n     .join(_.out)\n  )\n// derived type: GremlinScala[(List[Edge], List[Vertex])] \nval (outEdges, outVertices) = traversal.head\n```\n\n### A note on predicates\ntl;dr: use gremlin.scala.P to create predicates of type P. \n\nMany steps in take a tinkerpop3 predicate of type `org.apache.tinkerpop.gremlin.process.traversal.P`. Creating Ps that take collection types is dangerous though, because you need to ensure you're creating the correct P. For example `P.within(Set(\"a\", \"b\"))` would be calling the wrong overload (which checks if the value IS the given set). In that instance you actually wanted to create `P.within(Set(\"a\", \"b\").asJava: java.util.Collection[String])`. To avoid that confusion, it's best to just `import gremlin.scala._` and create it as `P.within(Set(\"a\", \"b\"))`.\n\n### Build a custom DSL on top of Gremlin-Scala\nYou can now build your own domain specific language, which is super helpful if you don't want to expose your users to the world of graphs and tinkerpop, but merely build an API for them. All you need to do is setup your ADT as case classes, define your DSL as Steps and create one implicit constructor (the only boilerplate code). The magic in gremlin.scala.dsl._ allows you to even write for comprehensions like this (DSL for tinkerpop testgraph):\n\n```scala\ncase class Person  (name: String, age: Integer) extends DomainRoot\ncase class Software(name: String, lang: String) extends DomainRoot\n\nval traversal = for {\n  person   \u003c- PersonSteps(graph)\n  software \u003c- person.created\n} yield (person.name, software)\n\n// note: `traversal` is inferred by the compiler as `gremlin.scala.dsl.Steps[(String, Software)]`\n\ntraversal.toSet // returns: \nSet(\n  (\"marko\", Software(\"lop\", \"java\")),\n  (\"josh\", Software(\"lop\", \"java\")),\n  (\"peter\", Software(\"lop\", \"java\")),\n  (\"josh\", Software(\"ripple\", \"java\"))\n)\n\n// DSL also supports typesafe as/select:\nPersonSteps(graph)\n  .as(\"person\")\n  .created\n  .as(\"software\")\n  .select\n  .toList\n// inferred return type is `List[(Person, Software)]`\n```\n\nSee the full setup and more tests in [DslSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/dsl/DslSpec.scala).\n\n### Common and useful steps\n\n```scala\n// get a vertex by id\ng.V(1).headOption\n\n// get all vertices\ng.V.toList\n\n// group all vertices by their label\ng.V.group(By.label)\n\n// group vertices by a property\nval age = Key[Int](\"age\")\ng.V.has(age).group(By(age))\n\n// order by property decreasing\nval age = Key[Int](\"age\")\ng.V.has(age).order(By(age, Order.decr))\n```\n\nMore working examples in [TraversalSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/TraversalSpec.scala).\n\n### Mapping vertices from/to case classes\nYou can save and load case classes as a vertex - implemented with a [blackbox macro](http://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html).\n\n* You can optionally specify the label of your class using `@label`\n* `Option` members will be automatically unwrapped, i.e. a `Some[A]` will be stored as the value of type `A` in the database, or `null` if it's `None`. If we wouldn't unwrap it, the database would have to understand Scala's Option type itself. \n* The same goes for value classes, i.e. a `case class ShoeSize(value: Int) extends AnyVal` will be stored as an Integer. \n* `List` members will be stored as multi-properties, i.e. Cardinality.list\n* Annotating members with `@id` and `@underlying` will instruct the marshaller to set the element id and/or the underlying element in the class. Note: you cannot specify the id when adding a vertex like this. Using `@id` only works when retrieving the vertex back from the graph and it therefor must be an `Option`.\n* Your classes must be defined outside the scope where they are being used (e.g. in the code below the class `Example` cannot be inside `object Main`). \n\n_Warning_: this may not work with your particular remote graph, depending on the implementation/configuration. That's because the graph [may choose to only return *referenced elements* which doesn't contain it's properties](http://tinkerpop.apache.org/docs/3.4.1/reference/#_properties_of_elements).\n\n\n```scala\n// this does _not_ work in a REPL\nimport gremlin.scala._\nimport org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph\n\n@label(\"my_custom_label\")\ncase class Example(longValue: Long, stringValue: Option[String], @underlying vertex: Option[Vertex] = None)\n\nobject Main extends App {\n  implicit val graph = TinkerGraph.open.asScala\n  val example = Example(Long.MaxValue, Some(\"optional value\"))\n  val v = graph + example\n  v.toCC[Example] // equal to `example`, but with `vertex` set\n\n  // find all vertices with the label of the case class `Example`\n  graph.V.hasLabel[Example]\n\n  // modify the vertex like a case class\n  v.updateAs[Example](_.copy(longValue = 0L))\n}\n```\n\nYou can also define your own marshaller, if the macro generated one doesn't quite cut it. For that and more examples check out the [MarshallableSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/marshallable/MarshallableSpec.scala).\n\n### More advanced traversals\nHere are some examples of more complex traversals from the [examples repo](https://github.com/mpollmeier/gremlin-scala-examples/). If you want to run them yourself, check out the tinkergraph examples in there. \n\n_What's the property distribution of all vertices?_\n```scala\ngraph.V.groupCount(By(__.properties().count)).head\n```\n\n_What is `Die Hard's` average rating?_\n```scala\ngraph.V.has(\"movie\", \"name\", \"Die Hard\")\n  .inE(\"rated\")\n  .values(\"stars\")\n  .mean\n  .head\n```\n\n_Get the maximum number of movies a single user rated_\n```scala\ng.V.hasLabel(\"person\")\n  .flatMap(_.outE(\"rated\").count)\n  .max\n  .head\n```\n\n_What 80's action movies do 30-something programmers like?_\n_Group count the movies by their name and sort the group count map in decreasing order by value._\n```scala\ng.V\n  .`match`(\n    __.as(\"a\").hasLabel(\"movie\"),\n    __.as(\"a\").out(\"hasGenre\").has(\"name\", \"Action\"),\n    __.as(\"a\").has(\"year\", P.between(1980, 1990)),\n    __.as(\"a\").inE(\"rated\").as(\"b\"),\n    __.as(\"b\").has(\"stars\", 5),\n    __.as(\"b\").outV().as(\"c\"),\n    __.as(\"c\").out(\"hasOccupation\").has(\"name\", \"programmer\"),\n    __.as(\"c\").has(\"age\", P.between(30, 40))\n  )\n  .select[Vertex](\"a\")\n  .map(_.value[String](\"name\"))\n  .groupCount()\n  .order(Scope.local).by(Order.valueDecr)\n  .limit(Scope.local, 10)\n  .head\n```\n\n_What is the most liked movie in each decade?_\n```scala\ng.V()\n  .hasLabel(Movie)\n  .where(_.inE(Rated).count().is(P.gt(10)))\n  .groupBy { movie =\u003e\n    val year = movie.value2(Year)\n    val decade = (year / 10)\n    (decade * 10): Integer\n  }\n  .map { moviesByDecade =\u003e\n    val highestRatedByDecade = moviesByDecade.mapValues { movies =\u003e\n      movies.toList\n        .sortBy { _.inE(Rated).value(Stars).mean().head }\n        .reverse.head //get the movie with the highest mean rating\n    }\n    highestRatedByDecade.mapValues(_.value2(Name))\n  }\n  .order(Scope.local).by(Order.keyIncr)\n  .head\n```\n\n### Serialise to and from files\nCurrently graphML, graphson and gryo/kryo are supported file formats, it is very easy to serialise and deserialise into those: see [GraphSerialisationSpec](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/GraphSerialisationSpec.scala). \nAn easy way to visualise your graph is to export it into graphML and import it into [gephi](https://gephi.org/). \n\n### Help - it's open source!\nIf you would like to help, here's a list of things that needs to be addressed:\n* add more graph databases and examples into the [examples project](https://github.com/mpollmeier/gremlin-scala-examples)\n* port over more TP3 steps - see [TP3 testsuite](https://github.com/apache/incubator-tinkerpop/tree/master/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step) and [Gremlin-Scala StandardTests](https://github.com/mpollmeier/gremlin-scala/blob/master/gremlin-scala/src/test/scala/gremlin/scala/GremlinStandardTestSuite.scala)\n* ideas for more type safety in traversals\n* fill this readme and provide other documentation, or how-tos, e.g. a blog post or tutorial\n\n### Why such a long version number?\nThe first three digits is the TP3 version number, only the last digit is automatically incremented on every release of gremlin-scala.\n\n### Talks\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=XIYBbJhhd9k\" target=\"_blank\"\u003e\n  \u003cimg src=\"doc/images/scaladays-talk-shot.png\" alt=\"ScalaDays Berlin 2018\" height=\"360\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n### Further reading\nFor more information about Gremlin see the [Gremlin docs](http://tinkerpop.incubator.apache.org/) and the [Gremlin users mailinglist](https://groups.google.com/forum/#!forum/gremlin-users).\nPlease note that while Gremlin-Scala is very close to the original Gremlin, there are differences to Gremlin-Groovy - don't be afraid, they hopefully all make sense to a Scala developer ;)\n\nRandom links:\n* Social Network using Titan Db: [part 1](https://partialflow.wordpress.com/2017/02/26/social-network-using-titan-db-part-1/) and [part 2](https://partialflow.wordpress.com/2017/03/04/social-network-using-titan-db-part-2/)\n* [Shortest path algorithm with Gremlin-Scala 3.0.0 (Michael Pollmeier)](http://www.michaelpollmeier.com/2014/12/27/gremlin-scala-shortest-path)\n* [Shortest path algorithm with Gremlin-Scala 2.4.1 (Stefan Bleibinhaus)](http://bleibinha.us/blog/2013/10/scala-and-graph-databases-with-gremlin-scala)\n\n### Random things worth knowing\n* `org.apache.tinkerpop.gremlin.structure.Transaction` is not thread-safe. It's implemented with Apache's ThreadLocal class, see https://github.com/mpollmeier/gremlin-scala/issues/196#issuecomment-301625679\n\n### Releases\n... happen automatically for every commit on `master` from [travis.ci](https://travis-ci.org/mpollmeier/gremlin-scala) thanks to [sbt-ci-release-early](https://github.com/ShiftLeftSecurity/sbt-ci-release-early)\n\n### YourKit endorsement\nYourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications.\nYourKit is the creator of \u003ca href=\"https://www.yourkit.com/java/profiler/\"\u003eYourKit Java Profiler\u003c/a\u003e,\n\u003ca href=\"https://www.yourkit.com/.net/profiler/\"\u003eYourKit .NET Profiler\u003c/a\u003e, and \u003ca href=\"https://www.yourkit.com/youmonitor/\"\u003eYourKit YouMonitor\u003c/a\u003e. \u003cbr/\u003e\n\u003cimg src=\"doc/images/yourkit.png\" height=\"44\" /\u003e\n\n### Breaking changes\n#### 3.4.7.2\nMarshallable now treats Sets as multi-properties, i.e. one property for each element in the set. This is similar to how\nList properties are handled and allows for a natural representation of vertex properties whose cardinality is `set`\nin case classes. This change breaks compatibility with Marshallable's previous behaviour in which Sets were effectively\ntreated as `single` cardinality properties, i.e. a single property whose value is the entire set.\n\n#### 3.4.1.13\nThe implementation for `@label` with non literal values (e.g. `@label(SomeClass.LABEL)`) was dropped due to it's [bad performance](https://github.com/mpollmeier/gremlin-scala/issues/288). Please use String literals instead, e.g. `@label(\"MyLabel\")`.\n\n#### 3.3.3.2\nWe now have a fully typed `union` step which supports heterogeneous queries. The old version is still available as `unionFlat`, since it may still be relevant in some situations where the union traversals are homogeneous.\n\n#### 3.3.2.0\nThe `by` modulator is now called `By`. E.g. `order(by(Order.decr))` becomes `order(By(Order.decr))`.\nBackground: case insensitive platforms like OSX (default) and Windows fail to compile `object by` and `trait By` because they lead to two separate .class files. I decided for this option because it conforms to Scala's naming best practices. \nSee https://github.com/mpollmeier/gremlin-scala/issues/237#issuecomment-375928284. \n\n#### 3.3.1.2\nTo fix problems with remote graphs and the arrow syntax (e.g. `vertex1 --- \"label\" --\u003e vertex2`) there now needs to be an `implicit ScalaGraph` in scope. Background: the configuration for remote is unfortunately not stored in the Tinkerpop Graph instance, but in the TraversalSource. Since a vertex only holds a reference to the graph instance, this configuration must be passed somehow. `ScalaGraph` does contain the configuration, e.g. for remote connections, so we now pass it implicitly. \n\n#### 3.3.1.1\nThe type signature of GremlinScala changed: the former type parameter `Labels` is now a type member, which shortens the type if you don't care about Labels. The Labels were only used in a small percentage of steps, but had to be written out by users all the time even if they didn't care.\nRewrite rules (old -\u003e new), using `Vertex` as an example:\n`GremlinScala[Vertex, _]` -\u003e `GremlinScala[Vertex]` (existential type: most common, i.e the user doesn't use or care about the Labels)\n`GremlinScala[Vertex, HNil]` -\u003e `GremlinScala.Aux[Vertex, HNil]` (equivalent: `GremlinScala[Vertex] {type Labels = HNil}`)\n`GremlinScala[Vertex, Vertex :: HNil]` -\u003e `GremlinScala.Aux[Vertex, Vertex :: HNil]` (equivalent: `GremlinScala[Vertex] {type Labels = Vertex :: HNil}`)\nNotice: GremlinScala isn't a case class any more - it shouldn't have been in the first place.\n\n#### 3.2.4.8 \nThe `filter` step changed it's signature and now takes a traversal: `filter(predicate: GremlinScala[End, _] =\u003e GremlinScala[_, _])`. The old `filter(predicate: End =\u003e Boolean)` is now called `filterOnEnd`, in case you still need it. This change might affect your for comprehensions. \n\nThe reasoning for the change is that it's discouraged to use lambdas (see http://tinkerpop.apache.org/docs/current/reference/#a-note-on-lambdas). Instead we are now creating anonymous traversals, which can be optimised by the driver, sent over the wire as gremlin binary for remote execution etc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpollmeier%2Fgremlin-scala","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmpollmeier%2Fgremlin-scala","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpollmeier%2Fgremlin-scala/lists"}