简体   繁体   中英

How to compose queries in Neo4j?

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?

First it's agains the GraphQL philosophy to have nested structures.

You should use references instead.

eg

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?

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

Update

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM