Repository: incubator-s2graph Updated Branches: refs/heads/master 07a5af39a -> bfc063f50
remove inefficiency logic Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/597b8535 Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/597b8535 Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/597b8535 Branch: refs/heads/master Commit: 597b853559ea5b6e79b2ddfd400757bc42654233 Parents: 8ff8c23 Author: daewon <[email protected]> Authored: Tue Jul 10 15:37:40 2018 +0900 Committer: daewon <[email protected]> Committed: Tue Jul 10 15:37:46 2018 +0900 ---------------------------------------------------------------------- .../apache/s2graph/graphql/GraphQLServer.scala | 23 +- .../org/apache/s2graph/graphql/HttpServer.scala | 22 +- .../graphql/repository/GraphRepository.scala | 23 +- .../s2graph/graphql/types/ManagementType.scala | 50 ++-- .../apache/s2graph/graphql/types/S2Type.scala | 238 ++++++++++++------- .../s2graph/graphql/types/SchemaDef.scala | 21 +- 6 files changed, 248 insertions(+), 129 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala index 5f1c225..a4a61ff 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala @@ -45,7 +45,7 @@ import scala.concurrent.{ExecutionContext, Future} import scala.util.control.NonFatal import scala.util.{Failure, Success, Try} -object GraphQLServer { +class GraphQLServer() { val className = Schema.getClass.getName val logger = LoggerFactory.getLogger(this.getClass) @@ -58,6 +58,7 @@ object GraphQLServer { val config = ConfigFactory.load() val s2graph = new S2Graph(config) val schemaCacheTTL = Try(config.getInt("schemaCacheTTL")).getOrElse(-1) + val withAdmin = Try(config.getBoolean("schemaCacheTTL")).getOrElse(false) val s2Repository = new GraphRepository(s2graph) val schemaConfig = ConfigFactory.parseMap(Map( SafeUpdateCache.MaxSizeKey -> 1, SafeUpdateCache.TtlKey -> schemaCacheTTL @@ -77,15 +78,24 @@ object GraphQLServer { ret } + logger.info(s"schemaCacheTTL: ${schemaCacheTTL}") + + val schemaCacheKey = className + "s2Schema" + + schemaCache.put(schemaCacheKey, createNewSchema(withAdmin)) + /** * In development mode(schemaCacheTTL = -1), * a new schema is created for each request. */ - logger.info(s"schemaCacheTTL: ${schemaCacheTTL}") - private def createNewSchema(): Schema[GraphRepository, Any] = { - val newSchema = new SchemaDef(s2Repository).S2GraphSchema - logger.info(s"Schema updated: ${System.currentTimeMillis()}") + private def createNewSchema(withAdmin: Boolean): Schema[GraphRepository, Any] = { + logger.info(s"Schema update start") + + val ts = System.currentTimeMillis() + val newSchema = new SchemaDef(s2Repository, withAdmin).S2GraphSchema + + logger.info(s"Schema updated: ${(System.currentTimeMillis() - ts) / 1000} sec") newSchema } @@ -115,8 +125,7 @@ object GraphQLServer { def executeGraphQLQuery(query: Document, op: Option[String], vars: JsObject)(implicit e: ExecutionContext) = { import GraphRepository._ - val cacheKey = className + "s2Schema" - val s2schema = schemaCache.withCache(cacheKey, broadcast = false, onEvict = onEvictSchema)(createNewSchema()) + val s2schema = schemaCache.withCache(schemaCacheKey, broadcast = false, onEvict = onEvictSchema)(createNewSchema(withAdmin)) val resolver: DeferredResolver[GraphRepository] = DeferredResolver.fetchers(vertexFetcher, edgeFetcher) val includeGrpaph = vars.fields.get("includeGraph").contains(spray.json.JsBoolean(true)) http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala index 65ff348..8b89c73 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala @@ -54,12 +54,14 @@ object Server extends App { import spray.json.DefaultJsonProtocol._ + val graphQLServer = new GraphQLServer() + val route: Route = get { getFromResource("assets/graphiql.html") } ~ (post & path("updateEdgeFetcher")) { entity(as[JsValue]) { body => - GraphQLServer.updateEdgeFetcher(body) match { + graphQLServer.updateEdgeFetcher(body) match { case Success(_) => complete(StatusCodes.OK -> JsString("Update fetcher finished")) case Failure(e) => logger.error("Error on execute", e) @@ -70,11 +72,11 @@ object Server extends App { parameters('operationName.?, 'variables.?) { (operationNameParam, variablesParam) => entity(as[Document]) { document â variablesParam.map(parseJson) match { - case None â complete(GraphQLServer.executeGraphQLQuery(document, operationNameParam, JsObject())) - case Some(Right(js)) â complete(GraphQLServer.executeGraphQLQuery(document, operationNameParam, js.asJsObject)) + case None â complete(graphQLServer.executeGraphQLQuery(document, operationNameParam, JsObject())) + case Some(Right(js)) â complete(graphQLServer.executeGraphQLQuery(document, operationNameParam, js.asJsObject)) case Some(Left(e)) â logger.error("Error on execute", e) - complete(StatusCodes.BadRequest -> GraphQLServer.formatError(e)) + complete(StatusCodes.BadRequest -> graphQLServer.formatError(e)) } } ~ entity(as[JsValue]) { body â val fields = body.asJsObject.fields @@ -84,13 +86,13 @@ object Server extends App { val variables = fields.get("variables").filterNot(_ == JsNull) query.map(QueryParser.parse(_)) match { - case None â complete(StatusCodes.BadRequest -> GraphQLServer.formatError("No query to execute")) + case None â complete(StatusCodes.BadRequest -> graphQLServer.formatError("No query to execute")) case Some(Failure(error)) â logger.error("Error on execute", error) - complete(StatusCodes.BadRequest -> GraphQLServer.formatError(error)) + complete(StatusCodes.BadRequest -> graphQLServer.formatError(error)) case Some(Success(document)) => variables match { - case Some(js) â complete(GraphQLServer.executeGraphQLQuery(document, operationName, js.asJsObject)) - case None â complete(GraphQLServer.executeGraphQLQuery(document, operationName, JsObject())) + case Some(js) â complete(graphQLServer.executeGraphQLQuery(document, operationName, js.asJsObject)) + case None â complete(graphQLServer.executeGraphQLQuery(document, operationName, JsObject())) } } } @@ -101,7 +103,9 @@ object Server extends App { logger.info(s"Starting GraphQL server... $port") - Http().bindAndHandle(route, "0.0.0.0", port) + Http().bindAndHandle(route, "0.0.0.0", port).foreach { binding => + logger.info(s"GraphQL server ready for connect") + } def shutdown(): Unit = { logger.info("Terminating...") http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/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 b5e65dc..bd98504 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 @@ -273,14 +273,29 @@ class GraphRepository(val graph: S2GraphLike) { def deleteLabel(args: Args): Try[Label] = { val labelName = args.arg[String]("name") - val deleteLabelTry = Management.deleteLabel(labelName) + withLogTryResponse("deleteLabel", deleteLabelTry) } - def services(): List[Service] = Service.findAll() + def services(): List[Service] = { + Service.findAll() + } + + def serviceColumns(): List[ServiceColumn] = { + val allServices = services().toSet - def serviceColumns(): List[ServiceColumn] = ServiceColumn.findAll() + ServiceColumn + .findAll() + .filter(sc => allServices(sc.service)) + } + + def labels() = { + val allServiceColumns = serviceColumns().toSet + + Label + .findAll() + .filter(l => allServiceColumns(l.srcColumn) || allServiceColumns(l.tgtColumn)) + } - def labels() = Label.findAll() } http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala index 1275ca7..7b57059 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala @@ -67,7 +67,11 @@ class ManagementType(repo: GraphRepository) { import org.apache.s2graph.graphql.bind.Unmarshaller._ import org.apache.s2graph.graphql.types.StaticTypes._ - lazy val serviceColumnOnServiceWithPropInputObjectFields = repo.services().map { service => + val services = repo.services() + val serviceColumns = repo.serviceColumns() + val labels = repo.labels() + + lazy val serviceColumnOnServiceWithPropInputObjectFields = services.map { service => InputField(service.serviceName.toValidName, OptionInputType(InputObjectType( s"Input_${service.serviceName.toValidName}_ServiceColumn_Props", description = "desc here", @@ -78,7 +82,7 @@ class ManagementType(repo: GraphRepository) { ))) } - lazy val serviceColumnOnServiceInputObjectFields = repo.services().map { service => + lazy val serviceColumnOnServiceInputObjectFields = services.map { service => InputField(service.serviceName.toValidName, OptionInputType(InputObjectType( s"Input_${service.serviceName.toValidName}_ServiceColumn", description = "desc here", @@ -91,15 +95,17 @@ class ManagementType(repo: GraphRepository) { def makeServiceColumnEnumTypeOnService(service: Service): EnumType[String] = { val columns = service.serviceColumns(false).toList EnumType( - s"Enum_${service.serviceName}_ServiceColumn", + s"Enum_${service.serviceName.toValidName}_ServiceColumn", description = Option("desc here"), - values = dummyEnum +: columns.map { column => - EnumValue(column.columnName.toValidName, value = column.columnName.toValidName) - } + values = + if (columns.isEmpty) dummyEnum :: Nil + else columns.map { column => + EnumValue(column.columnName.toValidName, value = column.columnName) + } ) } - lazy val labelPropsInputFields = repo.labels().map { label => + lazy val labelPropsInputFields = labels.map { label => InputField(label.label.toValidName, OptionInputType(InputObjectType( s"Input_${label.label.toValidName}_props", description = "desc here", @@ -134,28 +140,34 @@ class ManagementType(repo: GraphRepository) { lazy val ServiceListType = EnumType( s"Enum_Service", description = Option("desc here"), - values = - dummyEnum +: repo.services().map { service => + values = { + if (services.isEmpty) dummyEnum :: Nil + else services.map { service => EnumValue(service.serviceName.toValidName, value = service.serviceName) } + } ) lazy val ServiceColumnListType = EnumType( s"Enum_ServiceColumn", description = Option("desc here"), - values = - dummyEnum +: repo.serviceColumns().map { serviceColumn => + values = { + if (serviceColumns.isEmpty) dummyEnum :: Nil + else serviceColumns.map { serviceColumn => EnumValue(serviceColumn.columnName.toValidName, value = serviceColumn.columnName) } + } ) lazy val EnumLabelsType = EnumType( s"Enum_Label", description = Option("desc here"), - values = - dummyEnum +: repo.labels().map { label => + values = { + if (labels.isEmpty) dummyEnum :: Nil + else labels.map { label => EnumValue(label.label.toValidName, value = label.label) } + } ) lazy val ServiceMutationResponseType = makeMutationResponseType[Service]( @@ -200,19 +212,25 @@ class ManagementType(repo: GraphRepository) { val AddPropServiceType = InputObjectType[ServiceColumnParam]( "Input_Service_ServiceColumn_Props", description = "desc", - fields = DummyInputField +: serviceColumnOnServiceWithPropInputObjectFields + fields = + if (serviceColumnOnServiceWithPropInputObjectFields.isEmpty) DummyInputField :: Nil + else serviceColumnOnServiceWithPropInputObjectFields ) val ServiceColumnSelectType = InputObjectType[ServiceColumnParam]( "Input_Service_ServiceColumn", description = "desc", - fields = DummyInputField +: serviceColumnOnServiceInputObjectFields + fields = + if (serviceColumnOnServiceInputObjectFields.isEmpty) DummyInputField :: Nil + else serviceColumnOnServiceInputObjectFields ) val InputServiceType = InputObjectType[ServiceColumnParam]( "Input_Service", description = "desc", - fields = DummyInputField +: serviceColumnOnServiceInputObjectFields + fields = + if (serviceColumnOnServiceInputObjectFields.isEmpty) DummyInputField :: Nil + else serviceColumnOnServiceInputObjectFields ) lazy val servicesField: Field[GraphRepository, Any] = Field( http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/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 a18fc4e..e41e838 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 @@ -27,6 +27,7 @@ import org.apache.s2graph.graphql.repository.GraphRepository import sangria.schema._ import org.apache.s2graph.graphql.types.StaticTypes._ +import scala.collection.mutable import scala.language.existentials object S2Type { @@ -102,36 +103,116 @@ object S2Type { labelFields.asInstanceOf[Seq[InputField[Any]]] ++ propFields.asInstanceOf[Seq[InputField[Any]]] } - def makeServiceColumnFields(column: ServiceColumn, allLabels: Seq[Label]): List[Field[GraphRepository, Any]] = { - val reservedFields = List("id" -> column.columnType, "timestamp" -> "long") + def makeServiceColumnFields(column: ServiceColumn, + relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = { + + val reservedFields = Vector("id" -> column.columnType, "timestamp" -> "long") val columnMetasKv = column.metasWithoutCache.filter(ColumnMeta.isValid).map { columnMeta => columnMeta.name -> columnMeta.dataType } - val (sameLabel, diffLabel) = allLabels.toList.partition(l => l.srcColumn == l.tgtColumn) + val (sameLabel, diffLabel) = relatedLabels.toList.partition(l => l.srcColumn == l.tgtColumn) - val outLabels = diffLabel.filter(l => column == l.srcColumn).distinct.toList - val inLabels = diffLabel.filter(l => column == l.tgtColumn).distinct.toList + val outLabels = diffLabel.filter(l => column == l.srcColumn).distinct + val inLabels = diffLabel.filter(l => column == l.tgtColumn).distinct val inOutLabels = sameLabel.filter(l => l.srcColumn == column && l.tgtColumn == column) - lazy val columnFields = (reservedFields ++ columnMetasKv).map { case (k, v) => makeGraphElementField(k.toValidName, v) } + val columnFields = (reservedFields ++ columnMetasKv).map { case (k, v) => makeGraphElementField(k.toValidName, v) } - lazy val outLabelFields: List[Field[GraphRepository, Any]] = outLabels.map(l => makeLabelField("out", l, allLabels)) - lazy val inLabelFields: List[Field[GraphRepository, Any]] = inLabels.map(l => makeLabelField("in", l, allLabels)) - lazy val inOutLabelFields: List[Field[GraphRepository, Any]] = inOutLabels.map(l => makeLabelField("both", l, allLabels)) - lazy val propsType = wrapField(s"ServiceColumn_${column.service.serviceName}_${column.columnName}_props", "props", columnFields) + val outLabelFields: List[Field[GraphRepository, Any]] = outLabels.map(l => toLabelFieldOnColumn("out", l, relatedLabels)(typeCache)) + val inLabelFields: List[Field[GraphRepository, Any]] = inLabels.map(l => toLabelFieldOnColumn("in", l, relatedLabels)(typeCache)) + val inOutLabelFields: List[Field[GraphRepository, Any]] = inOutLabels.map(l => toLabelFieldOnColumn("both", l, relatedLabels)(typeCache)) + val propsType = wrapField(s"ServiceColumn_${column.service.serviceName.toValidName}_${column.columnName.toValidName}_props", "props", columnFields) - lazy val labelFieldNameSet = (outLabels ++ inLabels ++ inOutLabels).map(_.label).toSet + val labelFieldNameSet = (outLabels ++ inLabels ++ inOutLabels).map(_.label.toValidName).toSet - propsType :: inLabelFields ++ outLabelFields ++ inOutLabelFields ++ columnFields.filterNot(cf => labelFieldNameSet(cf.name)) + propsType :: inLabelFields ++ outLabelFields ++ inOutLabelFields ++ columnFields.filterNot(cf => labelFieldNameSet(cf.name.toValidName)) } - def makeServiceField(service: Service, allLabels: List[Label])(implicit repo: GraphRepository): List[Field[GraphRepository, Any]] = { - val columnsOnService = service.serviceColumns(false).toList.map { column => - lazy val serviceColumnFields = makeServiceColumnFields(column, allLabels) - lazy val ColumnType = ObjectType( - s"ServiceColumn_${service.serviceName}_${column.columnName}", + def toLabelFieldOnColumn(dir: String, label: Label, relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): Field[GraphRepository, Any] = { + + val LabelType = makeLabelType(dir, label, relatedLabels)(typeCache) + + val dirArgs = dir match { + case "in" => Argument("direction", OptionInputType(InDirectionType), "desc here", defaultValue = "in") :: Nil + case "out" => Argument("direction", OptionInputType(OutDirectionType), "desc here", defaultValue = "out") :: Nil + case "both" => Argument("direction", OptionInputType(BothDirectionType), "desc here", defaultValue = "out") :: Nil + } + + val indexEnumType = EnumType( + s"Label_Index_${label.label.toValidName}", + description = Option("desc here"), + values = + if (label.indices.isEmpty) EnumValue("_", value = "_") :: Nil + else label.indices.map(idx => EnumValue(idx.name.toValidName, value = idx.name)) + ) + + val paramArgs = List( + Argument("offset", OptionInputType(IntType), "desc here", defaultValue = 0), + Argument("limit", OptionInputType(IntType), "desc here", defaultValue = 100), + Argument("index", OptionInputType(indexEnumType), "desc here"), + Argument("filter", OptionInputType(StringType), "desc here") + ) + + val edgeTypeField: Field[GraphRepository, Any] = Field( + s"${label.label.toValidName}", + ListType(LabelType), + arguments = dirArgs ++ paramArgs, + description = Some("fetch edges"), + resolve = { c => + implicit val ec = c.ctx.ec + + val edgeQueryParam = graphql.types.FieldResolver.label(label, c) + val empty = Seq.empty[S2EdgeLike] + + DeferredValue( + GraphRepository.edgeFetcher.deferOpt(edgeQueryParam) + ).map(m => m.fold(empty)(m => m._2)) + } + ) + + edgeTypeField + } + + + def makeColumnType(column: ServiceColumn, relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = { + + val objectName = s"ServiceColumn_${column.service.serviceName.toValidName}_${column.columnName.toValidName}" + + typeCache.getOrElseUpdate(objectName, { + lazy val serviceColumnFields = makeServiceColumnFields(column, relatedLabels)(typeCache) + + val ColumnType = ObjectType( + objectName, () => fields[GraphRepository, Any](serviceColumnFields: _*) ) + ColumnType + }) + } + + def makeServiceType(service: Service, + relatedColumns: Seq[ServiceColumn], + relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = { + + val _serviceFields = makeServiceFields(service, relatedColumns, relatedLabels)(typeCache) + val serviceFields = if (_serviceFields.isEmpty) DummyObjectTypeField :: _serviceFields else _serviceFields + + ObjectType( + s"Service_${service.serviceName.toValidName}", + fields[GraphRepository, Any](serviceFields: _*) + ) + } + + def makeServiceFields(service: Service, columns: Seq[ServiceColumn], relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = { + + val columnsOnService = columns.map { column => + + val ColumnType = makeColumnType(column, relatedLabels)(typeCache) + Field(column.columnName.toValidName, ListType(ColumnType), arguments = List( @@ -152,76 +233,52 @@ object S2Type { ): Field[GraphRepository, Any] } - columnsOnService + columnsOnService.toList } - def makeLabelField(dir: String, label: Label, allLabels: Seq[Label]): Field[GraphRepository, Any] = { - val labelReserved = List("direction" -> "string", "timestamp" -> "long") - val labelProps = label.labelMetas.map { lm => lm.name -> lm.dataType } + def makeLabelType(dir: String, label: Label, relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = { - val column = if (dir == "out") label.tgtColumn else label.srcColumn + val objectName = s"Label_${label.label.toValidName}_${dir}" - lazy val labelFields: List[Field[GraphRepository, Any]] = - (labelReserved ++ labelProps).map { case (k, v) => makeGraphElementField(k.toValidName, v) } - - lazy val labelPropField = wrapField(s"Label_${label.label.toValidName}_props", "props", labelFields) + typeCache.getOrElseUpdate(objectName, { + lazy val labelFields = makeLabelFields(dir, label, relatedLabels)(typeCache) - lazy val labelColumnType = ObjectType(s"Label_${label.label.toValidName}_${column.columnName.toValidName}", - () => makeServiceColumnFields(column, allLabels) - ) - - lazy val serviceColumnField: Field[GraphRepository, Any] = Field(column.columnName, labelColumnType, resolve = c => { - implicit val ec = c.ctx.ec - val vertexQueryParam = FieldResolver.serviceColumnOnLabel(c) + val LabelType = ObjectType( + objectName, + () => fields[GraphRepository, Any](labelFields: _*) + ) - DeferredValue(GraphRepository.vertexFetcher.defer(vertexQueryParam)).map(m => m._2.head) + LabelType }) + } - lazy val EdgeType = ObjectType( - s"Label_${label.label.toValidName}_${column.columnName.toValidName}_${dir}", - () => fields[GraphRepository, Any]( - List(serviceColumnField, labelPropField) ++ labelFields.filterNot(_.name == column.columnName): _*) - ) + def makeLabelFields(dir: String, label: Label, relatedLabels: Seq[Label]) + (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = { - val dirArgs = dir match { - case "in" => Argument("direction", OptionInputType(InDirectionType), "desc here", defaultValue = "in") :: Nil - case "out" => Argument("direction", OptionInputType(OutDirectionType), "desc here", defaultValue = "out") :: Nil - case "both" => Argument("direction", OptionInputType(BothDirectionType), "desc here", defaultValue = "out") :: Nil - } + val labelReserved = List("direction" -> "string", "timestamp" -> "long") - val idxNames = label.indices.map { idx => - EnumValue(idx.name.toValidName, value = idx.name.toValidName) - } + val labelProps = label.labelMetas + .filterNot(l => labelReserved.exists(kv => kv._1 == l.name)) + .map { lm => lm.name -> lm.dataType } - val indexEnumType = EnumType( - s"Label_Index_${label.label.toValidName}", - description = Option("desc here"), - values = idxNames - ) + val column = if (dir == "out") label.tgtColumn else label.srcColumn - val paramArgs = List( - Argument("offset", OptionInputType(IntType), "desc here", defaultValue = 0), - Argument("limit", OptionInputType(IntType), "desc here", defaultValue = 100), - Argument("index", OptionInputType(indexEnumType), "desc here"), - Argument("filter", OptionInputType(StringType), "desc here") - ) + val labelFields: List[Field[GraphRepository, Any]] = + (labelReserved ++ labelProps).map { case (k, v) => makeGraphElementField(k.toValidName, v) } - lazy val edgeTypeField: Field[GraphRepository, Any] = Field( - s"${label.label.toValidName}", - ListType(EdgeType), - arguments = dirArgs ++ paramArgs, - description = Some("fetch edges"), - resolve = { c => + val labelPropField = wrapField(s"Label_${label.label.toValidName}_props", "props", labelFields) + val labelColumnType = makeColumnType(column, relatedLabels)(typeCache) + + val serviceColumnField: Field[GraphRepository, Any] = + Field(column.columnName.toValidName, labelColumnType, resolve = c => { implicit val ec = c.ctx.ec + val vertexQueryParam = FieldResolver.serviceColumnOnLabel(c) - val edgeQueryParam = graphql.types.FieldResolver.label(label, c) - val empty = Seq.empty[S2EdgeLike] + DeferredValue(GraphRepository.vertexFetcher.defer(vertexQueryParam)).map(m => m._2.head) + }) - DeferredValue(GraphRepository.edgeFetcher.deferOpt(edgeQueryParam)).map(m => m.fold(empty)(_._2)) - } - ) - - edgeTypeField + List(serviceColumnField, labelPropField) ++ labelFields.filterNot(_.name.toValidName == column.columnName.toValidName) } } @@ -229,26 +286,35 @@ class S2Type(repo: GraphRepository) { import S2Type._ import org.apache.s2graph.graphql.bind.Unmarshaller._ - - implicit val graphRepository = repo + import scala.collection._ /** * fields */ - lazy val serviceFields: List[Field[GraphRepository, Any]] = repo.services().map { service => - lazy val serviceFields = DummyObjectTypeField :: makeServiceField(service, repo.labels()) + val serviceFields: List[Field[GraphRepository, Any]] = { + val allColumns = repo.serviceColumns() + val allLabels = repo.labels() - lazy val ServiceType = ObjectType( - s"Service_${service.serviceName.toValidName}", - fields[GraphRepository, Any](serviceFields: _*) - ) + val typeCache = mutable.Map.empty[String, ObjectType[GraphRepository, Any]] + + repo.services().flatMap { service => + val relatedColumns = allColumns.filter(_.service == service).toSet + val relatedLabels = allLabels.filter(l => relatedColumns(l.srcColumn) || relatedColumns(l.tgtColumn)) + + if (relatedColumns.isEmpty) Nil + else { + val ServiceType = makeServiceType(service, relatedColumns.toVector, relatedLabels.distinct)(typeCache) - Field( - service.serviceName.toValidName, - ServiceType, - description = Some(s"serviceName: ${service.serviceName}"), - resolve = _ => service - ): Field[GraphRepository, Any] + val f = Field( + service.serviceName.toValidName, + ServiceType, + description = Some(s"serviceName: ${service.serviceName}"), + resolve = _ => service + ): Field[GraphRepository, Any] + + List(f) + } + } } /** @@ -287,7 +353,7 @@ class S2Type(repo: GraphRepository) { * Provide s2graph query / mutate API * - Fields is created(or changed) for metadata is changed. */ - lazy val queryFields = serviceFields + val queryFields = serviceFields lazy val mutationFields: List[Field[GraphRepository, Any]] = List( Field("addVertex", http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/597b8535/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala index 7451023..d094ee0 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala @@ -26,7 +26,7 @@ import org.apache.s2graph.graphql.repository.GraphRepository * * When a Label or Service is created, the GraphQL schema is created dynamically. */ -class SchemaDef(g: GraphRepository) { +class SchemaDef(g: GraphRepository, withAdmin: Boolean = false) { import sangria.schema._ @@ -39,17 +39,24 @@ class SchemaDef(g: GraphRepository) { fields(s2Type.queryFields ++ queryManagementFields: _*) ) - val mutateManagementFields = List(wrapField("MutationManagement", "Management", s2ManagementType.mutationFields)) - val S2MutationType = ObjectType[GraphRepository, Any]( - "Mutation", - fields(s2Type.mutationFields ++ mutateManagementFields: _*) - ) + lazy val mutateManagementFields = List(wrapField("MutationManagement", "Management", s2ManagementType.mutationFields)) + + val S2MutationType = + if (!withAdmin) None + else { + val mutationTpe = ObjectType[GraphRepository, Any]( + "Mutation", + fields(s2Type.mutationFields ++ mutateManagementFields: _*) + ) + + Option(mutationTpe) + } val directives = S2Directive.Transform :: BuiltinDirectives private val s2Schema = Schema( S2QueryType, - Option(S2MutationType), + S2MutationType, directives = directives )
