https://github.com/mneedham/neo4j-graphql
https://github.com/mneedham/neo4j-graphql
Last synced: about 1 year ago
JSON representation
- Host: GitHub
- URL: https://github.com/mneedham/neo4j-graphql
- Owner: mneedham
- License: apache-2.0
- Created: 2017-04-20T19:20:06.000Z (about 9 years ago)
- Default Branch: idl-roundtrip
- Last Pushed: 2017-05-15T12:00:14.000Z (about 9 years ago)
- Last Synced: 2025-02-14T13:50:21.948Z (over 1 year ago)
- Language: Kotlin
- Size: 419 KB
- Stars: 1
- Watchers: 5
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.adoc
- License: license.txt
Awesome Lists containing this project
README
= Neo4j-GraphQL Extension
image:https://travis-ci.org/neo4j-contrib/neo4j-graphql.svg?branch=master["Build Status", link="https://travis-ci.org/neo4j-contrib/neo4j-graphql"]
NOTE: This is an very early version of GraphQL support for Neo4j.
This implementation provides a GraphQL API to Neo4j, it comes as library but can also be installed as Neo4j server extension
to act as a GraphQL endpoint.
We want to explore three approaches:
1. read schema / metadata from the database provide GraphQL DataFetcher that generate and run Cypher (WIP) √
2. make the same work with externally configured schema information (using IDL) √
3. do a direct GraphQL AST to Cypher transformation without bothering with Schema (TBD)
NOTE: Currently only Node and simple Relationship queries are supported.
Deeper Relationship queries and aggregations are up next.
== License
Apache License v2
== Schema
The library samples the existing data and adds one GraphQLObject for each Node-Label with all the properties and their types found as outputs and input-arguments.
Relationship information is collected with direction, type, end-node-labels and degree (to determine single element or collection result).
// Additional labels on a node are added as GraphQLInterface's.
Each relationship-type and end-node label is added as a virtual property to the node type, named `TYPE_Label` for outgoing and `Label_TYPE` for incoming relationships.
== Installation
The current version of this extension works with Neo4j 3.0 and 3.1
----
git clone https://github.com/neo4j-contrib/neo4j-graphql
cd neo4j-graphql
mvn clean package
cp target/neo4j-graphql-1.0-SNAPSHOT.jar $NEO4J_HOME/plugins
echo 'dbms.unmanaged_extension_classes=org.neo4j.graphql=/graphql' >> $NEO4J_HOME/config/neo4j.conf
$NEO4J_HOME/bin/neo4j restart
----
== Features
[options=header,cols="a,2a,3m"]
|===
| name | information | example
| entities | each node label represented as entity | { User {name,age} }
| multi entities | multiple entities per query turned into `UNION` | { User {name,age} City {name,geo} }
| properties (out) | via sampling property names and types are determined | { City {name,geo} }
| properties (in) | all properties can be used as filtering (exact/list) input parameters, will be turned into Cypher parameters | { City(name:"Berlin") {name,geo} }
| parameters | passed through as Cypher parameters | query UserQuery ($name: String!) { User(name:$name) {name,age} }
| relationships (out) | via a `RELTYPE_Label` nested field | { User { name, LIVES_IN_City {name} } }
| relationships (in) | via a `Label_RELTYPE` nested field | { City { name, User_LIVES_IN { name } } }
| ordering | via an extra `orderBy` input field | query UserSortQuery { User(orderBy:[name_desc,age_desc]) {name,age}}
| directive | directives control cypher prefixes, *note that directives have to be set at the first entity* |
| directive - query plan | `@profile / @explain` will be returned in extra field `extensions` | query UserQuery { User @profile {name} }
// | directive - version | set cypher version to use `@version(3.0,3.1,3.2)` | query UserQuery { User @version(3.0) {name} }
// | directive - compiled runtime | force compiled runtime to be used | query UserQuery { User @compile {name} }
| extensions | extra information returned | fields are: columns, query, warnings, plan, type READ_ONLY/READ_WRITE,
| IDL | define schema via IDL | :POST /graphql/idl "type Person {name: String!, born: Int}"
|===
== Usage
Run `:play movies` in your Neo4j Server, click the statement and run it to insert the basic movie graph.
== GraphiQL
Get GraphiQL electron app from: https://github.com/skevy/graphiql-app
If your Neo4j Server runs with auth enabled, add the appropriate Basic-Auth header in the "Edit HTTP Headers" screen.
----
echo "Authorization: Basic $(echo -n "neo4j:" | base64)"
----
And then run a query like:
----
query AllPeopleQuery {
Person(name:"Kevin Bacon") {
name
born
ACTED_IN_Movie {
title
released
tagline
}
}
}
----
image::graphiql.jpg[width=600]
You can also use variables or query the schema:
.Which types are in the schema
----
{
__schema {
types {
name
kind
description
}
}
}
----
or
.Which types are available for querying
----
{
__schema {
queryType {
fields { name, description }
}
}
}
----
and then query for real data
----
# query
query PersonQuery($name: String!) {
Person(name: $name) {
name
born
ACTED_IN_Movie {
title
released
tagline
}
}
}
# variables
{"name":"Keanu Reeves"}
----
== Procedure
This library also comes with a User Defined Procedure to execute GraphQL:
[source,cypher]
----
WITH '{ Person(born: 1961) { name, born } }' as query, {} as params
CALL graphql.execute(query,params) YIELD result
UNWIND result.Person as p
RETURN p.name, p.born
----
image::graphql.execute.jpg[width=600]
== Examples
Some more examples
.Relationship Argument
----
query MoviePersonQuery {
Movie {
title
Person_ACTED_IN(name:"Tom Hanks") {
name
}
}
}
----
.Nested Relationships
----
query PersonMoviePersonQuery {
Person {
name
ACTED_IN_Movie {
title
Person_ACTED_IN {
name
}
}
}
}
----
.Sorting
----
query PersonQuery {
Person(orderBy: [age_asc, name_desc]) {
name
born
}
}
----
== CURL
=== POST IDL
_(Optional if no data in database)_
----
curl -u neo4j: -i -XPOST -d'type Person { name: String, born: Int }' http://localhost:7474/graphql/idl/
{Person=MetaData{type='Person', ids=[], indexed=[], properties={name=PropertyType(name=String, array=false, nonNull=false), born=PropertyType(name=Int, array=false, nonNull=false)}, labels=[], relationships={}}}
----
=== Query the Schema
----
curl -u neo4j: -i -XPOST -d'{"query": "query {__schema {types {kind, name, description}}}"}' -H accept:application/json -H content-type:application/json http://localhost:7474/graphql/
{"data":{"__schema":{"types":[{"kind":"OBJECT","name":"QueryType","description":null},{"kind":"OBJECT","name":"Movie","description":"Movie-Node"},....
----
----
query {__schema {queryType {
kind,description,fields {
name
}
}}}
----
=== Get All People
----
curl -u neo4j: -i -XPOST -d'{"query": "query AllPeopleQuery { Person {name,born} } }"}' -H accept:application/json -H content-type:application/json http://localhost:7474/graphql/
HTTP/1.1 200 OK
Date: Mon, 24 Oct 2016 21:40:15 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Transfer-Encoding: chunked
Server: Jetty(9.2.9.v20150224)
{"data":{"Person":[{"name":"Michael Sheen","born":1969},{"name":"Jack Nicholson","born":1937},{"name":"Nathan Lane","born":1956},{"name":"Philip Seymour Hoffman","born":1967},{"name":"Noah Wyle","born":1971},{"name":"Rosie O'Donnell","born":1962},{"name":"Greg Kinnear","born":1963},{"name":"Susan Sarandon","born":1946},{"name":"Takeshi Kitano","born":1947},{"name":"Gary Sinise","born":1955},{"name":"John Goodman","born":1960},{"name":"Christina Ricci","born":1980},{"name":"Jay Mohr","born":1970},{"name":"Ben Miles","born":1967},{"name":"Carrie Fisher","born":1956},{"name":"Christopher Guest","born":1948},{"name
...
----
=== Get one Person by name with Parameter
----
curl -u neo4j: -i -XPOST -d'{"query":"query PersonQuery($name:String!) { Person(name:$name) {name,born} }", "variables":{"name":"Kevin Bacon"}}' -H content-type:application/json http://localhost:7474/graphql/
HTTP/1.1 200 OK
Date: Mon, 24 Oct 2016 21:40:38 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Transfer-Encoding: chunked
Server: Jetty(9.2.9.v20150224)
{"data":{"Person":[{"name":"Kevin Bacon","born":1958}]}}
----
=== Get one Person by name literal with related movies
----
curl -u neo4j: -i -XPOST -d'{"query":"query PersonQuery { Person(name:\"Tom Hanks\") {name, born, ACTED_IN_Movie {title, released} } }"}' -H content-type:application/json http://localhost:7474/graphql/
HTTP/1.1 200 OK
Date: Tue, 25 Oct 2016 03:17:08 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Transfer-Encoding: chunked
Server: Jetty(9.2.9.v20150224)
{"data":{"Person":[{"name":"Tom Hanks","born":1956,"ACTED_IN_Movie":[{"title":"Charlie Wilson's War","released":2007},{"title":"A League of Their Own","released":1992},{"title":"The Polar Express","released":2004},{"title":"The Green Mile","released":1999},{"title":"Cast Away","released":2000},{"title":"Apollo 13","released":1995},{"title":"The Da Vinci Code","released":2006},{"title":"Cloud Atlas","released":2012},{"title":"Joe Versus the Volcano","released":1990},{"title":"Sleepless in Seattle","released":1993},{"title":"You've Got Mail","released":1998},{"title":"That Thing You Do","released":1996}]}]}}
----
== Resources
* https://github.com/graphql-java/graphql-java[GraphQL-Java] which we use in this project
* https://github.com/solidsnack/GraphpostgresQL[GraphQL for Postgres] as an inspiration of schema -> native queries
* https://github.com/jhwoodward/neo4j-graphQL[Schema Based GraphQL to Cypher in JavaScript]
* https://github.com/jameskyburz/graphqlcypherquery[GraphQL to Cypher translator (w/o schema)]
** https://github.com/JamesKyburz/graphql2cypher[GraphQL parser to Cypher]
* https://neo4j.com/blog/cypher-graphql-neo4j-3-1-preview/[GraphQL inspired Cypher features] Map projections and Pattern comprehensions
* https://github.com/facebook/dataloader
* https://github.com/skevy/graphiql-app[GraphiQL Electron App]
* http://graphql.org/learn/serving-over-http/[Serving over HTTP]
* https://facebook.github.io/graphq[GraphQL Specification]
////
echo "Authorization: Basic $(echo -n "neo4j:test" | base64)"
"Authorization: Basic bmVvNGo6dGVzdA=="
////
== TODO
* Non-Null and Nullable Input and Output Types
* https://www.reindex.io/docs/graphql-api/connections/#connection-arguments[Pagination]: Skip and Limit (first,last,after,before,skip,limit)
* √ https://www.reindex.io/docs/graphql-api/connections/#orderby[orderBy] with enum _PersonOrdering { name_asc,name_desc,... }
* https://www.reindex.io/docs/graphql-api/connections/#filtering[Filtering] with support of a object argument for an input-argument-field, with key=comparator, and value compare-value +
`(status: {eq/neq:true}, createdAt: { gte: "2016-01-01", lt: "2016-02-01"}, tags: {isNull:false, includes/excludes: "foo"})`
* Handle result aggregation.
* How to handle Geospatial and other complex input types
* √ Support for Directives, e.g. to specify the cypher compiler or runtime? or special handling for certain fields or types
* √ Add `extensions` result value for query statistics or query plan, depending on directives given, e.g. contain the generated cypher query as well
* @skip, @include directives, check if they are handled by the library
* handle nested relationships as optional or non-optional (perhaps via nullable?) or directive
* √ project non-found nested results as null vs. map with null-value entries
* https://facebook.github.io/relay/docs/graphql-connections.html#content[Connection] add support for edges / nodes special properties
* Support 3.1 via pattern comprehensions and map projections