Fetching nested data with NEO4J in Family Tree - neo4j

First of all, I'm new to NEO4J and to CYPHER. So I'm twerking here and there to figure out to get the result I want.
Below is my graph. Let's say it's a simple family tree.
I have come up with this simple cypher query to fetch the direct descendants of the node
MATCH (p:Person {username: "SETHLORDM"})<-[r:CHILD_OF]-(p2)
RETURN {current: p, children: collect(p2)}
and the text version of the result is as below
The above is okay, but I want to get the text result as follows if it's doable with NEO4J.
[
{
"username": "SETHLORDM",
"location": "NO_LOCATION",
"children": [
{
"username": "TESTNODE_1",
"location": "LEFT",
"children": [
{
"username": "TESTNODE_3",
"location": "LEFT",
"children": []
},
{
"username": "TESTNODE_4",
"location": "RIGHT",
"children": []
}
],
},
{
"username": "TESTNODE_2",
"location": "RIGHT",
"children": [
{
"username": "TESTNODE_5",
"location": "RIGHT",
"children": []],
},
{
"username": "TESTNODE_6",
"location": "RIGHT",
"children": []],
}
],
}
],
}
]
Any help regarding this would be highly appreciated. Thank you

One way to approach it is using apoc.convert.toTree (using the plugin apoc).
This can create the tree structure that you are looking for. But, since your tree is bottom-up, the result will be same, meaning each node will point its parent. If you want to get the results as you want, using this method, you will have to change your relations.
For example, using this data:
MERGE (a:Person{key: 1, username: "SETHLORDM"})
MERGE (b:Person{key: 2})
MERGE (c:Person{key: 3})
MERGE (d:Person{key: 4})
MERGE (e:Person{key: 5})
MERGE (f:Person{key: 6})
MERGE (g:Person{key: 7})
MERGE (b)-[:CHILD_OF]-(a)
MERGE (c)-[:CHILD_OF]-(a)
MERGE (d)-[:CHILD_OF]-(b)
MERGE (e)-[:CHILD_OF]-(b)
MERGE (f)-[:CHILD_OF]-(c)
MERGE (g)-[:CHILD_OF]-(c)
and this query:
MATCH path = (p:Person {username: "SETHLORDM"})<-[r:CHILD_OF*..2]-(p2)
WITH collect(path) AS paths
CALL apoc.convert.toTree(paths)
YIELD value
RETURN value;
will give this result:
"_type": "Person",
"child_of": [
{
"_type": "Person",
"child_of": [
{
"_type": "Person",
"_id": 243,
"key": 5
},
{
"_type": "Person",
"_id": 242,
"key": 4
}
],
"_id": 240,
"key": 2
},
{
"_type": "Person",
"child_of": [
{
"_type": "Person",
"_id": 245,
"key": 7
},
{
"_type": "Person",
"_id": 244,
"key": 6
}
],
"_id": 241,
"key": 3
}
],
"_id": 239,
"key": 1,
"username": "SETHLORDM"
}
But changing the links to this:
MERGE (a)-[:CHILDREN]-(b)
MERGE (a)-[:CHILDREN]-(c)
MERGE (b)-[:CHILDREN]-(d)
MERGE (b)-[:CHILDREN]-(e)
MERGE (c)-[:CHILDREN]-(f)
MERGE (c)-[:CHILDREN]-(g)
And adjusting the query to:
MATCH path = (p:Person {username: "SETHLORDM"})-[r:CHILDREN*..2]->(p2)
WITH collect(path) AS paths
CALL apoc.convert.toTree(paths)
YIELD value
RETURN value;
Will provide:
{
"_type": "Person",
"_id": 246,
"children": [
{
"_type": "Person",
"_id": 247,
"children": [
{
"_type": "Person",
"_id": 249,
"key": 4
},
{
"_type": "Person",
"_id": 250,
"key": 5
}
],
"key": 2
},
{
"_type": "Person",
"_id": 248,
"children": [
{
"_type": "Person",
"_id": 252,
"key": 7
},
{
"_type": "Person",
"_id": 251,
"key": 6
}
],
"key": 3
}
],
"key": 1,
"username": "SETHLORDM"
}
Which is now similar to what you wanted...
Bonus: if you are using apoc, you can replace the MATCH query by apoc.path.expandConfig which should be more efficient to larger graphs.

Related

cypher query to generate special path

I have a graph like this
O
|
A
|
B
/ \
C E
/ \
D F
I want to find the path from O to F. node F can contain an attribute call "non-direct". If this attribute is set false, then the path is
O-A-B-E-F
However, if it is set to true, the path is
O-A-B-C-D-C-B-E-F
I.e., the path first has to reach B which is a terminal node and then go back to the common parent between D and F, then walk to F.
How can I create a query to return these two path based on the attribute value?
Since Neo4j will not traverse back over the same relationship, you may need to create relationships in both direction on the branches. So something like this:
In addition, I would add some properties that indicate the toplogy, so for instance add a property on-branch for the nodes C and D.
Then you can run queries like this:
MATCH (f:Thing {name:'F'}), (o:Thing {name:'O'}), (d:Thing {name:'D'})
MATCH p = (o)-[:NEXT*]->(f)
WHERE ANY(node IN nodes(p) WHERE node.`on-branch`) = f.`non-direct`
AND (d IN nodes(p)) = f.`non-direct`
RETURN REDUCE(s='', node IN nodes(p) | s+ ' '+ node.name) AS nodes
The graph I tried this on can be created from this json:
{
"nodes": [
{
"id": 0,
"labels": [
"Thing"
],
"properties": {
"name": "O",
"on-branch": false
}
},
{
"id": 1,
"labels": [
"Thing"
],
"properties": {
"name": "A",
"on-branch": false
}
},
{
"id": 2,
"labels": [
"Thing"
],
"properties": {
"name": "B",
"on-branch": false
}
},
{
"id": 3,
"labels": [
"Thing"
],
"properties": {
"name": "C",
"on-branch": true
}
},
{
"id": 4,
"labels": [
"Thing"
],
"properties": {
"name": "D",
"on-branch": true
}
},
{
"id": 5,
"labels": [
"Thing"
],
"properties": {
"name": "E",
"on-branch": false
}
},
{
"id": 6,
"labels": [
"Thing"
],
"properties": {
"name": "F",
"non-direct": false,
"on-branch": false
}
}
],
"relations": [
{
"id": 0,
"source": 0,
"target": 1,
"type": "NEXT",
"properties": {}
},
{
"id": 1,
"source": 1,
"target": 2,
"type": "NEXT",
"properties": {}
},
{
"id": 2,
"source": 3,
"target": 2,
"type": "NEXT",
"properties": {}
},
{
"id": 3,
"source": 2,
"target": 3,
"type": "NEXT",
"properties": {}
},
{
"id": 4,
"source": 4,
"target": 3,
"type": "NEXT",
"properties": {}
},
{
"id": 5,
"source": 3,
"target": 4,
"type": "NEXT",
"properties": {}
},
{
"id": 6,
"source": 2,
"target": 5,
"type": "NEXT",
"properties": {}
},
{
"id": 7,
"source": 5,
"target": 6,
"type": "NEXT",
"properties": {}
}
]
}
The json above can be loaded with this cypher (in Neo4j with apoc):
CALL apoc.load.json([ url to the json]) YIELD value AS v
UNWIND v.nodes AS node
CALL apoc.merge.node(
node.labels,
{ _tempID : node.id},
node.properties,
{}
) YIELD node AS n
WITH DISTINCT v
UNWIND v.relations AS rel
MATCH (from {_tempID: rel.source})
MATCH (to {_tempID: rel.target})
CALL apoc.merge.relationship(
from,
rel.type,
{},
rel.properties,
to,
{}
) YIELD rel AS r
WITH DISTINCT v
MATCH (n) WHERE EXISTS(n._tempID)
REMOVE n._tempID

How to get a hierarchical list of nodes and relations as json in Neo4j

I have a neo4j DB in which user data and relations between them would be stored, the end user will interact with this data from a mobile app (app is in Flutter, we use a nestjs neo4j connector in between). Now we have to enable offline access to data. So the idea was to export the data of the user from neo4j as json and use it when offline, when the device gets online we will make the changes to the DB. I have some problem getting the data as json
This is a rough sample what I am trying to do
The cypher commands to create these nodes
CREATE (c:Computer {name: 'Andy',uid:'123'})
CREATE (d1:Drive {name: 'Drive1',capacity:"2gb",uid:'223'})
CREATE (d2:Drive {name: 'Drive2',capacity:"4gb",uid:'233'})
CREATE (f1:Folder {name: 'desktop',type:"special",uid:'323'})
CREATE (f2:Folder {name: 'mydocuments',type:"special",uid:'333'})
CREATE (f3:Folder {name: 'myprojects',type:"normal",uid:'343'})
CREATE (t1:File {name: 'text1',type:"txt",size:"1kb",uid:'423'})
CREATE (t2:File {name: 'text2',type:"txt",size:"1.5kb",uid:'433'})
CREATE (t3:File {name: 'text3',type:"txt",size:"2kb",uid:'443'})
CREATE (do1:File {name: 'doc1',type:"doc",size:"1mb",uid:'523'})
CREATE (do2:File {name: 'doc2',type:"doc",size:"1.5mb",uid:'533'})
CREATE (do3:File {name: 'doc3',type:"doc",size:"2mb",uid:'543'})
CREATE (c)-[r1:PARTITION{during: 'osinstall'}]->(d1)
CREATE (c)-[r2:PARTITION{during: 'setup'}]->(d2)
CREATE (d1)-[r3:AutoCreated{during: 'osinstall',type:"folder"}]->(f1)
CREATE (d1)-[r4:AutoCreated{during: 'osinstall',type:"folder"}]->(f2)
CREATE (f1)-[r5:Shortcut{type:"folder"}]->(c)
CREATE (f2)-[r6:Shortcut{type:"folder"}]->(c)
CREATE (d2)-[r7:UserCreated{type:"folder"}]->(f3)
CREATE (d2)-[r8:UserCreated{type:"file"}]->(t1)
CREATE (d2)-[r9:UserCreated{type:"file"}]->(t2)
CREATE (f3)-[r10:UserCreated{type:"file"}]->(t3)
CREATE (f3)-[r11:UserCreated{type:"file"}]->(do1)
CREATE (d2)-[r12:UserCreated{type:"file"}]->(do2)
CREATE (do2)-[r13:Shortcut{type:"file"}]->(f1)
CREATE (f3)-[r14:Shortcut{type:"folder"}]->(f1)
CREATE (f1)-[r15:UserCreated{type:"file"}]->(do3)
CREATE (do3)-[r16:Shortcut{type:"file"}]->(f3)
CREATE (c1:Computer {name: 'Randy',uid:'c1-123'})
CREATE (c1d1:Drive {name: 'Drive1',capacity:"1gb",uid:'c1-223'})
CREATE (c1t1:File {name: 'text1',type:"txt",size:"1kb",uid:'c1-423'})
CREATE (c1t2:File {name: 'text2',type:"txt",size:"1.5kb",uid:'c1-433'})
CREATE (c1sh1:SharedDrive {name:"SharedDrive",uid:'c1-s1'})
CREATE (c1)-[c1r1:PARTITION{during: 'osinstall'}]->(c1d1)
CREATE (c1d1)-[c1r2:UserCreated{type:"file"}]->(c1t1)
CREATE (c1d1)-[c1r3:UserCreated{type:"file"}]->(c1t2)
CREATE (c1t1)-[c1r4:Share{type:"file"}]->(c1sh1)
CREATE (c1)-[c1r5:SHAREDPARTITION{during: 'osinstall'}]->(c1sh1)
CREATE (c1)-[common:Network]->(c)
I want to query a root node(say Andy) with the users uid and get the data in the format
{
"name": "Andy",
"uid":"123",
"PARTITION":[
{
"name": "Drive1",
"capacity":"2gb",
"uid":"223",
"Folder":[
{ "name": "desktop","type":"special","uid":"323",
"File":[
"... Detail about file doc3 here.."
],
"Shortcut":[
"... Detail about file doc2 here.."
]
},
{"name": "mydocuments","type":"special","uid":"333"}
]
},
{
"name": "Drive2",
"capacity":"4gb",
"uid":"233",
"Folder":[
{ "name": "myprojects","type":"normal","uid":"343",
"File":[
"...Detail about Files doc1, text3 here..."
],
"Shortcut":[
"... Detail about file doc3 here.."
]
}
],
"File":[
"...Detail about Files text1,text2,doc 2 here..."
]
}
],
"Shortcut":[
{
"name": "desktop","type":"special","uid":"323"
},{
"name": "mydocuments","type":"special","uid":"333"
}
],
"Network":[
{
"name": "Randy",
"uid":"c1-123",
"SHAREDPARTITION":["...HERE ONLY NEED THE files and folders from shareddrive other drives should not show up..."]
}
]
}
I want to add the relations from and to the node as key and for the value add a list of nodes(with their related properties) connected to it and move to the next one. I don't know how to do so. So far I have tried
match (n:Computer{uid:"123"})-[r:PARTITION]->(x)
match b=(x)-[*]->(y)
with collect(b) as c
call apoc.convert.toTree(c) yield value
return value
but this does not return the shortcut file paths properly ie., if I add a shortcut from doc3(at desktop) to myproject, I don't find the file detail with myproject shortcuts I need it at both places at desktop(under files) and myproject(under shortcut) folder. Also the shared computer details are not fetched(all drives must not be fetched just the sharedpartition). Apart from this the return data is not in the expected format and I have to process it in the app after fetching it.
Can someone help me with this?
I am also open to different solutions for neo4j flutter offline.
You can create a connection from the root node then collect them all together. Just make sure you are using the relationship that you want to extract. Below is not exactly you described but closest to your json format.
match b=(n:Computer{uid:"123"})-[r:PARTITION]->(x:Drive)-[]-(y:Folder)-[]-(z:File)
match c=(n)-[:Shortcut]-()
match d=(n)-[:Network]-()-[:SHAREDPARTITION]-()
with collect(b) + collect(c) + collect(d) as t
call apoc.convert.toTree(t) yield value
return value
Result:
{
"name": "Andy",
"uid": "123",
"_type": "Computer",
"_id": 298,
"partition": [
{
"uid": "233",
"_type": "Drive",
"name": "Drive2",
"_id": 300,
"partition.during": "setup",
"capacity": "4gb",
"usercreated": [
{
"uid": "343",
"shortcut": [
{
"uid": "543",
"size": "2mb",
"shortcut.type": "file",
"_type": "File",
"name": "doc3",
"_id": 309,
"type": "doc"
}
],
"_type": "Folder",
"name": "myprojects",
"usercreated": [
{
"uid": "443",
"size": "2kb",
"_type": "File",
"name": "text3",
"_id": 306,
"type": "txt",
"usercreated.type": "file"
},
{
"uid": "523",
"size": "1mb",
"_type": "File",
"name": "doc1",
"_id": 307,
"type": "doc",
"usercreated.type": "file"
}
],
"_id": 303,
"type": "normal",
"usercreated.type": "folder"
}
]
},
{
"uid": "223",
"_type": "Drive",
"name": "Drive1",
"_id": 299,
"partition.during": "osinstall",
"capacity": "2gb",
"autocreated": [
{
"autocreated.during": "osinstall",
"autocreated.type": "folder",
"uid": "323",
"shortcut": [
{
"uid": "533",
"size": "1.5mb",
"shortcut.type": "file",
"_type": "File",
"name": "doc2",
"_id": 308,
"type": "doc"
}
],
"_type": "Folder",
"name": "desktop",
"usercreated": [
{
"uid": "543",
"size": "2mb",
"_type": "File",
"name": "doc3",
"_id": 309,
"type": "doc",
"usercreated.type": "file"
}
],
"_id": 301,
"type": "special"
}
]
}
],
"shortcut": [
{
"uid": "333",
"shortcut.type": "folder",
"_type": "Folder",
"name": "mydocuments",
"_id": 302,
"type": "special"
},
{
"uid": "323",
"shortcut.type": "folder",
"_type": "Folder",
"name": "desktop",
"_id": 301,
"type": "special"
}
],
"network": [
{
"_type": "Computer",
"name": "Randy",
"uid": "c1-123",
"_id": 310,
"sharedpartition": [
{
"_type": "SharedDrive",
"name": "SharedDrive",
"uid": "c1-s1",
"_id": 473,
"sharedpartition.during": "osinstall"
}
]
}
]
}

Neo4j Cypher : return nodes with id as a dictionary

I have nodes with these properties:
MATCH (n:A) RETURN n
[
{
"name": "114s09A.1",
"_id": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
},
{
"name": "114s09A.2",
"_id": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
]
Is there a way to build the cypher query so that the result would be shaped as a dictionary where id would be the key ?
[
{
"114s09A.1": {
"name": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
}
},
{
"114s09A.2": {
"name": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
}
]
The closest I came up to so far is :
MATCH (n:A) RETURN n._id AS _id, properties(n) AS properties
[
{
"_id":"114s09A.1",
"properties":{
"name": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
}
},
{
"_id":"114s09A.2",
"properties":{
"name": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
}
]
It's not possible with the default Cypher syntax, however if you have the apoc library installed, you can do this :
MATCH (n:A)
RETURN apoc.map.setKey({}, n.id, n{.*})

How can I restore data from a previous result in the browser?

I have a query that I ran in the neo4j web browser:
MATCH p=(z)<-[*]-(a)-[:foo]->(b) WHERE b.value = "bar" return p
This returned a large number of connected nodes. Something happened that seems to have deleted all of these nodes (in a separate incident), but I still have the output of the old query. The code section of the browser has the response data listed:
...
"graph": {
"nodes": [
{
"id": "1148578",
"labels": [
"text"
],
"properties": {
"value": "bar",
"timestamp": 1478747946867
}
},
...
Is there a way for me to recreate all of this data from the output of an old query?
You could use apoc.load.json to do this. Note that this solution will not preserve the internal node ids. APOC is a procedure library that extends built-in Neo4j functionality.
Given the JSON file
{"graph": {
"nodes": [
{
"id": "32496",
"labels": [
"Person"
],
"properties": {
"born": 1967,
"name": "Carrie-Anne Moss"
}
},
{
"id": "32505",
"labels": [
"Movie"
],
"properties": {
"tagline": "Evil has its winning ways",
"title": "The Devil's Advocate",
"released": 1997
}
},
{
"id": "32494",
"labels": [
"Movie"
],
"properties": {
"tagline": "Welcome to the Real World",
"title": "The Matrix",
"released": 1999
}
},
{
"id": "32495",
"labels": [
"Person"
],
"properties": {
"born": 1964,
"name": "Keanu Reeves"
}
}
],
"relationships": [
{
"id": "83204",
"type": "ACTED_IN",
"startNode": "32495",
"endNode": "32505",
"properties": {
"role": "Kevin Lomax"
}
},
{
"id": "83183",
"type": "ACTED_IN",
"startNode": "32496",
"endNode": "32494",
"properties": {
"role": "Trinity"
}
},
{
"id": "83182",
"type": "ACTED_IN",
"startNode": "32495",
"endNode": "32494",
"properties": {
"role": "Neo"
}
}
]
}
}
}
We can recreate the graph using this query:
CALL apoc.load.json("https://dl.dropboxusercontent.com/u/67572426/small_movie_graph.json") YIELD value AS row
WITH row, row.graph.nodes AS nodes
UNWIND nodes AS node
CALL apoc.create.node(node.labels, node.properties) YIELD node AS n
SET n.id = node.id
WITH row
UNWIND row.graph.relationships AS rel
MATCH (a) WHERE a.id = rel.startNode
MATCH (b) WHERE b.id = rel.endNode
CALL apoc.create.relationship(a, rel.type, rel.properties, b) YIELD rel AS r
RETURN *

neo4j Cypher hierarchical tree build response to JSON

Can you help me to build cypher query? i have following graph db structure:
(parent:Category)-[:subcategory]->(child:Category)
With this graph data i have hierarchical tree with deep level.
I found following code on Stackoverfllow.com and changed for my data:
MATCH (root:Category)-[:subcategory]->(parent:Category)-[:subcategory]->(child:Category)
WITH root, {category: parent, children: collect(child)} AS parent_with_children
WHERE NOT(()-[:subcategory]->(root))
RETURN {category: root, children: collect(parent_with_children)}
But he is build response only for depth with 3 levels of tree. I need bigger. I'm try to build json response like this example:
[
category: {
name: "PC"
children: {
category: {
name: "Parts"
children: {
category: {
name: "CPU"
...
}
}
},
category: {
name: "Accessories"
...
}
}
},
category: {
name: "Laptop"
...
}
]
The Cypher can make recursive calls? I think this will be better.
Thanks.
P.S. I know there are similar questions on SO, but they did not help me.
Cypher is not well suited for dumping out graph data in a tree structure when leaves are at arbitrary depths.
However, with neo4j 3.x, you can get close to what you want if you are able to install the APOC plugin on your server and use the apoc.convert.toTree procedure.
First, let's create some sample data:
CREATE
(c1:Category {name: 'PC'}),
(c1)-[:subcategory]->(c2:Category {name: 'Parts'}),
(c2)-[:subcategory]->(c3:Category {name: 'CPU'}),
(c3)-[:subcategory]->(c4:Category {name: 'CacheRAM'}),
(c1)-[:subcategory]->(c5:Category {name: 'Accessories'}),
(c5)-[:subcategory]->(c6:Category {name: 'Mouse'}),
(c5)-[:subcategory]->(c7:Category {name: 'Keyboard'}),
(c10:Category {name: 'Laptop'}),
(c10)-[:subcategory]->(c20:Category {name: 'Parts'}),
(c20)-[:subcategory]->(c30:Category {name: 'CPU'}),
(c10)-[:subcategory]->(c40:Category {name: 'Accessories'}),
(c40)-[:subcategory]->(c50:Category {name: 'Stylus'});
Then with this query:
MATCH p=(n:Category)-[:subcategory*]->(m)
WHERE NOT ()-[:subcategory]->(n)
WITH COLLECT(p) AS ps
CALL apoc.convert.toTree(ps) yield value
RETURN value;
... you will get N result rows, where N is the number of root Category nodes. Here is a snippet of sample results:
{
...
"row": [
{
"_id": 150,
"_type": "Category",
"name": "PC",
"subcategory": [
{
"_id": 154,
"_type": "Category",
"name": "Accessories",
"subcategory": [
{
"_id": 156,
"_type": "Category",
"name": "Keyboard"
},
{
"_id": 155,
"_type": "Category",
"name": "Mouse"
}
]
},
{
"_id": 151,
"_type": "Category",
"name": "Parts",
"subcategory": [
{
"_id": 152,
"_type": "Category",
"name": "CPU",
"subcategory": [
{
"_id": 153,
"_type": "Category",
"name": "CacheRAM"
}
]
}
]
}
]
}
],
...
"row": [
{
"_id": 157,
"_type": "Category",
"name": "Laptop",
"subcategory": [
{
"_id": 158,
"_type": "Category",
"name": "Parts",
"subcategory": [
{
"_id": 159,
"_type": "Category",
"name": "CPU"
}
]
},
{
"_id": 160,
"_type": "Category",
"name": "Accessories",
"subcategory": [
{
"_id": 161,
"_type": "Category",
"name": "Stylus"
}
]
}
]
}
],
...
}

Resources