Reputation: 19829
I want to define my Neo4j queries in terms of a generic structure (much like GraphQL) and compose them. For example, here's a simple query with no composition:
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true }}
My Neo4j interpreter may look like this:
chatrooms = ({query, fields}) ->
"""
MATCH (c:CHATROOMS)
RETURN #{R.join(' ', R.map(R.concat('c.'), R.keys(fields)))}
LIMIT #{query.limit}
"""
But I run into trouble when I want to compose more deeply nested queries. For example, what if I want some information about the owner of each chatroom as well? Rather than spend two round-trip HTTP requests, I should be able to query this all at once.
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: {
fields: {
id: true,
name: true}}}}
At this point, I get a little hung up. I don't know how to make the interpreter generic enough to handle these cases. I know the query should look something like this.
MATCH (c:CHATROOM)
MATCH (c)<-[:OWNS]-(u:USER)
RETURN c.id, c.name, u.id, u.name
LIMIT 10
Ideally, this query would return something with a similar structure like this:
[
{id: 1, name:'neo4j', owner: {id: 99, name: 'michael'}}
{id: 2, name:'meteor', owner: {id: 100, name: 'chet'}}
]
That would make composition and interpretation of the results much easier, but thats a detail for later.
Lastly, I'm having trouble nesting these queries even deeper. For example, what if I want some messages for each chatroom as well? And what if I want some information about the owner of each chatroom?
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: {
fields: {
id: true,
name: true}}
messages: {
query: {limit: 20}
fields: {
id: true,
text: true,
createdAt: true,
owner: {
fields: {
id: true,
name: true }}}}}}
Here's my go at it -- although I'm quite sure its totally wrong.
MATCH (c:CHATROOM)
MATCH (c)<-[:OWNS]-(u:USER)
UNWIND c as room
MATCH (room)-[:OWNS]->(m:MESSAGE)
MATCH (m)<-[:OWNS]-(x:USER)
RETURN collect(m.id, m.text, m.creatdAt, x.id, x.name)
LIMIT 20
RETURN c.id, c.name, u.id, u.name,
LIMIT 10
Anyways, the goal is to be able to specify one giant nested query and be able to run it all in a single HTTP request. Maybe some parsing of the output will be necessary, but ideally, I will get a comparable data-structure as output as well.
Any ideas?
Upvotes: 3
Views: 140
Reputation: 41676
You can use a sequence and combination of collect({key:value})
or {key:collect(value)
.
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
RETURN {id: c.id, name: c.name, owner: collect({id: u.id, name: u.name})} AS data
LIMIT 10
then with this data:
create (c:CHATROOM {id:1, name:"neo4j"})<-[:OWNS]-(u:USER {id:99,name:"Michael")
create (c:CHATROOM {id:2, name:"meteor"})<-[:OWNS]-(u:USER {id:100,name:"Chet")
running the query you get
+--------------------------------------------------------------------+
| data |
+--------------------------------------------------------------------+
| {id=2, name=meteor, owner=[{id=100, name=Chet}]} |
| {id=1, name=neo4j, owner=[{id=99, name=Michael}]} |
+--------------------------------------------------------------------+
live here: http://console.neo4j.org/r/d41luc
To combine both rows into one you would use WITH
and then RETURN
but then you wouldn't get the benefit of streaming your data back.
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
WITH {id: c.id, name: c.name, owner: collect({id: u.id, name: u.name})} AS row
RETURN collect(row) as data
LIMIT 10
Please use more descriptive relationship-types instead of just :OWNS
everywhere.
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
MATCH (c)-[:OWNS]->(m:MESSAGE)<-[:OWNS]-(x:USER)
WITH u,c,collect({id: m.id, text: m.text, created_at: m.createdAt, author_id: x.id, author: x.name}) as messages[0..20]
RETURN collect({ id: c.id, room: c.name,
owner_id: u.id, owner: u.name,
messages: messages}) as rooms
LIMIT 10
Upvotes: 1
Reputation: 45013
First it's agains the GraphQL philosophy to have nested structures.
You should use references instead.
e.g.
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: ${owner.id}
messages: {
query: {limit: 20}
fields: {
id: true,
text: true,
createdAt: true,
owner: ${owner.id}
}
}
}
owners: [{id: 1, fields: { id: true, name: true}}]
Why are you using fields property instead of fields at root object?
Upvotes: 1