Skip to content

Commit

Permalink
adds DSLResult abstraction to provide query and parameter map
Browse files Browse the repository at this point in the history
  • Loading branch information
manishkkatoch committed Jun 24, 2019
1 parent f1159f8 commit bd30ffb
Show file tree
Hide file tree
Showing 35 changed files with 910 additions and 442 deletions.
3 changes: 0 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ gradle.taskGraph.whenReady { taskGraph ->

allprojects {
ext."signing.password" = System.getenv('GPG_PASSPHRASE')
printf("%s", ext."signing.password")
printf("%s", ext."signing.keyId")

}
}
if (taskGraph.allTasks.any { it.name == 'build' || it.name == 'assemble' }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.manishkatoch.scala.cypherDSL.spec

case class DSLResult(query: String, queryMap: Map[String,Any])

object DSLResult {
def apply(query: String):DSLResult = DSLResult(query, Map.empty)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import shapeless.{HList, HNil}
import scala.util.Try

private[cypherDSL] class Path(val pathLinks: PathLink*) {
def toQuery(context: Context = new Context()): String =
pathLinks.map(_.toQuery(context)).mkString
def toQuery(context: Context = new Context()): DSLResult = {
val (queryList, paramMap) =
pathLinks.map(_.toQuery(context)).foldLeft((List.empty[String], Map.empty[String, Any])) { (acc, result) =>
(acc._1 :+ result.query, acc._2 ++ result.queryMap)
}
DSLResult(queryList.mkString, paramMap)
}

def |[T <: Product, TH <: HList, U <: Product, UH <: HList](rel: U)(
implicit queryProvider: QueryProvider[U]): Path = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package me.manishkatoch.scala.cypherDSL.spec
import me.manishkatoch.scala.cypherDSL.spec.entities.CypherEntity

private[cypherDSL] case class PathLink(leftLink: Option[String], element: CypherEntity, rightLink: Option[String]) {
def toQuery(context: Context = new Context()): String =
leftLink.map(_.toString).mkString + element.toQuery(context) + rightLink.map(_.toString).mkString
def toQuery(context: Context = new Context()): DSLResult = {
val entityResult = element.toQuery(context)
entityResult.copy(
query = leftLink.map(_.toString).mkString + entityResult.query + rightLink.map(_.toString).mkString)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package me.manishkatoch.scala.cypherDSL.spec

import shapeless.ops.hlist.ToTraversable
import shapeless.ops.record.Keys
import shapeless.{HList, LabelledGeneric}

trait QueryProvider[T <: Product] {
def getMatchers(element: T)(implicit context: Context): Seq[String]
def getMatchers[U <: HList](element: T, columns: U)(implicit context: Context,
i0: ToTraversable.Aux[U, List, Symbol]): Seq[String]
def getMatchers(element: T)(implicit context: Context): Seq[DSLResult]
def getMatchers[U <: HList](element: T, columns: U)(implicit context: Context): Seq[DSLResult]
}

object QueryProvider {
Expand All @@ -17,20 +15,29 @@ object QueryProvider {
implicit def makeQueryProvider[T <: Product, H <: HList, K <: HList](
implicit
lGen: LabelledGeneric.Aux[T, H],
keys: Keys.Aux[H, K],
i0: ToTraversable.Aux[K, List, Symbol]): QueryProvider[T] =
new QueryProvider[T] {
keys: Keys.Aux[H, K]): QueryProvider[T] = new QueryProvider[T] {

override def getMatchers(element: T)(implicit context: Context): Seq[String] = {
override def getMatchers(element: T)(implicit context: Context): Seq[DSLResult] = {
val id = context.get(element).get
keys().toList.map(sym => Utils.matchPropertyPattern(id, sym.name))
val record = Utils.toList(keys()) zip Utils.toAnyList(lGen.to(element))
recordToDSLResults(id, record)
}

override def getMatchers[U <: HList](element: T, columns: U)(
implicit context: Context,
i0: ToTraversable.Aux[U, List, Symbol]): Seq[String] = {
override def getMatchers[U <: HList](element: T, columns: U)(implicit context: Context): Seq[DSLResult] = {
val id = context.get(element).get
columns.toList[Symbol].map(sym => Utils.matchPropertyPattern(id, sym.name))
val record = Utils.toList(keys()) zip Utils.toAnyList(lGen.to(element))
val columnList = Utils.toList(columns)
val filteredRecord = record.filter(t => columnList.contains(t._1))
recordToDSLResults(id, filteredRecord)
}

private def recordToDSLResults(id: String, record: Seq[(String,Any)]):Seq[DSLResult] =
record.map(tuple => {
val (key, value) = tuple
val queryString = Utils.matchPropertyPattern(id, key)
val propertyPlaceHolder = Utils.propertyValuePlaceHolder(id, key)
DSLResult(queryString, Map(propertyPlaceHolder -> value))
})
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package me.manishkatoch.scala.cypherDSL.spec
import me.manishkatoch.scala.cypherDSL.spec.clauses.Clause

private[cypherDSL] case class Statement(clauses: Seq[Clause]) {
def toQuery(context: Context = new Context()): String = {
clauses.map(clause => clause.toQuery(context)).mkString(System.lineSeparator())
def toQuery(context: Context = new Context()): DSLResult = {
val (queryList, paramMap) = clauses.map(clause => clause.toQuery(context))
.foldLeft((List.empty[String], Map.empty[String,Any])) {(acc, result) =>
(acc._1 :+ result.query, acc._2 ++ result.queryMap)
}
DSLResult(queryList.mkString(System.lineSeparator()), paramMap)
}
}

Expand Down
15 changes: 13 additions & 2 deletions src/main/scala/me/manishkatoch/scala/cypherDSL/spec/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package me.manishkatoch.scala.cypherDSL.spec
import shapeless.{::, HList, HNil}

private object Utils {
def matchPropertyPattern(identifier: String, propertyName: String) = s"$propertyName: {${identifier}_$propertyName}"

def matchPropertyPattern(identifier: String, propertyName: String) =
s"$propertyName: {${propertyValuePlaceHolder(identifier,propertyName)}}"

def propertyValuePlaceHolder(identifier: String, propertyName: String) = s"${identifier}_$propertyName"

def toList[H <: HList](list: H): List[String] = {
def lpToList(list: HList): List[String] = list match {
Expand All @@ -13,5 +17,12 @@ private object Utils {
}
lpToList(list)
}

def toAnyList[H <: HList](list: H): List[Any] = {
def lpToList(list: HList): List[Any] = list match {
case HNil => List.empty
case (s: Symbol) :: tail => s.name +: lpToList(tail)
case s :: tail => s +: lpToList(tail)
}
lpToList(list)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}

private[spec] trait Clause {
def toQuery(context: Context): String
def toQuery(context: Context): DSLResult
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}

private[cypherDSL] class Limits(count: Int) extends Clause {
override def toQuery(context: Context = new Context()): String = s"LIMIT $count"
override def toQuery(context: Context = new Context()): DSLResult = DSLResult(s"LIMIT $count")
}
private[cypherDSL] object Limits {
def apply(count: Int) = new Limits(count)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import shapeless.ops.hlist.ToTraversable
import shapeless.{HList, HNil}

private[spec] class Matches(path: Path) extends Clause {
override def toQuery(context: Context = new Context()): String = s"MATCH ${path.toQuery(context)}"
override def toQuery(context: Context = new Context()): DSLResult = {
val result = path.toQuery(context)
result.copy(query = s"MATCH ${result.query}")
}
}

private[spec] object Matches {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.entities.{Node, NodeType}
import me.manishkatoch.scala.cypherDSL.spec.{Context, Path, PathLink, QueryProvider}
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult, Path, PathLink, QueryProvider}
import shapeless.{::, HList, HNil}
import shapeless.ops.hlist.ToTraversable

private[cypherDSL] class OptionallyMatches(path: Path) extends Clause {
override def toQuery(context: Context = new Context()): String = s"OPTIONAL MATCH ${path.toQuery(context)}"
override def toQuery(context: Context = new Context()): DSLResult = {
val result = path.toQuery(context)
result.copy(query = s"OPTIONAL MATCH ${result.query}")
}
}
private[cypherDSL] object OptionallyMatches {
def apply[T <: Product, TH <: HList](element: Node[T, TH])(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}
import me.manishkatoch.scala.cypherDSL.spec.entities.OrderingProduct
import me.manishkatoch.scala.cypherDSL.spec.utils.ElementPropertyExtractingAndAliasing

Expand All @@ -9,7 +9,7 @@ private[cypherDSL] class OrdersBy(descendingOrder: Boolean, elements: OrderingPr
with ElementPropertyExtractingAndAliasing {
private val errorMessage = "One or more of the elements to be returned are not in Context!"

override def toQuery(context: Context): String = {
override def toQuery(context: Context): DSLResult = {
val ids = elements
.map(element => {
val (el, properties) = getElementAndProperties(element.element)
Expand All @@ -19,7 +19,7 @@ private[cypherDSL] class OrdersBy(descendingOrder: Boolean, elements: OrderingPr
.getOrElse(throw new NoSuchElementException(errorMessage))
})
.mkString(",")
(if (ids.nonEmpty) s"ORDER BY $ids $getOrderingString" else "").trim
DSLResult((if (ids.nonEmpty) s"ORDER BY $ids $getOrderingString" else "").trim)
}

private def makeAliasedReturnString(identifier: String, properties: List[String]): String = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}
import me.manishkatoch.scala.cypherDSL.spec.entities.AliasedProduct
import me.manishkatoch.scala.cypherDSL.spec.operators.Operator
import me.manishkatoch.scala.cypherDSL.spec.utils.ElementPropertyExtractingAndAliasing
Expand All @@ -11,7 +11,7 @@ private[cypherDSL] class Returns(elements: Either[AliasedProduct, Operator]*)
private val errorMessage = "One or more of the elements to be returned are not in Context!"

@throws[NoSuchElementException]
def toQuery(context: Context = new Context()): String = {
def toQuery(context: Context = new Context()): DSLResult = {
val ids = elements
.map(element => {
if (element.isRight) element.right.get.toQuery(context)
Expand All @@ -26,7 +26,7 @@ private[cypherDSL] class Returns(elements: Either[AliasedProduct, Operator]*)
})
.mkString(",")

if (ids.nonEmpty) s"RETURN $ids" else ""
DSLResult(if (ids.nonEmpty) s"RETURN $ids" else "")
}
}
private[cypherDSL] object Returns {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}

private[spec] class Skips(count: Int) extends Clause {
override def toQuery(context: Context = new Context()): String = s"SKIP $count"
override def toQuery(context: Context = new Context()): DSLResult = DSLResult(s"SKIP $count")
}
private[spec] object Skips {
def apply(count: Int) = new Skips(count)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package me.manishkatoch.scala.cypherDSL.spec.clauses

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}
import me.manishkatoch.scala.cypherDSL.spec.entities.AliasedProduct
import me.manishkatoch.scala.cypherDSL.spec.operators.Operator
import me.manishkatoch.scala.cypherDSL.spec.utils.ElementPropertyExtractingAndAliasing
Expand All @@ -11,7 +11,7 @@ private[cypherDSL] class With(elements: Either[AliasedProduct, Operator]*)
private val errorMessage = "One or more of the elements to be returned are not in Context!"

@throws[NoSuchElementException]
def toQuery(context: Context = new Context()): String = {
def toQuery(context: Context = new Context()): DSLResult = {
val ids = elements
.map(element => {
if (element.isRight) element.right.get.toQuery(context)
Expand All @@ -29,7 +29,7 @@ private[cypherDSL] class With(elements: Either[AliasedProduct, Operator]*)
})
.mkString(",")

if (ids.nonEmpty) s"WITH $ids" else ""
DSLResult(if (ids.nonEmpty) s"WITH $ids" else "")
}
}
private[cypherDSL] object With {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.manishkatoch.scala.cypherDSL.spec.entities

import me.manishkatoch.scala.cypherDSL.spec.Context
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult}

private[cypherDSL] trait CypherEntity {
def toQuery(context: Context = new Context()): String
def toQuery(context: Context = new Context()): DSLResult
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.manishkatoch.scala.cypherDSL.spec.entities

import me.manishkatoch.scala.cypherDSL.spec.utils.SnakeCasing
import me.manishkatoch.scala.cypherDSL.spec.{Context, QueryProvider}
import me.manishkatoch.scala.cypherDSL.spec.{Context, DSLResult, QueryProvider}
import shapeless.ops.hlist.ToTraversable
import shapeless.{HList, HNil}

Expand All @@ -11,7 +11,7 @@ private[spec] sealed abstract class CypherInstance[T <: Product: QueryProvider,

private val queryProvider = implicitly[QueryProvider[T]]

def toQuery(context: Context = new Context()): String = {
def toQuery(context: Context = new Context()): DSLResult = {
context.map(element)(getIdentifierOnlyQuery).getOrElse {
val id = context.add(element)
getQueryBasedOnProperties(id, context)
Expand All @@ -27,22 +27,27 @@ private[spec] sealed abstract class CypherInstance[T <: Product: QueryProvider,
makeExpandedQuery(id, matchers)
}

private def makeExpandedQuery(id: String, parts: Seq[String]) = {
val repr = s"$id:$label {${parts.mkString(",")}}"
makeQuery(repr)
private def makeExpandedQuery(id: String, parts: Seq[DSLResult]) = {
val (query, paramMap) = parts.foldLeft((List.empty[String], Map.empty[String, Any])) { (acc, part) =>
(acc._1 :+ part.query, acc._2 ++ part.queryMap)
}
val repr = s"$id:$label {${query.mkString(",")}}"
DSLResult(repr, paramMap)
}

def label: String = element.getClass.getSimpleName

private def getIdentifierOnlyQuery(id: String): String = makeQuery(id)
private def getIdentifierOnlyQuery(id: String): DSLResult = DSLResult(id)

private def makeQuery(repr: String) = s"$repr"
}

private[cypherDSL] case class Node[T <: Product: QueryProvider, H <: HList](element: T, properties: H)(
implicit i0: ToTraversable.Aux[H, List, Symbol])
extends CypherInstance(element, properties) {
override def toQuery(context: Context = new Context()): String = s"(${super.toQuery(context)})"
override def toQuery(context: Context = new Context()): DSLResult = {
val result = super.toQuery(context)
result.copy(query = s"(${result.query})")
}
}

private[cypherDSL] case class Relationship[T <: Product: QueryProvider, H <: HList](
Expand All @@ -54,11 +59,22 @@ private[cypherDSL] case class Relationship[T <: Product: QueryProvider, H <: HLi
extends CypherInstance(element, properties)
with SnakeCasing {

override def toQuery(context: Context = new Context()): String = {
val orRelationStringIfAny =
if (context.get(element).isDefined) "" else orRelations.map(_.toQuery(context)).map(str => s"|:$str").mkString
override def toQuery(context: Context = new Context()): DSLResult = {
val (orRelationString, orRelationMap) = if (context.get(element).isDefined) {
("", Map.empty)
} else {
val orRelationsResults = orRelations.map(_.toQuery(context))
val orRelationStringIfAny = orRelationsResults.map(result => s"|:${result.query}").mkString
val orRelationMapIfAny = orRelationsResults.foldLeft(Map.empty[String, Any]) { (acc, result) =>
acc ++ result.queryMap
}
(orRelationStringIfAny, orRelationMapIfAny)
}

val varLengthStringIfAny = variableLengthRelation.map(_.toQuery(context)).mkString
s"[${super.toQuery(context)}$orRelationStringIfAny$varLengthStringIfAny]"
val result = super.toQuery(context)
result.copy(query = s"[${result.query}$orRelationString$varLengthStringIfAny]",
queryMap = result.queryMap ++ orRelationMap)
}

def or[U <: Product, UH <: HList](rel: U, properties: UH)(
Expand All @@ -77,5 +93,7 @@ private[cypherDSL] case class Relationship[T <: Product: QueryProvider, H <: HLi

private[cypherDSL] case class VariableLengthRelationship(variableLengthRelation: VariableLengthRelation)
extends CypherEntity {
override def toQuery(context: Context): String = s"[${variableLengthRelation.toQuery(context)}]"
override def toQuery(context: Context): DSLResult = {
DSLResult(s"[${variableLengthRelation.toQuery(context)}]")
}
}
Loading

0 comments on commit bd30ffb

Please sign in to comment.