Suggest to implement GraphQL as standard web interface for S2Graph.

  • To support GraphQL i used Akka HTTP and Sangria. each is an HTTP Server and GraphQL Scala implementation.
  • I also used GraphiQL as a tool for GraphQL queries.

Wroking example

mutation

query

Overview

The reason I started this work is because the Label used by S2Graph has a strong type system, so I think it will work well with the schema provided by GraphQL.

To do this, we converted S2Graph Model (Label, Service ...) into GraphLQL schema whenever added (changed).

Setup

Assume that hbase is running on localhost.
If the hbase environment is not set up, you can run it with the following command

sbt package
target/apache-s2graph-0.2.1-SNAPSHOT-incubating-bin/bin/hbase-standalone.sh start 

If hbase is running well, run the following command after cloning the project locally.

GraphiQL is not directly related to the GraphQL implementation, but is recommended for convenient queries. Because of the license problem, you should download the file through the following command.

cd s2graphql/src/main/resources/assets
wget https://raw.githubusercontent.com/daewon/sangria-akka-http-example/master/src/main/resources/assets/graphiql.html

You can see that the graphiql.html file is added to the s2graphql/src/main/resources/assets folder as shown below.

$ls
graphiql.html

And let's run http server.

sbt -DschemaCacheTTL=-1 -Dhttp.port=8000 'project s2graphql' '~re-start'

When the server is running, connect to http://localhost:8000. If it works normally, you can see the following screen.

2018-01-31 4 39 25

API List

  • createService
  • createLabel
  • addEdges
  • addEdge
  • query (You can recursively browse the linked labels from the service and any other labels that are linked from that label)

Your First Grpah (GraphQL version)

S2Graph tutorial I have ported the contents of Your first graph provided by S2Graph based on GraphQL.

Start by connecting to http://localhost:8000.

The environment for this example is Mac OS and Chrome. You can get help with schema-based Autocompletion using the ctrl + space key.

If you add a label or service, etc. you will need to refresh (cmd + r) your browser because the schema will change dynamically.

  1. First, we need a name for the new service.

    The following POST query will create a service named “KakaoFavorites”.

Request

mutation {
  Management {
    createService(
      name: "KakaoFavorites"
      compressionAlgorithm: gz      
    ) {
      object {
        name
      }
    }
  }
}

Response

{
  "data": {
    "Management": {
      "createService": {
        "object": {
          "name": "KakaoFavorites"
        }
      }
    }
  }
}

1.1 And create a `service column`` which is meta information for storing vertex.

The following POST query will create a service column with the age attribute named "user"

Request

mutation {
  Management {
    createServiceColumn(
      serviceName: KakaoFavorites
      columnName: "user"
      columnType: string
      props: {
        name: "age"
        dataType: int
        defaultValue: "0"
        storeInGlobalIndex: true
      }
    ) {
      isSuccess
      object {
        name
        props {
          name
          dataType          
        }
      }
    }
  }
}

Response

{
  "data": {
    "Management": {
      "createServiceColumn": {
        "isSuccess": true,
        "object": {
          "name": "user",
          "props": [
            {
              "name": "age",
              "dataType": "int"
            }
          ]
        }
      }
    }
  }
}

To make sure the service and service column is created correctly, check out the following.

Since the schema has changed, GraphiQL must recognize the changed schema. To do this, refresh the browser several times.

Request

query {
  Management {
    Service(name:KakaoFavorites) {    
      name
      serviceColumns {
        name
        columnType
        props {
          name
          dataType
        }
      }
    }
  }
}

Response

{
  "data": {
    "Management": {
      "Service": {
        "name": "KakaoFavorites",
        "serviceColumns": [
          {
            "name": "user",
            "columnType": "string",
            "props": [
              {
                "name": "age",
                "dataType": "int"
              }
            ]
          }
        ]
      }
    }
  }
}
  1. Next, we will need some friends.

    In S2Graph, relationships are organized as labels. Create a label called friends using the following createLabel API call:

Request

mutation {
  Management {
    createLabel(
      name: "friends"
      sourceService: {
        KakaoFavorites: {
          columnName: user
        }
      }
      targetService: {
        KakaoFavorites: {
          columnName: user
        }
      }
      consistencyLevel: strong
    ) {
      isSuccess
      message
      object {
        name
        serviceName
        tgtColumnName        
      }
    }
  }
} 

Response

{
  "data": {
    "Management": {
      "createLabel": {
        "isSuccess": true,
        "message": "Mutation successful",
        "object": {
          "name": "friends",
          "serviceName": "KakaoFavorites",
          "tgtColumnName": "user"
        }
      }
    }
  }
}

Check if the label has been created correctly

Since the schema has changed, GraphiQL must recognize the changed schema. To do this, refresh the browser several times.

Request

query {
  Management {
    Label(name: friends) {
      name
      srcColumnName
      tgtColumnName
    }
  }
}

Response

{
  "data": {
    "Management": {
      "Label": {
        "name": "friends",
        "srcColumnName": "user",
        "tgtColumnName": "user"
      }
    }
  }
}

Now that the label friends is ready, we can store the friendship data. Entries of a label are called edges, and you can add edges with edges/insert API:

Since the schema has changed, GraphiQL must recognize the changed schema. To do this, refresh the browser several times.

Request

mutation {
  addEdge(
    friends: [
      {from: "Elmo", to: "Big Bird"},
      {from: "Elmo", to: "Ernie"},    
      {from: "Elmo", to: "Bert"},    
      {from: "Cookie Monster", to: "Grover"},    
      {from: "Cookie Monster", to: "Kermit"},    
      {from: "Cookie Monster", to: "Oscar"},    
    ]
  ) {
    isSuccess    
  }
}

Response

{
  "data": {
    "addEdge": [
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      }
    ]
  }
}

Query friends of Elmo with getEdges API:

Request

query {
  KakaoFavorites {    
    user(id: "Elmo") {
      friends {
        to {
          id
        }
      }
    }
  }
}

Response

{
  "data": {
    "KakaoFavorites": [
      {
        "friends": [
          {
            "to": "Bert"
          },
          {
            "to": "Ernie"
          },
          {
            "to": "Big Bird"
          }
        ]
      }
    ]
  }
}

Now query friends of Cookie Monster:

Request

query {
  KakaoFavorites {    
    user(id: "Elmo") {      
      friends {        
        to {
          id
        }
      }
    }
  }
}

Response

{
  "data": {
    "KakaoFavorites": {
      "user": [
        {
          "friends": [
            {
              "to": {
                "id": "Ernie"
              }
            },
            {
              "to": {
                "id": "Big Bird"
              }
            },
            {
              "to": {
                "id": "Bert"
              }
            }
          ]
        }
      ]
    }
  }
}
  1. Users of Kakao Favorites will be able to post URLs of their favorite websites.

Request

mutation {
  Management {
    createLabel(
      name: "post"
      sourceService: {
        KakaoFavorites: {
          columnName: user
        }
      }
			targetService: {
        KakaoFavorites: {
          columnName: user
        }
      }
      consistencyLevel: strong
    ) {
      isSuccess
      message
      object {        
        name      
      }
    }
  }
}

Response

{
  "data": {
    "Management": {
      "createLabel": {
        "isSuccess": true,
        "message": "Mutation successful",
        "object": {
          "name": "post"
        }
      }
    }
  }
}

Now, insert some posts of the users:

Since the schema has changed, GraphiQL must recognize the changed schema. To do this, refresh the browser several times.

Request

mutation {
  addEdge(
    post: [
      { from: "Big Bird", to: "www.kakaocorp.com/en/main" },
      { from: "Big Bird", to: "github.com/kakao/s2graph" },
      { from: "Ernie", to: "groups.google.com/forum/#!forum/s2graph" },
      { from: "Grover", to: "hbase.apache.org/forum/#!forum/s2graph" },
      { from: "Kermit", to: "www.playframework.com"},
      { from: "Oscar", to: "www.scala-lang.org"}
    ]
  ) {
    isSuccess
  }
}

Response

{
  "data": {
    "addEdge": [
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      },
      {
        "isSuccess": true
      }
    ]
  }
}
  1. So far, we have designed a label schema for the labels friends and post, and stored some edges to them.+

    This should be enough for creating the timeline feature! The following two-step query will return the URLs for Elmo‘s timeline, which are the posts of Elmo’s friends:

Request

query {
  KakaoFavorites {
    user(id: "Elmo") {
      id
      friends {
        to {
          id
          post {
            to {
              id
            }
          }
        }
      }
    }
  }
}

Response

{
  "data": {
    "KakaoFavorites": {
      "user": [
        {
          "id": "Elmo",
          "friends": [
            {
              "to": {
                "id": "Ernie",
                "post": [
                  {
                    "to": {
                      "id": "groups.google.com/forum/#!forum/s2graph"
                    }
                  }
                ]
              }
            },
            {
              "to": {
                "id": "Bert",
                "post": []
              }
            },
            {
              "to": {
                "id": "Big Bird",
                "post": [
                  {
                    "to": {
                      "id": "www.kakaocorp.com/en/main"
                    }
                  },
                  {
                    "to": {
                      "id": "github.com/kakao/s2graph"
                    }
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  }
}

Also try Cookie Monster's timeline:

Request

query {
  KakaoFavorites {
    user(id: "Cookie Monster") {
      friends {      
        to {
          id
          post {
            to {
              id
            }
          }
        }
      }
    }
  }
}

Response

{
  "data": {
    "KakaoFavorites": {
      "user": [
        {
          "friends": [
            {
              "to": {
                "id": "Oscar",
                "post": [
                  {
                    "to": {
                      "id": "www.scala-lang.org"
                    }
                  }
                ]
              }
            },
            {
              "to": {
                "id": "Kermit",
                "post": [
                  {
                    "to": {
                      "id": "www.playframework.com"
                    }
                  }
                ]
              }
            },
            {
              "to": {
                "id": "Grover",
                "post": [
                  {
                    "to": {
                      "id": "hbase.apache.org/forum/#!forum/s2graph"
                    }
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  }
}

2018-01-31 5 18 46

The example above is by no means a full blown social network timeline, but it gives you an idea of how to represent, store and query graph data with S2Graph.