Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Introducing Scala Cypher DSL

DZone 's Guide to

Introducing Scala Cypher DSL

A type-safe, compile-time DSL for Neo4J Cypher Query Language in Scala.

· Database Zone ·
Free Resource

Working with Neo4J and Scala at ThoughtWorks, I had a major qualm regarding the way we were interacting with the graph database using Cypher. Cypher is a declarative language, hence it is tricky to compose it programmatically. This is the reason why most of the ORMs (or micro-ORMs) for Neo4J are effective only for simple use cases. We observed that as the scale and complexity of our business logic started to increase, our code fragmented into two distinct flavors. There were Scala models and business implementations. And then there were string generation and manipulation methods to generate Cypher queries.

String-based queries have inherent issues like no type safety, minimal syntax checking, difficulty in composing directly proportional to complexity, etc.

Scala-cypher-DSL aims to leverage the models created as part of business logic and create Cypher queries intelligently and type-safe manner.

Installation

Binary release artifacts are published to the Sonatype OSS Repository Hosting service and synced to Maven Central.

SBT

"me.manishkatoch" %% "scala-cypher-dsl" % "0.4.6"

Gradle

implementation group: 'me.manishkatoch', name: 'scala-cypher-dsl', version: '0.4.6'

Usage

Consider the 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 will fail. Yay!

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)

For detailed DSL usage and more examples, there is a Wiki.

Support and Contributions

Scala-cypher-dsl aims to be an important library for anyone who wants to write idiomatic scala when interacting with Neo4J or any other cypher query language based platforms. I aim to support 100% of cypher specifications, and any form of contribution (issue report, PR, etc.) is more than welcome!

Topics:
database ,tutorial ,scala ,cypher ,compile-time dsl ,type-safe dsl

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}