Skip to content

manishkkatoch/scala-cypher-dsl

Repository files navigation

scala-cypher-dsl Build Status Sonatype Nexus (Releases) Maven Central

A type-safe, compile time DSL for writing Cypher queries in Scala.

Motivation

With Neo4J and Scala, the ORMs satisfy only a small subset of querying needs and majority of the fairly complex cypher query tend to be in the form of strings.Cypher strings (like SQL strings) have inherent issues like no type safety, minimal syntax checking, difficulty in composing etc.

Scala-Cypher-DSL aims to alleviate above by providing following:

  1. Type-safe constructs using user defined models and ADTs
  2. Chainable DSL like Cypher.
  3. Automatic identifiers generation( you don't have to manage identifiers, just work with instances/models).
  4. Parameterized queries and automatic creation of parameters map.

Note: It does not provide drivers for Neo4J but only concerns with query and query parameters creation

Installation

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

SBT

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

Gradle

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

Usage

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 {name: {a0_name}})
//              RETURN a0

johnDoeQuery.queryMap
//res1: scala.collection.immutable.Map[String,Any] = Map(a0_name -> John Doe))

Note: if the property doesn't exist, compilation will fail.

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, see Wiki

DSL Specifications

as of v0.4.5

Cypher Clauses DSL Support
MATCH
OPTIONAL MATCH
START
RETURN
WITH
UNWIND
WHERE
ORDER BY
SKIP
LIMIT
CREATE
DELETE
SET
REMOVE
FOREACH
MERGE
CALL […​YIELD]
CREATE UNIQUE
UNION

Contributors and Participation

scala-cypher-dsl is currently maintained by Manish Katoch.

Any form of contribution (issue report, PR, etc) is more than welcome.

Special Mentions

This project is made possible by Shapeless. Special thanks to Miles Sabin