Skip to content
Manish Katoch edited this page Jul 8, 2019 · 3 revisions

Usage

for adding to your project, see how to install

Basics

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.

Quick Start

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)
Clone this wiki locally