change query: edge centric to vertec centric

Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/a5afdf20
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/a5afdf20
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/a5afdf20

Branch: refs/heads/master
Commit: a5afdf20dae62c272837c1d84a910d5e0fdc393f
Parents: 07d816d
Author: daewon <[email protected]>
Authored: Fri Mar 23 18:35:21 2018 +0900
Committer: daewon <[email protected]>
Committed: Fri Mar 23 18:35:21 2018 +0900

----------------------------------------------------------------------
 .../s2graph/graphql/marshaller/package.scala    |  82 ++---
 .../graphql/repository/GraphRepository.scala    |  43 +--
 .../s2graph/graphql/resolver/Resolver.scala     |   8 +-
 .../apache/s2graph/graphql/types/S2Type.scala   | 256 +++++++++------
 .../apache/s2graph/graphql/types/package.scala  |  15 +-
 .../apache/s2graph/graphql/ScenarioTest.scala   | 328 ++++++++++++-------
 6 files changed, 410 insertions(+), 322 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala
index 97a8097..ac73e14 100644
--- 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala
+++ 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala
@@ -3,35 +3,53 @@ package org.apache.s2graph.graphql
 import org.apache.s2graph.core.Management.JsonModel._
 import org.apache.s2graph.graphql.types.S2Type._
 import sangria.marshalling._
+import sangria.schema.Args
 
 package object marshaller {
-  def unwrapOption(map: Map[String, Any]): Map[String, Any] = map.map {
-    case (k, v: Some[_]) => v.get match {
-      case m: Map[_, _] => k -> unwrapOption(m.asInstanceOf[Map[String, Any]])
-      case _ => k -> v.get
-    }
-    case (k, v: Map[_, _]) => k -> unwrapOption(v.asInstanceOf[Map[String, 
Any]])
-    case org@_ => org
+  type RawNode = Map[String, Any]
+
+  def unwrap(any: Any): Any = any match {
+    case s: Some[_] => unwrap(s.get)
+    case v: Seq[_] => v.map(unwrap)
+    case m: Map[_, _] => m.mapValues(unwrap)
+    case _ => any
   }
 
-  implicit object AddVertexParamFromInput extends 
FromInput[Vector[AddVertexParam]] {
+  implicit object AddVertexParamFromInput extends 
FromInput[List[AddVertexParam]] {
     val marshaller = CoercedScalaResultMarshaller.default
 
     def fromResult(node: marshaller.Node) = {
-      val currentTime = System.currentTimeMillis()
-      val inenrMap = unwrapOption(node.asInstanceOf[Map[String, Any]])
+      val now = System.currentTimeMillis()
+      val map = unwrap(node).asInstanceOf[RawNode]
 
-      val params = inenrMap.flatMap { case (serviceName, serviceColumnMap) =>
-        serviceColumnMap.asInstanceOf[Map[String, Any]].map  { case 
(columnName, vertexParamMap) =>
-          val propMap = vertexParamMap.asInstanceOf[Map[String, Any]]
-          val id = propMap("id")
-          val ts = propMap.getOrElse("timestamp", 
currentTime).asInstanceOf[Long]
+      val params = map.flatMap { case (columnName, vls: Vector[_]) =>
+        vls.map { _m =>
+          val m = _m.asInstanceOf[RawNode]
+          val id = m("id")
+          val ts = m.getOrElse("timestamp", now).asInstanceOf[Long]
 
-          AddVertexParam(ts, id, serviceName, columnName, propMap.filterKeys(k 
=> k != "timestamp" || k != "id"))
+          AddVertexParam(ts, id, columnName, props = m)
         }
       }
 
-      params.toVector
+      params.toList
+    }
+  }
+
+  implicit object AddEdgeParamFromInput extends FromInput[AddEdgeParam] {
+    val marshaller = CoercedScalaResultMarshaller.default
+
+    def fromResult(node: marshaller.Node) = {
+      val inputMap = unwrap(node).asInstanceOf[RawNode]
+      val now = System.currentTimeMillis()
+
+      val from = inputMap("from")
+      val to = inputMap("to")
+      val ts = 
inputMap.get("timestamp").map(_.asInstanceOf[Long]).getOrElse(now)
+      val dir = 
inputMap.get("direction").map(_.asInstanceOf[String]).getOrElse("out")
+      val props = inputMap
+
+      AddEdgeParam(ts, from, to, dir, props)
     }
   }
 
@@ -69,7 +87,7 @@ package object marshaller {
     val marshaller = CoercedScalaResultMarshaller.default
 
     def fromResult(node: marshaller.Node) = {
-      val input = unwrapOption(node.asInstanceOf[Map[String, Any]])
+      val input = unwrap(node.asInstanceOf[Map[String, 
Any]]).asInstanceOf[Map[String, Any]]
 
       val partialServiceColumns = input.map { case (serviceName, 
serviceColumnMap) =>
         val innerMap = serviceColumnMap.asInstanceOf[Map[String, Any]]
@@ -85,32 +103,4 @@ package object marshaller {
     }
   }
 
-  implicit object AddEdgeParamFromInput extends FromInput[AddEdgeParam] {
-    val marshaller = CoercedScalaResultMarshaller.default
-
-    def fromResult(node: marshaller.Node) = {
-      val inputMap = node.asInstanceOf[Map[String, Any]]
-
-      val from = inputMap("from")
-      val to = inputMap("to")
-
-      val ts = inputMap.get("timestamp") match {
-        case Some(Some(v)) => v.asInstanceOf[Long]
-        case _ => System.currentTimeMillis()
-      }
-
-      val dir = inputMap.get("direction") match {
-        case Some(Some(v)) => v.asInstanceOf[String]
-        case _ => "out"
-      }
-
-      val props = inputMap.get("props") match {
-        case Some(Some(v)) => v.asInstanceOf[Map[String, 
Option[Any]]].filter(_._2.isDefined).mapValues(_.get)
-        case _ => Map.empty[String, Any]
-      }
-
-      AddEdgeParam(ts, from, to, dir, props)
-    }
-  }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala
 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala
index 4c802c3..ac6b167 100644
--- 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala
+++ 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala
@@ -25,7 +25,6 @@ import org.apache.s2graph.core.mysqls._
 import org.apache.s2graph.core.rest.RequestParser
 import org.apache.s2graph.core.storage.MutateResponse
 import org.apache.s2graph.core.types._
-import org.apache.s2graph.graphql.types.S2ManagementType.PropWithColumn
 import org.apache.s2graph.graphql.types.S2Type._
 import sangria.schema._
 
@@ -46,7 +45,7 @@ class GraphRepository(val graph: S2GraphLike) {
 
   implicit val ec = graph.ec
 
-  def AddEdgeParamToS2Edge(labelName: String, param: AddEdgeParam): S2EdgeLike 
= {
+  def toS2EdgeLike(labelName: String, param: AddEdgeParam): S2EdgeLike = {
     graph.toEdge(
       srcId = param.from,
       tgtId = param.to,
@@ -56,47 +55,27 @@ class GraphRepository(val graph: S2GraphLike) {
     )
   }
 
-  def toVertex(vid: Any, column: ServiceColumn): S2VertexLike = {
+  def toS2VertexLike(vid: Any, column: ServiceColumn): S2VertexLike = {
     graph.toVertex(column.service.serviceName, column.columnName, vid)
   }
 
-  def parseAddVertexParam(args: Args): Seq[S2VertexLike] = {
-    val vertexParams = args.arg[Vector[Vector[AddVertexParam]]]("vertex")
-
-    vertexParams.flatMap { params =>
-      params.map { param =>
-        graph.toVertex(
-          serviceName = param.serviceName,
-          columnName = param.columnName,
-          id = param.id,
-          props = param.props,
-          ts = param.timestamp)
-      }
-    }
+  def toS2VertexLike(serviceName: String, param: AddVertexParam): S2VertexLike 
= {
+    graph.toVertex(
+      serviceName = serviceName,
+      columnName = param.columnName,
+      id = param.id,
+      props = param.props,
+      ts = param.timestamp)
   }
 
-  def addVertex(vertices: Seq[S2VertexLike]): Future[Seq[MutateResponse]] = {
+  def addVertices(vertices: Seq[S2VertexLike]): Future[Seq[MutateResponse]] = {
     graph.mutateVertices(vertices, withWait = true)
   }
 
-  def addEdges(args: Args): Future[Seq[MutateResponse]] = {
-    val edges: Seq[S2EdgeLike] = args.raw.keys.toList.flatMap { labelName =>
-      val params = args.arg[Vector[AddEdgeParam]](labelName)
-      params.map(param => AddEdgeParamToS2Edge(labelName, param))
-    }
-
+  def addEdges(edges: Seq[S2EdgeLike]): Future[Seq[MutateResponse]] = {
     graph.mutateEdges(edges, withWait = true)
   }
 
-  def addEdge(args: Args): Future[Option[MutateResponse]] = {
-    val edges: Seq[S2EdgeLike] = args.raw.keys.toList.map { labelName =>
-      val param = args.arg[AddEdgeParam](labelName)
-      AddEdgeParamToS2Edge(labelName, param)
-    }
-
-    graph.mutateEdges(edges, withWait = true).map(_.headOption)
-  }
-
   def getVertices(vertex: Seq[S2VertexLike]): Future[Seq[S2VertexLike]] = {
     graph.getVertices(vertex)
   }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala
index 0715793..3310276 100644
--- 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala
+++ 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala
@@ -1,5 +1,9 @@
-package org.apache.s2graph.graphql.resolvers
+package org.apache.s2graph.graphql.resolver
 
-object resolver {
+import org.apache.s2graph.core.S2VertexLike
+import org.apache.s2graph.graphql.repository.GraphRepository
 
+object Resolver {
+  def vertexResolver(v: S2VertexLike)(implicit repo: GraphRepository) {
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala
index 0e7b9ba..59506d4 100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala
@@ -22,23 +22,16 @@ package org.apache.s2graph.graphql.types
 import org.apache.s2graph.core.Management.JsonModel.{Index, Prop}
 import org.apache.s2graph.core._
 import org.apache.s2graph.core.mysqls._
-import org.apache.s2graph.core.storage.MutateResponse
-import org.apache.s2graph.graphql._
 import org.apache.s2graph.graphql.repository.GraphRepository
-import org.apache.s2graph.graphql.types.S2ManagementType._
-import play.api.libs.json.JsValue
-import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput}
 import sangria.schema._
 
 import scala.language.existentials
-import scala.util.{Failure, Success, Try}
 import org.apache.s2graph.graphql.marshaller._
 
 object S2Type {
 
   case class AddVertexParam(timestamp: Long,
                             id: Any,
-                            serviceName: String,
                             columnName: String,
                             props: Map[String, Any])
 
@@ -46,9 +39,10 @@ object S2Type {
                           from: Any,
                           to: Any,
                           direction: String,
-                          props: Map[String, Any])
+                          props: Map[String, Any]) {
+  }
 
-  // management params
+  // Management params
   case class ServiceColumnParam(serviceName: String,
                                 columnName: String,
                                 props: Seq[Prop] = Nil)
@@ -56,66 +50,78 @@ object S2Type {
 
   val DirArg = Argument("direction", OptionInputType(DirectionType), "desc 
here", defaultValue = "out")
 
-  def makePropFields(fieldNameWithTypes: List[(String, String)]): 
List[Field[GraphRepository, Any]] = {
-    def makeField[A](name: String, cType: String, tpe: ScalarType[A]): 
Field[GraphRepository, Any] =
-      Field(name,
-        OptionType(tpe),
-        description = Option("desc here"),
-        resolve = _.value match {
-          case v: S2VertexLike =>
-            name match {
-              case "timestamp" => v.ts.asInstanceOf[A]
-              case _ =>
-                val innerVal = v.propertyValue(name).get
-                JSONParser.innerValToAny(innerVal, cType).asInstanceOf[A]
-            }
-
-          case e: S2EdgeLike =>
-            name match {
-              case "timestamp" => e.ts.asInstanceOf[A]
-              case _ =>
-                val innerVal = e.propertyValue(name).get.innerVal
-                JSONParser.innerValToAny(innerVal, cType).asInstanceOf[A]
-            }
-
-          case _ => throw new RuntimeException("Error !!!!")
-        })
-
-    fieldNameWithTypes.map { case (cName, cType) =>
-      cType match {
-        case "boolean" | "bool" => makeField[Boolean](cName, cType, 
BooleanType)
-        case "string" | "str" | "s" => makeField[String](cName, cType, 
StringType)
-        case "int" | "integer" | "i" | "int32" | "integer32" => 
makeField[Int](cName, cType, IntType)
-        case "long" | "l" | "int64" | "integer64" => makeField[Long](cName, 
cType, LongType)
-        case "double" | "d" | "float64" | "float" | "f" | "float32" => 
makeField[Double](cName, cType, FloatType)
-        case _ => throw new RuntimeException(s"Cannot support data type: 
${cType}")
+  def makeField[A](name: String, cType: String, tpe: ScalarType[A]): 
Field[GraphRepository, Any] =
+    Field(name,
+      OptionType(tpe),
+      description = Option("desc here"),
+      resolve = c => c.value match {
+        case v: S2VertexLike => name match {
+          case "timestamp" => v.ts.asInstanceOf[A]
+          case _ =>
+            val innerVal = v.propertyValue(name).get
+            JSONParser.innerValToAny(innerVal, cType).asInstanceOf[A]
+        }
+        case e: S2EdgeLike => name match {
+          case "timestamp" => e.ts.asInstanceOf[A]
+          case "direction" => e.getDirection().asInstanceOf[A]
+          case _ =>
+            val innerVal = e.propertyValue(name).get.innerVal
+            JSONParser.innerValToAny(innerVal, cType).asInstanceOf[A]
+        }
+        case _ =>
+          throw new RuntimeException(s"Error on resolving field: ${name}, 
${cType}, ${c.value.getClass}")
       }
-    }
+    )
+
+  def makePropField(cName: String, cType: String): Field[GraphRepository, Any] 
= cType match {
+    case "boolean" | "bool" => makeField[Boolean](cName, cType, BooleanType)
+    case "string" | "str" | "s" => makeField[String](cName, cType, StringType)
+    case "int" | "integer" | "i" | "int32" | "integer32" => 
makeField[Int](cName, cType, IntType)
+    case "long" | "l" | "int64" | "integer64" => makeField[Long](cName, cType, 
LongType)
+    case "double" | "d" => makeField[Double](cName, cType, FloatType)
+    case "float64" | "float" | "f" | "float32" => makeField[Double](cName, 
"double", FloatType)
+    case _ => throw new RuntimeException(s"Cannot support data type: ${cType}")
   }
 
-  def makeInputPartialEdgeParamType(label: Label): 
InputObjectType[AddEdgeParam] = {
-    lazy val InputPropsType = InputObjectType[Map[String, ScalarType[_]]](
-      s"Input_${label.label}_edge_props",
-      description = "desc here",
-      () => label.labelMetaSet.toList.map { lm =>
+  def makeInputFieldsOnService(service: Service): Seq[InputField[Any]] = {
+    val inputFields = service.serviceColumns(false).map { serviceColumn =>
+      val idField = InputField("id", 
s2TypeToScalarType(serviceColumn.columnType))
+      val propFields = 
serviceColumn.metasWithoutCache.filter(ColumnMeta.isValid).map { lm =>
         InputField(lm.name, OptionInputType(s2TypeToScalarType(lm.dataType)))
       }
-    )
 
-    lazy val labelFields = List(
+      val vertexMutateType = InputObjectType[Map[String, Any]](
+        
s"Input_${service.serviceName}_${serviceColumn.columnName}_vertex_mutate",
+        description = "desc here",
+        () => idField :: propFields
+      )
+
+      InputField[Any](serviceColumn.columnName, 
OptionInputType(ListInputType(vertexMutateType)))
+    }
+
+    inputFields
+  }
+
+  def makeInputFieldsOnLabel(label: Label): Seq[InputField[Any]] = {
+    val propFields = label.labelMetaSet.toList.map { lm =>
+      InputField(lm.name, OptionInputType(s2TypeToScalarType(lm.dataType)))
+    }
+
+    val labelFields = List(
       InputField("timestamp", OptionInputType(LongType)),
       InputField("from", s2TypeToScalarType(label.srcColumnType)),
       InputField("to", s2TypeToScalarType(label.srcColumnType)),
       InputField("direction", OptionInputType(DirectionType))
     )
 
-    InputObjectType[AddEdgeParam](
-      s"Input_${label.label}_edge_mutate",
-      description = "desc here",
-      () =>
-        if (label.labelMetaSet.isEmpty) labelFields
-        else labelFields ++ Seq(InputField("props", 
OptionInputType(InputPropsType)))
-    )
+    labelFields.asInstanceOf[Seq[InputField[Any]]] ++ 
propFields.asInstanceOf[Seq[InputField[Any]]]
+  }
+
+  def makeServiceColumnFields(column: ServiceColumn): 
List[Field[GraphRepository, Any]] = {
+    val reservedFields = List("id" -> column.columnType, "timestamp" -> "long")
+    val columnMetasKv = 
column.metasWithoutCache.filter(ColumnMeta.isValid).map { columnMeta => 
columnMeta.name -> columnMeta.dataType }
+
+    (reservedFields ++ columnMetasKv).map { case (k, v) => makePropField(k, v) 
}
   }
 
   def makeServiceField(service: Service, allLabels: List[Label])(implicit 
repo: GraphRepository): List[Field[GraphRepository, Any]] = {
@@ -124,19 +130,18 @@ object S2Type {
         column.id.get == lb.srcColumn.id.get || column.id.get == 
lb.tgtColumn.id.get
       }.distinct
 
-      lazy val connectedLabelFields: List[Field[GraphRepository, Any]] = 
connectedLabels.map(makeLabelField(_, connectedLabelFields))
-      val columnMetasKv = 
column.metasWithoutCache.filter(ColumnMeta.isValid).map { columnMeta => 
columnMeta.name -> columnMeta.dataType }
-      val reservedFields = List("id" -> column.columnType, "timestamp" -> 
"long")
+      lazy val vertexPropFields = makeServiceColumnFields(column)
 
-      val vertexPropFields = makePropFields(reservedFields ++ columnMetasKv)
+      lazy val connectedLabelFields: List[Field[GraphRepository, Any]] =
+        connectedLabels.map(makeLabelField(_, connectedLabelFields))
 
-      lazy val ConnectedLabelType = ObjectType(
+      lazy val connectedLabelType = ObjectType(
         s"Input_${service.serviceName}_${column.columnName}",
         () => fields[GraphRepository, Any](vertexPropFields ++ 
connectedLabelFields: _*)
       )
 
       Field(column.columnName,
-        ListType(ConnectedLabelType),
+        ListType(connectedLabelType),
         arguments = List(
           Argument("id", 
OptionInputType(s2TypeToScalarType(column.columnType))),
           Argument("ids", 
OptionInputType(ListInputType(s2TypeToScalarType(column.columnType)))),
@@ -145,13 +150,14 @@ object S2Type {
         description = Option("desc here"),
         resolve = c => {
           val ids = c.argOpt[Any]("id").toSeq ++ 
c.argOpt[List[Any]]("ids").toList.flatten
-          val vertices = ids.map(vid => c.ctx.toVertex(vid, column))
+          val vertices = ids.map(vid => c.ctx.toS2VertexLike(vid, column))
 
           val selectedFields = c.astFields.flatMap { f =>
             f.selections.map(s => s.asInstanceOf[sangria.ast.Field].name)
           }
 
-          if (selectedFields.forall(_ == "id")) vertices else 
repo.getVertices(vertices) // fill props
+          if (selectedFields.forall(_ == "id")) 
scala.concurrent.Future.successful(vertices)
+          else repo.getVertices(vertices) // fill props
         }
       ): Field[GraphRepository, Any]
     }
@@ -159,30 +165,61 @@ object S2Type {
     columnsOnService
   }
 
+  def fillPartialVertex(vertex: S2VertexLike,
+                        column: ServiceColumn,
+                        c: Context[GraphRepository, Any]): 
scala.concurrent.Future[S2VertexLike] = {
+    implicit val ec = c.ctx.ec
+
+    val selectedFields = c.astFields.flatMap { f =>
+      f.selections.map(s => s.asInstanceOf[sangria.ast.Field].name)
+    }
+
+    lazy val newVertex = c.ctx.toS2VertexLike(vertex.innerId, column)
+
+    if (selectedFields.forall(_ == "id")) 
scala.concurrent.Future.successful(vertex)
+    else c.ctx.getVertices(Seq(newVertex)).map(_.head) // fill props
+  }
+
   def makeLabelField(label: Label, connectedLabelFields: => 
List[Field[GraphRepository, Any]]): Field[GraphRepository, Any] = {
-    val labelColumns = List("from" -> label.srcColumnType, "to" -> 
label.tgtColumnType, "timestamp" -> "long")
+    val labelColumns = List("direction" -> "string", "timestamp" -> "long")
     val labelProps = label.labelMetas.map { lm => lm.name -> lm.dataType }
 
+    lazy val edgeFields: List[Field[GraphRepository, Any]] =
+      (labelColumns ++ labelProps).map { case (k, v) => makePropField(k, v) }
+
+    lazy val fromType = ObjectType(s"${label.label}_from", () =>
+      makeServiceColumnFields(label.srcColumn) ++ connectedLabelFields
+    )
+
+    lazy val toType = ObjectType(s"${label.label}_to", () =>
+      makeServiceColumnFields(label.tgtColumn) ++ connectedLabelFields
+    )
+    lazy val fromField: Field[GraphRepository, Any] = Field("from", fromType, 
resolve = c => {
+      val vertex = c.value.asInstanceOf[S2EdgeLike].srcVertex
+      fillPartialVertex(vertex, label.srcColumn, c)
+    })
+
+    lazy val toField: Field[GraphRepository, Any] = Field("to", toType, 
resolve = c => {
+      val vertex = c.value.asInstanceOf[S2EdgeLike].tgtVertex
+      fillPartialVertex(vertex, label.tgtColumn, c)
+    })
+
     lazy val EdgeType = ObjectType(
       s"Label_${label.label}",
-      () => fields[GraphRepository, Any](edgeFields ++ connectedLabelFields: 
_*)
+      () => fields[GraphRepository, Any](List(fromField, toField) ++ 
edgeFields: _*)
     )
 
-    lazy val edgeFields: List[Field[GraphRepository, Any]] = 
makePropFields(labelColumns ++ labelProps)
     lazy val edgeTypeField: Field[GraphRepository, Any] = Field(
       s"${label.label}",
       ListType(EdgeType),
       arguments = DirArg :: Nil,
-      description = Some("edges"),
+      description = Some("fetch edges"),
       resolve = { c =>
         val dir = c.argOpt("direction").getOrElse("out")
 
         val vertex: S2VertexLike = c.value match {
           case v: S2VertexLike => v
-          case e: S2Edge => if (dir == "out") e.tgtVertex else e.srcVertex
-          //          case vp: ServiceParam =>
-          //            if (dir == "out") c.ctx.toVertex(label.tgtColumn, vp)
-          //            else c.ctx.toVertex(label.srcColumn, vp)
+          case _ => throw new IllegalArgumentException(s"ERROR: 
${c.value.getClass}")
         }
 
         c.ctx.getEdges(vertex, label, dir)
@@ -204,7 +241,7 @@ class S2Type(repo: GraphRepository) {
     * fields
     */
   lazy val serviceFields: List[Field[GraphRepository, Any]] = 
repo.allServices.map { service =>
-    lazy val serviceFields = paddingDummyField(makeServiceField(service, 
repo.allLabels()))
+    lazy val serviceFields = DummyObjectTypeField :: makeServiceField(service, 
repo.allLabels())
     lazy val ServiceType = ObjectType(
       s"Service_${service.serviceName}",
       fields[GraphRepository, Any](serviceFields: _*)
@@ -222,40 +259,30 @@ class S2Type(repo: GraphRepository) {
     * arguments
     */
   lazy val addVertexArg = {
-    val fields = repo.allServices.map { service =>
-      val inputFields = service.serviceColumns(false).map { serviceColumn =>
-        val idField = InputField("id", 
s2TypeToScalarType(serviceColumn.columnType))
-        val propFields = 
serviceColumn.metasWithoutCache.filter(ColumnMeta.isValid).map { lm =>
-          InputField(lm.name, OptionInputType(s2TypeToScalarType(lm.dataType)))
-        }
-
-        val vertexMutateType = InputObjectType[Map[String, Any]](
-          
s"Input_${service.serviceName}_${serviceColumn.columnName}_vertex_mutate",
-          description = "desc here",
-          () => idField :: propFields
-        )
-
-        InputField[Any](serviceColumn.columnName, vertexMutateType)
-      }
-
-      val tpe = InputObjectType[Any](
-        s"${service.serviceName}_param", fields = DummyInputField :: 
inputFields.toList
+    val serviceArguments = repo.allServices().map { service =>
+      val serviceFields = DummyInputField +: makeInputFieldsOnService(service)
+      val serviceInputType = InputObjectType[List[AddVertexParam]](
+        s"Input_vertex_${service.serviceName}_param",
+        () => serviceFields.toList
       )
-
-      InputField(service.serviceName, OptionInputType(tpe))
+      Argument(service.serviceName, OptionInputType(serviceInputType))
     }
 
-    InputObjectType[Vector[AddVertexParam]]("vertex_input", fields = 
DummyInputField :: fields)
+    serviceArguments
   }
 
-  lazy val addEdgeArg = repo.allLabels().map { label =>
-    val inputPartialEdgeParamType = makeInputPartialEdgeParamType(label)
-    Argument(label.label, OptionInputType(inputPartialEdgeParamType))
-  }
+  lazy val addEdgeArg = {
+    val labelArguments = repo.allLabels().map { label =>
+      val labelFields = DummyInputField +: makeInputFieldsOnLabel(label)
+      val labelInputType = InputObjectType[AddEdgeParam](
+        s"Input_label_${label.label}_param",
+        () => labelFields.toList
+      )
+
+      Argument(label.label, OptionInputType(ListInputType(labelInputType)))
+    }
 
-  lazy val addEdgesArg = repo.allLabels().map { label =>
-    val inputPartialEdgeParamType = makeInputPartialEdgeParamType(label)
-    Argument(label.label, 
OptionInputType(ListInputType(inputPartialEdgeParamType)))
+    labelArguments
   }
 
   /**
@@ -268,16 +295,31 @@ class S2Type(repo: GraphRepository) {
   lazy val mutationFields: List[Field[GraphRepository, Any]] = List(
     Field("addVertex",
       ListType(MutateResponseType),
-      arguments = Argument("vertex", ListInputType(addVertexArg)) :: Nil,
+      arguments = addVertexArg,
       resolve = c => {
-        val vertices = repo.parseAddVertexParam(c.args)
-        c.ctx.addVertex(vertices)
+        val vertices = c.args.raw.keys.flatMap { serviceName =>
+          val addVertexParams = c.arg[List[AddVertexParam]](serviceName)
+          addVertexParams.map { param =>
+            repo.toS2VertexLike(serviceName, param)
+          }
+        }
+
+        c.ctx.addVertices(vertices.toSeq)
       }
     ),
     Field("addEdge",
-      OptionType(MutateResponseType),
+      ListType(MutateResponseType),
       arguments = addEdgeArg,
-      resolve = c => c.ctx.addEdge(c.args)
+      resolve = c => {
+        val edges = c.args.raw.keys.flatMap { labelName =>
+          val addEdgeParams = c.arg[Vector[AddEdgeParam]](labelName)
+          addEdgeParams.map { param =>
+            repo.toS2EdgeLike(labelName, param)
+          }
+        }
+
+        c.ctx.addEdges(edges.toSeq)
+      }
     )
   )
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala 
b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala
index ec633f7..a6077a8 100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala
@@ -63,7 +63,7 @@ package object types {
       EnumValue("string", value = "string"),
       EnumValue("int", value = "int"),
       EnumValue("long", value = "long"),
-      EnumValue("float", value = "float"),
+      EnumValue("double", value = "double"),
       EnumValue("boolean", value = "boolean")
     )
   )
@@ -149,17 +149,4 @@ package object types {
     description = Some("dummy field"),
     resolve = _ => None
   )
-
-  def paddingDummyField(fields: List[Field[GraphRepository, Any]]): 
List[Field[GraphRepository, Any]] = {
-    if (fields.nonEmpty) fields else List(DummyObjectTypeField)
-  }
-
-  def trySequence[A >: Throwable](tries: Seq[Try[A]]): Try[Seq[A]] = {
-    Try {
-      tries.collect {
-        case Success(v) => v
-        case Failure(e) => e
-      }
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a5afdf20/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala
----------------------------------------------------------------------
diff --git 
a/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala 
b/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala
index d5cbe3a..c5ea043 100644
--- a/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala
+++ b/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala
@@ -26,16 +26,16 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        mutation {
-          Management {
-            createService(
-              name: "kakao"
-              compressionAlgorithm: gz
-            ) {
-              isSuccess
+          mutation {
+            Management {
+              createService(
+                name: "kakao"
+                compressionAlgorithm: gz
+              ) {
+                isSuccess
+              }
             }
           }
-        }
 
         """
 
@@ -51,7 +51,7 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
                        }
                }
           }
-          """.stripMargin
+          """
         )
 
         actual shouldBe expected
@@ -61,31 +61,31 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        mutation {
-          Management {
-            createServiceColumn (
-              serviceName: kakao
-              columnName: "user"
-              columnType: string
-              props: {
-                name: "age"
-                dataType: int
-                defaultValue: "0"
-                storeInGlobalIndex: true
-              }
-            ) {
-              isSuccess
-              object {
-                name
-                props {
+          mutation {
+            Management {
+              createServiceColumn (
+                serviceName: kakao
+                columnName: "user"
+                columnType: string
+                props: {
+                  name: "age"
+                  dataType: int
+                  defaultValue: "0"
+                  storeInGlobalIndex: true
+                }
+              ) {
+                isSuccess
+                object {
                   name
+                  props {
+                    name
+                  }
                 }
               }
             }
           }
-        }
 
-        """
+          """
 
         val actual = testGraph.queryAsJs(query)
         val expected = Json.parse(
@@ -114,26 +114,26 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        mutation {
-          Management {
-            addPropsToServiceColumn(
-              service: {
-                kakao: {
-                  columnName: user
-                  props: {
-                    name: "gender"
-                    dataType: string
-                    defaultValue: ""
-                    storeInGlobalIndex: true
+          mutation {
+            Management {
+              addPropsToServiceColumn(
+                service: {
+                  kakao: {
+                    columnName: user
+                    props: {
+                      name: "gender"
+                      dataType: string
+                      defaultValue: ""
+                      storeInGlobalIndex: true
+                    }
                   }
                 }
+              ) {
+                isSuccess
               }
-            ) {
-              isSuccess
             }
           }
-        }
-        """
+          """
 
         val actual = testGraph.queryAsJs(query)
         val expected = Json.parse(
@@ -157,22 +157,22 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        query {
-          Management {
-            Service(name: kakao) {
-              name
-              serviceColumns {
+          query {
+            Management {
+              Service(name: kakao) {
                 name
-                props {
+                serviceColumns {
                   name
-                  dataType
+                  props {
+                    name
+                    dataType
+                  }
                 }
               }
             }
           }
-        }
 
-        """
+          """
 
         val actual = testGraph.queryAsJs(query)
         val expected = Json.parse(
@@ -204,25 +204,25 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        mutation {
-          Management {
-            createLabel(
-              name: "friends"
-              sourceService: {
-                kakao: {
-                  columnName: user
+          mutation {
+            Management {
+              createLabel(
+                name: "friends"
+                sourceService: {
+                  kakao: {
+                    columnName: user
+                  }
                 }
-              }
-              targetService: {
-                kakao: {
-                  columnName: user
+                targetService: {
+                  kakao: {
+                    columnName: user
+                  }
                 }
+              ) {
+                isSuccess
               }
-            ) {
-              isSuccess
             }
           }
-        }
 
         """
 
@@ -253,7 +253,7 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
                 labelName: friends
                 props: {
                   name: "score"
-                  dataType: float
+                  dataType: double
                   defaultValue: "0"
                   storeInGlobalIndex: true
                }
@@ -285,37 +285,37 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val query =
           graphql"""
 
-        query {
-          Management {
-            Label(name: friends) {
-              name
-              props {
+          query {
+            Management {
+              Label(name: friends) {
                 name
-                dataType
+                props {
+                  name
+                  dataType
+                }
               }
             }
           }
-        }
 
-        """
+          """
 
         val actual = testGraph.queryAsJs(query)
         val expected = Json.parse(
           """
-         {
-               "data": {
-                       "Management": {
-                               "Label": {
-                                       "name": "friends",
-                                       "props": [{
-                                               "name": "score",
-                                               "dataType": "float"
-                                       }]
-                               }
-                       }
-               }
-         }
-         """)
+          {
+               "data": {
+                       "Management": {
+                               "Label": {
+                                       "name": "friends",
+                                       "props": [{
+                                               "name": "score",
+                                               "dataType": "double"
+                                       }]
+                               }
+                       }
+               }
+          }
+          """)
 
         actual shouldBe expected
       }
@@ -328,25 +328,18 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
 
           mutation {
             addVertex(
-              vertex: [{
-                kakao: {
-                  user: {
-                    id: "daewon"
-                    age: 20
-                    gender: "M"
-                  }
-                }
-              },
-              {
-                kakao: {
-                  user: {
-                    id: "shon"
-                    age: 19
-                    gender: "F"
-                  }
-                }
-              }]
-            ) {
+              kakao: {
+                user: [{
+                  id: "daewon"
+                  age: 20
+                  gender: "M"
+                },
+                {
+                  id: "shon"
+                  age: 19
+                  gender: "F"
+                }]
+              }) {
              isSuccess
            }
           }
@@ -387,25 +380,118 @@ class ScenarioTest extends FunSpec with Matchers with 
BeforeAndAfterAll {
         val actual = testGraph.queryAsJs(query)
         val expected = Json.parse(
           """
+          {
+               "data": {
+                       "kakao": {
+                               "user": [{
+                                       "id": "daewon",
+                                       "age": 20,
+                  "gender": "M"
+                               }, {
+                                       "id": "shon",
+                                       "age": 19,
+                  "gender": "F"
+                               }]
+                       }
+               }
+          }
+          """)
+
+        actual shouldBe expected
+      }
+    }
+  }
+
+  describe("Add edge to label 'friends' and fetch ") {
+    it("should add edges: daewon -> shon(score: 2) to friends") {
+      val query =
+        graphql"""
+
+        mutation {
+          addEdge(
+            friends: {
+              from: "daewon"
+              to: "shon"
+              score: 0.9
+            }
+          ) {
+           isSuccess
+         }
+        }
+        """
+
+      val actual = testGraph.queryAsJs(query)
+
+      val expected = Json.parse(
+        """
         {
                "data": {
-                       "kakao": {
-                               "user": [{
-                                       "id": "daewon",
-                                       "age": 20,
-                "gender": "M"
-                               }, {
-                                       "id": "shon",
-                                       "age": 19,
-                "gender": "F"
-                               }]
-                       }
+                       "addEdge": [{
+                               "isSuccess": true
+                       }]
                }
         }
         """)
 
-        actual shouldBe expected
-      }
+      actual shouldBe expected
+    }
+
+    it("should fetch edges: friends of kakao.user(daewon) ") {
+      val query =
+        graphql"""
+
+        query {
+          kakao {
+            user(id: "daewon") {
+              id
+              friends {
+                score
+                to {
+                  id
+                  age
+                  friends(direction: in) {
+                    to {
+                      id
+                      age
+                    }
+                    direction
+                  }
+                }
+              }
+            }
+          }
+        }
+        """
+
+      val actual = testGraph.queryAsJs(query)
+      val expected = Json.parse(
+        """
+        {
+          "data" : {
+            "kakao" : {
+              "user" : [ {
+                "id" : "daewon",
+                "friends" : [ {
+                  "score" : 0.9,
+                  "to" : {
+                    "id" : "shon",
+                    "age" : 19,
+                    "friends" : [ {
+                      "to" : {
+                        "id" : "daewon",
+                        "age" : 20
+                      },
+                      "direction" : "in"
+                    } ]
+                  }
+                } ]
+              } ]
+            }
+          }
+        }
+        """)
+
+      actual shouldBe expected
     }
   }
 }


Reply via email to