Skip to content

Commit 0275502

Browse files
committed
Prepare for Release 1.0.0-M02
1 parent f94a3ce commit 0275502

File tree

2 files changed

+190
-22
lines changed

2 files changed

+190
-22
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<artifactId>neo4j-graphql-java</artifactId>
99
<name>Neo4j GraphQL Java</name>
1010
<description>GraphQL to Cypher Mapping</description>
11-
<version>1.0.0-M01</version>
11+
<version>1.0.0-M02</version>
1212
<url>http://github.com/neo4j-contrib/neo4j-tinkerpop-api</url>
1313

1414
<licenses>
@@ -24,7 +24,7 @@
2424
<java.version>1.8</java.version>
2525
<kotlin.version>1.3.21</kotlin.version>
2626
<kotlin.compiler.jvmTarget>${java.version}</kotlin.compiler.jvmTarget>
27-
<neo4j.version>3.4.1</neo4j.version>
27+
<neo4j.version>3.5.3</neo4j.version>
2828
<driver.version>1.7.2</driver.version>
2929
</properties>
3030

readme.adoc

Lines changed: 188 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
= JVM Library to translate GraphQL queries and mutations to Neo4j's Cypher
22

3-
This is an early stage alpha implementation written in Kotlin.
3+
This is a beta GraphQL transpiler written in Kotlin.
44

55
== How does it work
66

@@ -13,28 +13,30 @@ Those Cypher queries can then executed, e.g via the Neo4j-Java-Driver (or other
1313

1414
The request, result and error handling is not part of this library, but we provide demo programs on how to use it in different languages.
1515

16-
NOTE: All the supported features are listed below, detailed docs will be added.
16+
NOTE: All the <<features|supported features>> are listed and explained below, more detailed docs will be added in time.
1717

1818
== FAQ
1919

2020
=== How does this relate to the other neo4j graphql libraries?
2121

22-
Similar to `neo4j-graphql-js` this library focusses on query translation, just for the *JVM* instead of Node.js.
23-
It does not provide a server or other facilities but is meant to be used as a dependency included for a single purpose.
22+
Similar to `neo4j-graphql-js` this library focuses on query translation, just for the *JVM* instead of Node.js.
23+
It does not provide a server (except as examples) or other facilities but is meant to be used as a dependency included for a single purpose.
2424

2525
If this library is feature complete we plan to replace the code in the current Neo4j server plugin `neo4j-graphql` with a single call to this library.
2626
The server plugin would still handle request-response and error-handling, and perhaps some schema management but be slimmed down to a tiny piece.
2727

2828
=== How does this related to graphql-java
2929

30-
This library uses GraphQL Java under the hood for parsing of schema and queries, and managing the GraphQL state and context.
31-
But not for nested field resolvers or data fetching.
30+
This library uses `graphql-java` under the hood for parsing of schema and queries, and managing the GraphQL state and context.
31+
But *not for nested field resolvers or data fetching*.
3232

33-
If you wanted to you could combine graphql-java resolvers with this library to have a part of your schema handled by Neo4j.
33+
If you wanted, you could combine `graphql-java` resolvers with this library to have a part of your schema handled by Neo4j.
34+
35+
Thanks a lot to the maintainers of `graphql-java` for the awesome library.
3436

3537
== Usage
3638

37-
You can use the library as dependency: `org.neo4j:neo4j-graphql-java:1.0.0-M01` in any JVM program.
39+
You can use the library as dependency: `org.neo4j:neo4j-graphql-java:1.0.0-M02` in any JVM program.
3840

3941
The basic usage should be:
4042

@@ -43,9 +45,10 @@ The basic usage should be:
4345
val schema =
4446
"""
4547
type Person {
46-
name: String
48+
name: ID!
4749
age: Int
4850
}
51+
# Optional if you use generated queries
4952
type Query {
5053
person : [Person]
5154
personByName(name:String) : Person
@@ -56,14 +59,19 @@ val query = """ { p:personByName(name:"Joe") { age } } """
5659
val schema = SchemaBuilder.buildSchema(idl)
5760
val (cypher, params) = Translator(schema).translate(query, params)
5861
62+
// generated Cypher
5963
cypher == "MATCH (p:Person) WHERE p.name = $pName RETURN p {.age} as p"
6064
----
6165

66+
You find more usage examples in the link:src/test/resources[TCK scripts].
67+
6268
== Demo
6369

6470
Here is a minimalistic example in Groovy using the Neo4j-Java driver and Spark-Java as webserver.
6571
It is running against a Neo4j instance at `bolt://localhost` (username: `neo4j`, password: `password`) containing the `:play movies` graph.
6672

73+
(You can also use a link:src/test/kotlin/GraphQLServer.kt[Kotlin based server example].)
74+
6775
[source,groovy]
6876
----
6977
// Simplistic GraphQL Server using SparkJava
@@ -82,12 +90,12 @@ import org.neo4j.driver.v1.*
8290
8391
schema = """
8492
type Person {
85-
name: String
93+
name: ID!
8694
born: Int
8795
actedIn: [Movie] @relation(name:"ACTED_IN")
8896
}
8997
type Movie {
90-
title: String
98+
title: ID!
9199
released: Int
92100
tagline: String
93101
}
@@ -123,12 +131,12 @@ It uses a schema of:
123131
[source,graphql]
124132
----
125133
type Person {
126-
name: String
134+
name: ID!
127135
born: Int
128136
actedIn: [Movie] @relation(name:"ACTED_IN")
129137
}
130138
type Movie {
131-
title: String
139+
title: ID!
132140
released: Int
133141
tagline: String
134142
}
@@ -160,8 +168,11 @@ You can also test it with `curl`
160168
curl -XPOST http://localhost:4567/graphql -d'{"query":"{person {name}}"}'
161169
----
162170

171+
This example doesn't handle introspection queries but the one in the test directory does.
172+
163173
== Advanced Queries
164174

175+
.Filter, Sorting, Paging support
165176
----
166177
{
167178
person(filter: {name_starts_with: "L"}, orderBy: "born_asc", first: 5, offset: 2) {
@@ -187,6 +198,7 @@ curl -XPOST http://localhost:4567/graphql -d'{"query":"{person {name}}"}'
187198
}
188199
----
189200

201+
[[features]]
190202
== Features
191203

192204
=== Current
@@ -195,15 +207,17 @@ curl -XPOST http://localhost:4567/graphql -d'{"query":"{person {name}}"}'
195207
* resolve query fields via result types
196208
* handle arguments as equality comparisons for top level and nested fields
197209
* handle relationships via @relation directive on schema fields
210+
* @relation directive on types for rich relationships (from, to fields for start & end node)
198211
* handle first, offset arguments
199212
* argument types: string, int, float, array
200-
* parameter support
201-
* parametrization
213+
* request parameter support
214+
* parametrization for cypher query
202215
* aliases
203216
* inline and named fragments
204-
* auto-generate queries
205-
* @cypher for fields
206-
* auto-generate mutations
217+
* auto-generate query fields for all objects
218+
* @cypher directive for fields to compute field values, support arguments
219+
* auto-generate mutation fields for all objects to create, update, delete
220+
* @cypher directive for top level queries and mutations, supports arguments
207221

208222
=== Next
209223

@@ -219,8 +233,12 @@ curl -XPOST http://localhost:4567/graphql -d'{"query":"{person {name}}"}'
219233

220234
=== Parse SDL schema
221235

222-
Currently schemas with object types, enums and Query types are parsed and handled.
236+
Currently schemas with object types, enums, fragments and Query types are parsed and handled.
237+
We support @relation directives on fields and types for rich relationships
238+
We support @cypher directives on fields and top-level query and mutation fields.
239+
The configurable augmentation auto-generates queries and mutations (create,update,delete) for all types.
223240
It supports the built-in scalars for GraphQL.
241+
For arguments we support input types in many places and filters as known from GraphCool/Prisma.
224242

225243
=== Resolve query Fields via Result Types
226244

@@ -413,6 +431,7 @@ person(name:"Keanu Reeves") {
413431
}
414432
----
415433

434+
[[filters]]
416435
=== Filters
417436

418437
Filters are a powerful way of selecting a subset of data.
@@ -442,4 +461,153 @@ You can also apply nested filter on relations, which use suffixes like `("",not,
442461
}
443462
----
444463

445-
NOTE: Those nested input types are not yet generated, we use leniency in the parser.
464+
NOTE: Those nested input types are not yet generated, we use leniency in the parser.
465+
466+
=== Inline and Named Fragments
467+
468+
We support inline and named fragments according to the GraphQL spec.
469+
Most of this is resolved on the parser/query side.
470+
471+
.Named Fragment
472+
[source,graphql]
473+
----
474+
fragment details on Person { name, email, dob }
475+
query {
476+
person {
477+
...details
478+
}
479+
}
480+
----
481+
482+
.Inline Fragment
483+
[source,graphql]
484+
----
485+
query {
486+
person {
487+
... on Person { name, email, dob }
488+
}
489+
}
490+
----
491+
492+
493+
=== @cypher Directives
494+
495+
With `@cypher` directives you can add the power of Cypher to your GraphQL API.
496+
It allows you, without code to compute field values using complex queries.
497+
You can also write your own, custom top-level queries and mutations using Cypher.
498+
499+
Arguments on the field are passed to the Cypher statement as parameters.
500+
Input types are supported, they appear as `Map` type in your Cypher statement.
501+
502+
NOTE: Those Cypher directive queries are only included in the generated Cypher statement if the field or query is included in the GraphQL query.
503+
504+
==== On Fields
505+
506+
.@cypher directive on a field
507+
[source,graphql]
508+
----
509+
type Movie {
510+
title: String
511+
released: Int
512+
similar(limit:Int=10): [Movie] @cypher(statement:"
513+
MATCH (this)-->(:Genre)<--(sim)
514+
WITH sim, count(*) as c ORDER BY c DESC LIMIT $limit
515+
RETURN sim")
516+
}
517+
----
518+
519+
Here the `this` variable is bound to the current movie and you can use it to navigate the graph and collect data.
520+
The `limit` variable is passed to the query as parameter.
521+
522+
==== On Queries
523+
524+
Similarly you can use the `@cypher` directive with a top level query.
525+
526+
.@cypher directive on query
527+
[source,graphql]
528+
----
529+
type Query {
530+
person(name:String) Person @cypher("MATCH (p:Person) WHERE p.name = $name RETURN p")
531+
}
532+
----
533+
534+
Of course you can also return arrays from your query, the statements on query fields should be read-only queries.
535+
536+
==== On Mutations
537+
538+
You can do the same for mutations, just with updating Cypher statements.
539+
540+
.@cypher directive on mutation
541+
[source,graphql]
542+
----
543+
type Mutation {
544+
createPerson(name:String, age:Int) Person @cypher("CREATE (p:Person) SET p.name = $name, p.age = $age RETURN p")
545+
}
546+
----
547+
548+
You can use more complex statements for creating these entities or even subgraphs.
549+
550+
NOTE: The common CRUD mutations and queries are auto-generated, see below.
551+
552+
=== Auto Generate Queries and Mutations
553+
554+
To reduce the amount of boilerplate code a user has to write we auto-generate top-level CRUD queries and mutations for all types.
555+
556+
This is configurable via the API, you can:
557+
558+
* disable auto-generation (for mutations/queries)
559+
* disable it per type
560+
* disable mutations per operation (create,delete,update)
561+
562+
For a schema like this:
563+
564+
[source,graphql]
565+
----
566+
type Person {
567+
id:ID!
568+
name: String
569+
age: Int
570+
}
571+
----
572+
573+
574+
It would auto-generate quite a lot of things:
575+
576+
* a query: `person(id:ID, name:String , age: Int, _id: Int, filter:_PersonFilter, orderBy:_PersonOrdering, first:Int, offset:Int) : [Person]`
577+
* a `_PersonOrdering` enum, for the `orderBy` argument with all fields for `_asc` and `_desc` sort order
578+
* a `_PersonInput` for creating Person objects
579+
* a `_PersonFilter` for the `filter` argument, which is a deeply nested input object (see <<filters>>)
580+
* mutations for:
581+
** createPerson: `createPerson(id:ID!, name:String, age: Int) : Person`
582+
** updatePerson: `updatePerson(id:ID!, name:String, age:Int) : Person`
583+
** deletePerson: `deletePerson(id:ID!) : Person`
584+
585+
////
586+
** addPersonMovie
587+
** deletePersonMovie
588+
////
589+
590+
You can then use those in your GraphQL queries like this:
591+
592+
[source,graphql]
593+
----
594+
query { person(age:42, orderBy:name_asc) {
595+
id
596+
name
597+
age
598+
}
599+
----
600+
601+
or
602+
603+
604+
[source,graphql]
605+
----
606+
mutation {
607+
createPerson(id: "34920n9qw0", name:"Jane Doe", age:42) {
608+
id
609+
name
610+
age
611+
}
612+
}
613+
----

0 commit comments

Comments
 (0)