Configurable Cypher REST endpoints
Neo4j server-extension that allows to configure fixed REST-Endpoints for Cypher queries.
You can PUT
cypher queries to an endpoint with a certain url-suffix and then later execute those queries by running
GET
with query parameters, only readonly queriesPOST
with JSON (map, list of maps) payload for parametersPOST
with CSV payload for parameters, with optional batch-size and delimiterVerb: PUT
URL: /cypher-rs/<yourEndpoint>
Headers:
Content-type: plain/text
Body:
<yourCypherRequest>
PUT /cypher-rs/users
Content-type: plain/text
Body:
MATCH (n:User) WHERE n.name={name} RETURN n
--> 201 Location: /cypher-rs/users
PUT /cypher-rs/create-user
Content-type: plain/text
Body:
CREATE (n:Node {name:{name},age:{age},male:{male}})
--> 201 Location: /cypher-rs/create-user
Verb: GET
URL: /cypher-rs/<yourEndpoint>
GET /cypher-rs/users?name=Andres
--> 200
[
{
"name": "Andres",
"age": 21,
"male": true,
"children": [
"Cypher",
"L.",
"N."
]
}
]
GET /cypher-rs/users?name=NotExists
--> 204
Verb: POST
URL: /cypher-rs/<yourEndpoint>
Headers:
Content-type: application/json
Body:
<jsonData>
POST /cypher-rs/users
Content-type: application/json
Body:
{
"name": "Andres"
}
--> 200
[
{
"name": "Andres",
"age": 21,
"male": true,
"children": [
"Cypher",
"L.",
"N."
]
}
]
POST /cypher-rs/users
Content-type: application/json
Body:
{
"name": "NotExists"
}
--> 204
POST /cypher-rs/users
Content-type: application/json
Body:
[
{
"name": "Andres"
},
{
"name": "Peter"
},
{
"name": "NotExists"
}
]
--> 200
[
{
"name": "Andres",
"age": 21,
"male": true,
"children": [
"Cypher",
"L.",
"N."
]
},
{
"name": "Peter",
"age": 32,
"male": true,
"children": [
"Neo4j",
"O.",
"K."
]
},
null
]
Verb: POST
URL: /cypher-rs/<yourEndpoint>
Headers:
Content-type: text/plain
Body:
<csvData>
POST /cypher-rs/create-user
Content-type: text/plain
Body:
name,age,male\nAndres,21,true
--> 200
{
"nodes_created": 1,
"labels_added": 1,
"properties_set": 3,
"rows": 1
}
POST /cypher-rs/create-user?delim=\t&batch=20000
Content-type: text/plain
Body: name\tage\tmale\nAndres\t21\ttrue
--> 200
{
"nodes_created": 1,
"labels_added": 1,
"properties_set": 3,
"rows": 1
}
Verb: DELETE
URL: /cypher-rs/<yourEndpoint>
DELETE /cypher-rs/users
--> 200
Verb: GET
URL: /cypher-rs
GET /cypher-rs
--> 200 ["users","create-user"]
GET /cypher-rs?full=true
--> 200 {"users": "start n=node:node_auto_index(name={name}) return n",
"create-user": "create (n {name:{name},age:{age},male:{male}})"}
Verb: GET
URL: /cypher-rs/<yourEndpoint>/query
GET /cypher-rs/users/query
--> 200 start n=node:node_auto_index(name={name}) return n
GET /cypher-rs/create-users/query
--> 200 create (n {name:{name},age:{age},male:{male}})
single column, single row
[
{
"name": "Andres",
"age": 21,
"male": true,
"children": [
"Cypher",
"L.",
"N."
]
}
]
single column, multiple rows
[
{
"name": "Andres",
"age": 21,
"male": true,
"children": [
"Cypher",
"L.",
"N."
]
},
{
"name": "Peter",
"age": 32,
"male": true,
"children": [
"Neo4j",
"O.",
"K."
]
}
]
multiple columns, single row (column names are keys)
[
{
"user": "Andres",
"friends": [
"Peter",
"Michael"
]
}
]
multiple columns, multiple rows (column names are keys)
[
{
"user": "Andres",
"friends": [
"Peter",
"Michael"
]
},
{
"user": "Michael",
"friends": [
"Peter",
"Andres"
]
},
{
"user": "Peter",
"friends": [
"Andres",
"Michael"
]
}
]
Build with mvn clean install dependency:copy-dependencies
Copy files cp target/cypher-rs-2.1-SNAPSHOT.jar target/dependency/opencsv-2.3.jar 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
There is some magic happening with converting query parameters to cypher parameters, as query-parameters are all strings things that look like a number are converted to numbers and collections (aka multiple query parameters) are converted into lists.
title
curl -i -XPUT -H content-type:text/plain \
-d'MATCH (movie:Movie {title:{title}})
OPTIONAL MATCH (movie)<-[:ACTED_IN]-(actor)
RETURN {title:movie.title, cast: collect(actor.name)} as movie' \
http://localhost:7474/cypher-rs/movie
// use it
curl http://localhost:7474/cypher-rs/movie?title=The%20Matrix
name
curl -i -XPUT -H content-type:text/plain \
-d'MATCH (actor:Person {name:{name}})-[:ACTED_IN*2..2]-(co_actor)
WITH actor.name as name, {name:co_actor.name, count: count(*)} as co_actors
ORDER BY count(*) DESC
RETURN {name:name, co_actors: collect(co_actors)} as result' \
http://localhost:7474/cypher-rs/co-actor
// use it
curl -i http://localhost:7474/cypher-rs/co-actor?name=Keanu%20Reeves
title
and released
MERGE
as "get-or-create" operationcurl -i -XPUT -H content-type:text/plain \
-d'MERGE (movie:Movie {title:{title}}) ON CREATE SET movie.released={released} RETURN movie' \
http://localhost:7474/cypher-rs/create-movie
// use it
curl -i -XPOST -H content-type:application/json -d'{"title":"Forrest Gump","released":1994}' http://localhost:7474/cypher-rs/create-movie
title
, released
for the movies, actors
for the actor namesMERGE
as "get-or-create" operation for both movie and actorscurl -i -XPUT -H content-type:text/plain \
-d'MERGE (movie:Movie {title:{title}}) ON CREATE SET movie.released={released}
FOREACH (name in {actors} | MERGE (actor:Person {name:name}) MERGE (actor)-[:ACTED_IN]->(movie))
RETURN movie' \
http://localhost:7474/cypher-rs/create-movie2
// use it
curl -i -XPOST -H content-type:application/json -d'{"title":"Forrest Gump","released":1994, "actors":["Tom Hanks","Robin Wright","Gary Sinise"]}' http://localhost:7474/cypher-rs/create-movie2
curl http://localhost:7474/cypher-rs/movie?title=Forrest%20Gump
```