GraphQL @Cypher Query Error

**I have a simple graph of Part nodes and CHILD_OF relationships. There is a property "index" on the "CHILD_OF" relationship that determines the order of the children.

This cypher works like a charm

MATCH p = (:Part { name:"Root" })<-[i:CHILD_OF *0..]-(c:Part)
RETURN REDUCE (path = '1', index IN i | path + '.' + index.index) AS path, c.name as name
ORDER BY path

It returns the following, which is exactly what I am trying to achieve with Graphql. Note that "Sub Assy 1" appears in two places in the tree.

│"path"   │"name"      │	
│"1"      │"Root"      │	
│"1.1"    │"Sub Assy 1"│	
│"1.1.1"  │"Part 1"    │	
│"1.1.2"  │"Part 2"    │	
│"1.2"    │"Sub Assy 2"│	
│"1.2.1"  │"Sub Assy 1"│	
│"1.2.1.1"│"Part 1"    │	
│"1.2.1.2"│"Part 2"    │	
│"1.3"    │"Sub Assy 3"│

Here is the Graphql schema I came up with:

type Part {
  name: String
  children: [Children]
}

type Children @relation(name: "CHILD_OF") {
	from: Part
	to: Part
	_id: ID
	index: String
}

type Tree {
	path: String
	name: String
}

type Query {
 tree(root: String):[Tree]
  @cypher(statement:
   """MATCH p = (:Part {name: $root})	<-[:Children *0..]-(c:Part) with *, relationships(p) as i
   RETURN REDUCE (path = '1', child IN i | path + '.' + child.index) AS path, c.name as name
   ORDER BY path""")
}

This is my Graphql Playground query:

query{tree(root: "Root"){
  path
  name
}}

This is returned:

{
  "errors": [
    {
      "message": "String(\"1\") (of class org.neo4j.values.storable.StringWrappingStringValue)",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "tree"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "code": "Neo.DatabaseError.General.UnknownError",
          "name": "Neo4jError",
          "stacktrace": [
            "Neo4jError: String(\"1\") (of class org.neo4j.values.storable.StringWrappingStringValue)",
            "",
            "    at captureStacktrace (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/result.js:199:15)",
            "    at new Result (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/result.js:65:19)",
            "    at _newRunResult (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/transaction.js:354:10)",
            "    at Object.run (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/transaction.js:238:14)",
            "    at Transaction.run (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/transaction.js:104:26)",
            "    at /home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/index.js:102:25",
            "    at TransactionExecutor._safeExecuteTransactionWork (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/internal/transaction-executor.js:134:22)",
            "    at TransactionExecutor._executeTransactionInsidePromise (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/internal/transaction-executor.js:122:32)",
            "    at /home/rchevalier/dev/grand-stack/api/node_modules/neo4j-driver/lib/v1/internal/transaction-executor.js:61:15",
            "    at new Promise (<anonymous>)"
          ]
        }
      }
    }
  ],
  "data": {
    "tree": null
  }
}

I tried this query and was able to get all the properties returned without error, but it is not the result that I want.

query{Part{
  _id
  name
  children{
    from{
      index
      _id
    }
  }
}}

{
  "data": {
    "Part": [
      {
        "_id": "10135",
        "name": "Root",
        "children": {
          "from": [
            {
              "index": "1",
              "_id": "10136"
            },
            {
              "index": "2",
              "_id": "10137"
            },
            {
              "index": "3",
              "_id": "10138"
            }
          ]
        }
      },
      {
        "_id": "10136",
        "name": "Sub Assy 1",
        "children": {
          "from": [
            {
              "index": "1",
              "_id": "10139"
            },
            {
              "index": "2",
              "_id": "10140"
            }
          ]
        }
      },
      {
        "_id": "10137",
        "name": "Sub Assy 2",
        "children": {
          "from": [
            {
              "index": "1",
              "_id": "10136"
            }
          ]
        }
      },
      {
        "_id": "10138",
        "name": "Sub Assy 3",
        "children": {
          "from": []
        }
      },
      {
        "_id": "10139",
        "name": "Part 1",
        "children": {
          "from": []
        }
      },
      {
        "_id": "10140",
        "name": "Part 2",
        "children": {
          "from": []
        }
      }
    ]
  }
}

So something is obviously wrong with my Schema and/or tree query. Any suggestions are appreciated.

I believe you still need to specify the relationship in your Part definition like:

children: [Children] @relation(name: "CHILD_OF", direction: "IN")

in order for the schema to explicitly know what you're after. It may also be better to rename the nodes as Parent and Child to make it even more clear.

Thanks for the reply Michael. Unfortunately that did not change the result.

As far as renaming the nodes, any node can both a child and a parent so it wouldn't be meaningful to do that. For example, "Sub Assy 1" is a child of "Root" and a parent of "Part 1" and "Part 2".

No worries. I know you need to specify the direction of the relationship as I've shown you above but I'm just not sure your query type can accept the tree structure. You might return it as a map, and get a JSON object back.

DOH! I think I just figured out what has been staring me in the face for two weeks. My query output is actually not JSON it's just a bunch of strings like so:

│"path"   │"name"      │	
│"1"      │"Root"      │	
│"1.1"    │"Sub Assy 1"│

It needs to look like this:

{
  "data": {
    "tree": [
      {
        "path: "1",
        "name": "Root",
      },
     {
        "path: "1.1",
        "name": "Sub Assy 1",
      },
...
     ]
}

Any ideas on how to do that?
If I'm not mistaken, this has just become a neo4j question

Yea, that was the issue. Here is the query:

type Query {
	tree(root: String):[Tree]
		@cypher(statement:
			"""MATCH p = (:Part {name: $root})	<-[:CHILD_OF *0..]-(c:Part) with *, relationships(p) as i
			with REDUCE (path = '1', index IN i | path + '.' + index.index) AS path, c.name as name
			ORDER BY path
			RETURN { path: path, name: name }"""
		)
}

And the result in GraphQL Playground:

{
  "data": {
    "tree": [
      {
        "path": "1",
        "name": "Root"
      },
      {
        "path": "1.1",
        "name": "Sub Assy 1"
      },
      {
        "path": "1.1.1",
        "name": "Part 1"
      },
      {
        "path": "1.1.2",
        "name": "Part 2"
      },
      {
        "path": "1.2",
        "name": "Sub Assy 2"
      },
      {
        "path": "1.2.1",
        "name": "Sub Assy 1"
      },
      {
        "path": "1.2.1.1",
        "name": "Part 1"
      },
      {
        "path": "1.2.1.2",
        "name": "Part 2"
      },
      {
        "path": "1.3",
        "name": "Sub Assy 3"
      }
    ]
  }
}
1 Like

Thanks all, this saved me a tonne of time.

1 Like