Document oriented access
to a graph database
Using a Neo4j Server Extension
Michael Hunger - @mesirii
http://github.com/jexp/cypher-rs
Graphs
Graphs are great
• for modeling highly connected domains
• finding patterns to answer questions
• high performance traversals
Graphs are great
Graphs suck
• for compact access patterns
• use-case specific views
• easy integration in modern webapps
Graphs suck
Documents
Documents are great
• for everything that eats JSON
• especially modern JavaScript based webapps
• as dedicated projections of a domain
• compact representation
Documents are great
{
name:"Michael",
age:39,
children:[„Selina“,“Rana“,"Selma"],
address: { street: „Holbeinstr“, city: „Dresden“}
hobbies: [„Coding“,„Buchbar“,“MUD MorgenGrauen“]
blog: „http://jexp.de/blog“
}
Documents suck
• for different projections besides the main use-case
• for connected data
• for cross-document linking
• for less redundancy / duplication
Documents suck
{
name:"Michael",
age:39,
children:[„Selina“,“Rana“,"Selma"],
address: { street: „Holbeinstr“, city: „Dresden“}
hobbies: [„Coding“,„Buchbar“,“MUD MorgenGrauen“]
blog: „http://jexp.de/blog“
}
„Show people by hobby“
Documents suck
{ _id: 1,
name:“Michael“,
works_at: dbref:2
}
{ _id: 2,
name:“Neo Technology“,
employees: [dbref:1]
}
„Link Employees to Companies“
What can we do?
• Use a document oriented access layer for a graph database
• Use graph queries whose results project into documents
• Make them easily accessible
How can we do that?
• Use Cypher in Neo4j 2.0
• Support for literal maps
• Parameters, including maps
Cypher Maps
Cypher & Maps - Results
MATCH (n:User)
WHERE n.name={name}
RETURN n
// returns a map of the node’s properties
RETURN {id: id(n), labels: labels(n), data : n}
Cypher & Maps - Results (II)
MATCH (n:User)-[:PARENT_OF]->(child)
WHERE n.name={name}
RETURN {name:n.name, children:collect(child.name)}
Cypher & Maps - Params
CREATE (u:User { name: {p}.name, age: {p}.age})
RETURN u
CREATE (u:User {params})
RETURN u
Some Questions
How can we do that?
• Create a RESTful API to
• Provide endpoints that represent on Cypher queries
• Use GET, POST with parameters to execute those endpoints
• Return only JSON documents or lists of those
How do we implement it?
• Add a Neo4j-Server-Extension
• Jersey JAX-RS
• Save queries in graph properties
• Execute queries for reads on GET
• Execute queries for reads and writes on POST
• Support JSON and CSV as payload
• Batch transactional write operations
What does it look like?
What does it look like?
• Examples for
• Managing endpoints
• Querying Endpoints
• Writing to Endpoints
Create Endpoint
PUT /cypher-rs/users
Body: match (n:User) where n.name={name} return n
--> 201 Location: /cypher-rs/users
PUT /cypher-rs/create-user
Body: create (n:Node {name:{name},age:{age}})
--> 201 Location: /cypher-rs/create-user
GET, POST - Reads
GET Request
GET /cypher-rs/users?name=Andres
--> 200
{"name":"Andres",
"age":21,"children":["Cypher","L.","N."]}
GET /cypher-rs/users?name=NotExists
--> 204
POST Request - Read
POST /cypher-rs/users
Content-type: application/json
Body: {"name":"Andres"}
--> 200
{"name":"Andres","age":21,
"children":["Cypher","L.","N."]}
POST Request - Read (II)
POST /cypher-rs/users
Content-type: application/json
Body: [{"name":"Andres"},{"name":"Peter"},{"name":"NotExists"}]
!
--> 200
[{"name":"Andres","age":21,"children":["Cypher","L.","N."]},
{"name":"Peter" ,“age“:32,"children":["Neo4j","O.","K."]},
null]
POST - Write
POST Request - Write JSON
POST /cypher-rs/create-user
Content-type: application/json
Body: {"name":"Andres","age":21}
--> 201
POST Request - Write JSON (II)
POST /cypher-rs/create-user
Content-type: application/json
Body: [{"name":"Andres","age":21},
{"name":"Peter", "age":40},
--> 201
POST Request - Write CSV (III)
POST /cypher-rs/create-user[?batch=20000&delim=,]
Content-type: text/plain
Body: name,agenAndres,21nPeter,40
--> 201
More Management
Delete Endpoint
DELETE /cypher-rs/users
--> 200
List Endpoints
GET /cypher-rs
--> 200
[„create-users“, „users“]
Inspect Endpoint
GET /cypher-rs/users/query
--> 200
match (n:User) where n.name={name} return n
Results
Possible Results
single column, single row!
{"name":"Andres","age":21,"children":["Cypher","L.","N."]}
single column, multiple rows!
[
{"name":"Andres","age":21,"children":["Cypher","L.","N."]},
{"name":"Peter", "age":40,"children":["Neo4j","O.","K."]}
]
Possible Results (II)
multiple columns, single row!
{"user": "Andres", "friends": ["Peter","Michael"]}
multiple columns, multiple rows!
[
{"user": "Andres", "friends": ["Peter","Michael"]},
{"user": "Michael","friends": ["Peter","Andres"]},
{"user": "Peter", "friends": ["Andres","Michael"]}
]
Implementation
Implementation - Init
Implementation - PUT
Implementation - GET
Usage
Build with mvn clean install dependency:copy-dependencies
Copy files
cypher-rs-2.0-SNAPSHOT.jar opencsv-2.3.jar to path/to/server/plugins
Add this line to path/to/server/conf/neo4j-server.properties
org.neo4j.server.thirdparty_jaxrs_classes=org.neo4j.cypher_rs=/cypher-rs
Restart
Next Steps
• Add streaming
• Rule the world

Document Oriented Access to Graphs

  • 1.
    Document oriented access toa graph database Using a Neo4j Server Extension Michael Hunger - @mesirii http://github.com/jexp/cypher-rs
  • 2.
  • 3.
    Graphs are great •for modeling highly connected domains • finding patterns to answer questions • high performance traversals
  • 4.
  • 5.
    Graphs suck • forcompact access patterns • use-case specific views • easy integration in modern webapps
  • 6.
  • 7.
  • 8.
    Documents are great •for everything that eats JSON • especially modern JavaScript based webapps • as dedicated projections of a domain • compact representation
  • 9.
    Documents are great { name:"Michael", age:39, children:[„Selina“,“Rana“,"Selma"], address:{ street: „Holbeinstr“, city: „Dresden“} hobbies: [„Coding“,„Buchbar“,“MUD MorgenGrauen“] blog: „http://jexp.de/blog“ }
  • 10.
    Documents suck • fordifferent projections besides the main use-case • for connected data • for cross-document linking • for less redundancy / duplication
  • 11.
    Documents suck { name:"Michael", age:39, children:[„Selina“,“Rana“,"Selma"], address: {street: „Holbeinstr“, city: „Dresden“} hobbies: [„Coding“,„Buchbar“,“MUD MorgenGrauen“] blog: „http://jexp.de/blog“ } „Show people by hobby“
  • 12.
    Documents suck { _id:1, name:“Michael“, works_at: dbref:2 } { _id: 2, name:“Neo Technology“, employees: [dbref:1] } „Link Employees to Companies“
  • 13.
    What can wedo? • Use a document oriented access layer for a graph database • Use graph queries whose results project into documents • Make them easily accessible
  • 14.
    How can wedo that? • Use Cypher in Neo4j 2.0 • Support for literal maps • Parameters, including maps
  • 15.
  • 16.
    Cypher & Maps- Results MATCH (n:User) WHERE n.name={name} RETURN n // returns a map of the node’s properties RETURN {id: id(n), labels: labels(n), data : n}
  • 17.
    Cypher & Maps- Results (II) MATCH (n:User)-[:PARENT_OF]->(child) WHERE n.name={name} RETURN {name:n.name, children:collect(child.name)}
  • 18.
    Cypher & Maps- Params CREATE (u:User { name: {p}.name, age: {p}.age}) RETURN u CREATE (u:User {params}) RETURN u
  • 19.
  • 20.
    How can wedo that? • Create a RESTful API to • Provide endpoints that represent on Cypher queries • Use GET, POST with parameters to execute those endpoints • Return only JSON documents or lists of those
  • 21.
    How do weimplement it? • Add a Neo4j-Server-Extension • Jersey JAX-RS • Save queries in graph properties • Execute queries for reads on GET • Execute queries for reads and writes on POST • Support JSON and CSV as payload • Batch transactional write operations
  • 22.
    What does itlook like?
  • 23.
    What does itlook like? • Examples for • Managing endpoints • Querying Endpoints • Writing to Endpoints
  • 24.
    Create Endpoint PUT /cypher-rs/users Body:match (n:User) where n.name={name} return n --> 201 Location: /cypher-rs/users PUT /cypher-rs/create-user Body: create (n:Node {name:{name},age:{age}}) --> 201 Location: /cypher-rs/create-user
  • 25.
  • 26.
    GET Request GET /cypher-rs/users?name=Andres -->200 {"name":"Andres", "age":21,"children":["Cypher","L.","N."]} GET /cypher-rs/users?name=NotExists --> 204
  • 27.
    POST Request -Read POST /cypher-rs/users Content-type: application/json Body: {"name":"Andres"} --> 200 {"name":"Andres","age":21, "children":["Cypher","L.","N."]}
  • 28.
    POST Request -Read (II) POST /cypher-rs/users Content-type: application/json Body: [{"name":"Andres"},{"name":"Peter"},{"name":"NotExists"}] ! --> 200 [{"name":"Andres","age":21,"children":["Cypher","L.","N."]}, {"name":"Peter" ,“age“:32,"children":["Neo4j","O.","K."]}, null]
  • 29.
  • 30.
    POST Request -Write JSON POST /cypher-rs/create-user Content-type: application/json Body: {"name":"Andres","age":21} --> 201
  • 31.
    POST Request -Write JSON (II) POST /cypher-rs/create-user Content-type: application/json Body: [{"name":"Andres","age":21}, {"name":"Peter", "age":40}, --> 201
  • 32.
    POST Request -Write CSV (III) POST /cypher-rs/create-user[?batch=20000&delim=,] Content-type: text/plain Body: name,agenAndres,21nPeter,40 --> 201
  • 33.
  • 34.
  • 35.
    List Endpoints GET /cypher-rs -->200 [„create-users“, „users“]
  • 36.
    Inspect Endpoint GET /cypher-rs/users/query -->200 match (n:User) where n.name={name} return n
  • 37.
  • 38.
    Possible Results single column,single row! {"name":"Andres","age":21,"children":["Cypher","L.","N."]} single column, multiple rows! [ {"name":"Andres","age":21,"children":["Cypher","L.","N."]}, {"name":"Peter", "age":40,"children":["Neo4j","O.","K."]} ]
  • 39.
    Possible Results (II) multiplecolumns, single row! {"user": "Andres", "friends": ["Peter","Michael"]} multiple columns, multiple rows! [ {"user": "Andres", "friends": ["Peter","Michael"]}, {"user": "Michael","friends": ["Peter","Andres"]}, {"user": "Peter", "friends": ["Andres","Michael"]} ]
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
    Usage Build with mvnclean install dependency:copy-dependencies Copy files cypher-rs-2.0-SNAPSHOT.jar opencsv-2.3.jar to path/to/server/plugins Add this line to path/to/server/conf/neo4j-server.properties org.neo4j.server.thirdparty_jaxrs_classes=org.neo4j.cypher_rs=/cypher-rs Restart
  • 45.
    Next Steps • Addstreaming • Rule the world