-
Notifications
You must be signed in to change notification settings - Fork 6
Usage
for adding to your project, see how to install
scala-cypher-dsl is convention-based and relies on how the domain models are defined. The case class name is used as label while its properties are used as cypher node/relationship properties. A model is considered a node or a relationship depending on where it is positioned on a graph path. No annotations/ no special derivations.
scala-cypher-dsl has three important elements: cypher, Context
and QueryProvider[T]
. cypher
provides the DSL and abstractions to generate the final query and its parameters. Context
maintains the state of the cypher query composition and provides way to compose DSL in a distributed fashion (see examples). QueryProvider[T]
is a generic engine that provides query information, given a model of type T
.
Note:
QueryProvider[T]
does the necessary transformations at compile time and hence should be cached for optimal performance. for more on implicit caching, see this.
Consider following domain models representing people working in a fictitious department and friendly by nature.
//sample domain models
case class Person(id: String, name: String, age: Int)
case class WorksIn(sinceDays: Int)
case class IsFriendOf(since: Int, lastConnectedOn: String)
case class Department(id: String, name: String)
To start writing query DSL, import the following
import me.manishkatoch.scala.cypherDSL.spec.syntax.v1._
import me.manishkatoch.scala.cypherDSL.spec.syntax.patterns._ //optional, import for expressing paths.
using DSL for a simple match query generation for an instance of model
//for a person John Doe
val johnDoe = Person("AX31SD", "John Doe", 50)
//match and return Neo4J data
val johnDoeQuery = cypher.MATCH(johnDoe)
.RETURN(johnDoe)
.toQuery()
johnDoeQuery.query
//res0: String = MATCH (a0:Person {id: {a0_id},name: {a0_name},age: {a0_age}})
// RETURN a0
johnDoeQuery.queryMap
//res1: scala.collection.immutable.Map[String,Any] = Map(a0_id -> AX31SD, a0_name -> John Doe, a0_age -> 50))
match Person only by a property(e.g. name)
//for a person John Doe
val johnDoe = Person("AX31SD", "John Doe", 50)
//match and return Neo4J data
val johnDoeQuery = cypher.MATCH(johnDoe('name))
.RETURN(johnDoe)
.toQuery()
johnDoeQuery.query
//res0: String = MATCH (a0:Person {id: {a0_id},name: {a0_name},age: {a0_age}})
// RETURN a0
johnDoeQuery.queryMap
//res1: scala.collection.immutable.Map[String,Any] = Map(a0_id -> AX31SD, a0_name -> John Doe, a0_age -> 50))
Note: if the property doesn't exist, compilation fails.
using DSL for matching any instance of model.
//for any person
val anyPerson = any[Person] // any instance of node labelled Person
val result = cypher.MATCH(anyPerson)
.RETURN(anyPerson)
.toQuery()
result.query
//res0: String = MATCH (a0:Person)
// RETURN a0
result.queryMap
//res1: scala.collection.immutable.Map[String,Any] = Map()
query for all the friends of John Doe in Science department
val scienceDept = Department("ZSW12R", "Science")
val anyPerson = any[Person]
val isFriendOf = anyRel[IsFriendOf] //any relation instance of label IsFriendOf
val result = cypher.MATCH(johnDoe -| isFriendOf |-> anyPerson <-- scienceDept)
.RETURN(anyPerson)
.toQuery()
result.query
//res0: String = MATCH (a0:Person {id: {a0_id},name: {a0_name},age: {a0_age}})-[a1:IS_FRIEND_OF]->(a2:Person)<--(a3:Department {id: {a3_id},name: {a3_name}})
// RETURN a2
result.queryMap
//res1: scala.collection.immutable.Map[String,Any] = Map(a0_id -> AX31SD, a0_name -> John Doe, a3_name -> Science, a0_age -> 50, a3_id -> ZSW12R)