From d357d5de6264151c0822adaa6fbf869018a1781d Mon Sep 17 00:00:00 2001 From: DanielBerton Date: Fri, 5 May 2017 12:16:30 +0200 Subject: [PATCH] FIxes #331 - missing detailed documentation for some procedures --- docs/graph-refactor.adoc | 287 ++++++++++++++++++++++++++++++++++++ docs/img/apoc.graph.png | Bin 0 -> 60332 bytes docs/index.adoc | 8 ++ docs/overview.adoc | 304 --------------------------------------- docs/virtual-graph.adoc | 62 ++++++++ 5 files changed, 357 insertions(+), 304 deletions(-) create mode 100644 docs/graph-refactor.adoc create mode 100644 docs/img/apoc.graph.png create mode 100644 docs/virtual-graph.adoc diff --git a/docs/graph-refactor.adoc b/docs/graph-refactor.adoc new file mode 100644 index 0000000000..603b3fa3b3 --- /dev/null +++ b/docs/graph-refactor.adoc @@ -0,0 +1,287 @@ + +[cols="1m,5"] +|=== +| call apoc.refactor.cloneNodes([node1,node2,...]) | clone nodes with their labels and properties +| call apoc.refactor.cloneNodesWithRelationships([node1,node2,...]) | clone nodes with their labels, properties and relationships +| call apoc.refactor.mergeNodes([node1,node2]) | merge nodes onto first in list +| call apoc.refactor.to(rel, endNode) | redirect relationship to use new end-node +| call apoc.refactor.from(rel, startNode) | redirect relationship to use new start-node +| call apoc.refactor.invert(rel) | inverts relationship direction +| call apoc.refactor.setType(rel, 'NEW-TYPE') | change relationship-type +| call apoc.refactor.extractNode([rel1,rel2,...], [labels], 'OUT','IN') | extract node from relationships +| call apoc.refactor.collapseNode([node1,node2],'TYPE') | collapse node to relationship, node with one rel becomes self-relationship +| call apoc.refactor.normalizeAsBoolean(entity, propertyKey, true_values, false_values) | normalize/convert a property to be boolean +| call apoc.refactor.categorize(node, propertyKey, type, outgoing, label) | turn each unique propertyKey into a category node and connect to it +|=== + +TODO: + +* merge nodes by label + property +* merge relationships + +=== Graph Refactoring Examples + +.Clone nodes + +We create a dataset +[source,cypher] +---- +CREATE (f:Foo{name:'Foo'}),(b:Bar{name:'Bar'}) +---- + +As result we have two nodes + +image::{img}/apoc.refactor.cloneNodes.dataset.png[width=800] + +[source,cypher] +---- +MATCH (f:Foo{name:'Foo'}),(b:Bar{name:'Bar'}) WITH f,b +CALL apoc.refactor.cloneNodes([f,b]) yield input, output RETURN * +---- + +As result we have the two nodes that we have created before and their clones + +image::{img}/apoc.refactor.cloneNodes.png[width=800] + +.Clone nodes with relationship + +We create a dataset of two different nodes of type `Actor` connected with other two different node of type `Movie` + +[source,cypher] +---- +CREATE (k:Actor {name:'Keanu Reeves'})-[:ACTED_IN {role:'Neo'}]->(m:Movie {title:'The Matrix'}), + (t:Actor {name:'Tom Hanks'})-[:ACTED_IN {role:'Forrest'}]->(f:Movie {title:'Forrest Gump'}) RETURN * +---- + +image::{img}/apoc.refactor.cloneNodesWithRelationships.dataset.png[width=800] + +[source,cypher] +---- +MATCH (k:Actor {name:'Keanu Reeves'}), (t:Actor {name:'Tom Hanks'}) +CALL apoc.refactor.cloneNodesWithRelationships([k,t]) YIELD input, output RETURN * +---- + +As result we have a copy of the nodes and relationships + +image::{img}/apoc.refactor.cloneNodesWithRelationships.png[width=800] + +.Merge nodes + +We create two nodes with different properties + +[source,cypher] +---- +CREATE (f:Person {name:'Foo'}), (b:Person {surname:'Bar'}) RETURN f,b +---- + +image::{img}/apoc.refactor.mergeNodes.dataset.png[width=800] + +Now we want to merge these nodes into one + +[source,cypher] +---- +MATCH (f:Person {name:'Foo'}), (b:Person {surname:'Bar'}) +CALL apoc.refactor.mergeNodes([f,b]) +YIELD node RETURN node +---- + +image::{img}/apoc.refactor.mergeNodes.png[width=800] + +Thus we have one node with both properties `name` and `surname` + +.Redirect relationship to + +We start with two nodes related each other with a relationship. We create a new node which we will use to redirect the relationship like end node + +[source,cypher] +---- +CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) +CREATE (p:Person {name:'Antony'}) +RETURN * +---- + +image::{img}/apoc.refactor.to.dataset.png[width=800] + +[source,cypher] +---- +MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) with id(rel) as id +MATCH (p:Person {name:'Antony'}) with p as p +MATCH ()-[r]->(), (p:Person) CALL apoc.refactor.to(r, p) YIELD input, output RETURN * +---- + +image::{img}/apoc.refactor.to.png[width=800] + +Now the relationship is towards the new node `Person` + +.Redirect relationship from + +We start with two nodes related each other with a relationship. We create a new node which we will use to redirect the relationship like start node + +[source,cypher] +---- +CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) +CREATE (p:Person {name:'Antony'}) +RETURN * +---- + +image::{img}/apoc.refactor.from.dataset.png[width=800] + +[source,cypher] +---- +MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) with id(rel) as id +MATCH (p:Person {name:'Antony'}) with p as p +MATCH ()-[r]->(), (p:Person) CALL apoc.refactor.from(r, p) YIELD input, output RETURN * +---- + +image::{img}/apoc.refactor.from.png[width=800] + +Now the relationship starts from the new node `Person` from the old node `Bar` + +.Invert relationship + +We start with two nodes connected by a relationship + +[source,cypher] +---- +CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) +---- + +image::{img}/apoc.refactor.invert.dataset.png[width=800] + +Now we want to invert the relationship direction + +[source,cypher] +---- +MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) WITH id(rel) as id +MATCH ()-[r]->() WHERE id(r) = id +CALL apoc.refactor.invert(r) yield input, output RETURN * +---- + +image::{img}/apoc.refactor.invert.call.png[width=800] + +image::{img}/apoc.refactor.invert.png[width=800] + +.Set type + +With a simple relationship between two node + +[source,cypher] +---- +CREATE (f:Foo)-[rel:FOOBAR]->(b:Bar) +---- + +image::{img}/apoc.refactor.setType.dataset.png[width=800] + +We can change the relationship type from `FOOBAR` to `NEW-TYPE` + +[source,cypher] +---- +MATCH (f:Foo)-[rel:FOOBAR]->(b:Bar) with rel +CALL apoc.refactor.setType(rel, 'NEW-TYPE') YIELD input, output RETURN * +---- + +image::{img}/apoc.refactor.setType.png[width=800] + +.Extract node from relationships + +[source,cypher] +---- +CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) +---- + +image::{img}/apoc.refactor.extractNode.dataset.png[width=800] + +We pass the ID of the relationship as parameter to extract a node + +[source,cypher] +---- +MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) WITH id(rel) as id +CALL apoc.refactor.extractNode(id,['FooBar'],'FOO','BAR') +YIELD input, output RETURN * +---- + +image::{img}/apoc.refactor.extractNode.png[width=800] + +.Collapse node to relationship + +[source,cypher] +---- +CREATE (f:Foo)-[:FOO {a:1}]->(b:Bar {c:3})-[:BAR {b:2}]->(f) WITH id(b) as id +CALL apoc.refactor.collapseNode(id,'FOOBAR') +YIELD input, output RETURN * +---- + +Before we have this situation + +image::{img}/apoc.refactor.collapseNode.dataset.png[width=800] + +And the result are + +image::{img}/apoc.refactor.collapseNode.png[width=800] + +The property of the two relationship and the property of the node are joined in one relationship that has the properties `a:1`, `b:2`, `name:Bar` + +.Normalize As Boolean + +[source,cypher] +---- +CREATE (:Person {prop: 'Y', name:'A'}),(:Person {prop: 'Yes', name:'B'}),(:Person {prop: 'NO', name:'C'}),(:Person {prop: 'X', name:'D'}) +---- + +As a resul we have four nodes with different properties `prop` like `Y`, `Yes`, `NO`, `X` + +image::{img}/apoc.refactor.normalizeAsBoolean.dataset.png[width=800] + +Now we want to transform some properties into a boolean, `Y`, `Yes` into true and the properties `NO` into false. +The other properties that don't match these possibilities will be set as `null`. + +[source,cypher] +---- +MATCH (n) CALL apoc.refactor.normalizeAsBoolean(n,'prop',['Y','Yes'],['NO']) WITH n ORDER BY n.id RETURN n.prop AS prop +---- + +image::{img}/apoc.refactor.normalizeAsBoolean.png[width=800] + +.Categorize + +First of all we create some nodes as dataset + +[source,cypher] +---- +CREATE (:Person {prop: 'A', k: 'a', id: 1}), + (:Person {prop: 'A', k: 'a', id: 2}), + (:Person {prop: 'C', k: 'c', id: 3}), + (:Person { id: 4}), + (:Person {prop: 'B', k: 'b', id: 5}), + (:Person {prop: 'C', k: 'c', id: 6}) +---- + +As result we have six nodes with label 'Person' with different properties + +image::{img}/apoc.refactor.categorize.dataset.png[width=800] + +Now we want to transform the property `prop` into a separate node with label `Letter` and transfer the properties of the nodes `Person`: `prop` (now renamed in `name`) and `k`. +The nodes `Person` will keep only the propertie `id`, and will be connected with a relationship `IS_A` with the new nodes `Letter`. + +[source,cypher] +---- +CALL apoc.refactor.categorize('prop','IS_A',true,'Letter','name',['k'],1) +---- + +image::{img}/apoc.refactor.categorize.png[width=800] + +The direction of the relationship (in this case outgoing) is defined by the third field, if `true` outgoing else incoming. +If a node doesn't has the property `prop` (like node with `id: 4`) it won't be managed. + +=== Rename + +Procedures set for renaming labels, relationship types, nodes and relationships' properties. +They return the list of eventually impacted constraints and indexes, the user should take care of. + +[cols="1m,5"] +|=== +| call apoc.refactor.rename.label(oldLabel, newLabel, [nodes]) | rename a label from 'oldLabel' to 'newLabel' for all nodes. If 'nodes' is provided renaming is applied to this set only +| call apoc.refactor.rename.type(oldType, newType, [rels]) | rename all relationships with type 'oldType' to 'newType'. If 'rels' is provided renaming is applied to this set only +| call apoc.refactor.rename.nodeProperty(oldName, newName, [nodes]) | rename all node's property from 'oldName' to 'newName'. If 'nodes' is provided renaming is applied to this set only +| call apoc.refactor.rename.typeProperty(oldName, newName, [rels]) | rename all relationship's property from 'oldName' to 'newName'. If 'rels' is provided renaming is applied to this set only +|=== diff --git a/docs/img/apoc.graph.png b/docs/img/apoc.graph.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc071b2928ccb9d3b19eb1732cf19ee91021de0 GIT binary patch literal 60332 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 E|5kdES^xk5 literal 0 HcmV?d00001 diff --git a/docs/index.adoc b/docs/index.adoc index c1d0ac2bdb..102b5392f9 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -151,6 +151,14 @@ $(document).ready(function() { ++++ +== Virtual + +include::virtual-graph.adoc[leveloffset=1] + +== Graph Refactoring + +include::graph-refactor.adoc[leveloffset=1] + == Appendix: Complete Overview include::overview.adoc[tags=overview,leveloffset=1] \ No newline at end of file diff --git a/docs/overview.adoc b/docs/overview.adoc index 8d0dd74442..c5d436032f 100644 --- a/docs/overview.adoc +++ b/docs/overview.adoc @@ -466,21 +466,6 @@ CALL apoc.create.vRelationship(a,rel_type,{count:count},b) yield rel RETURN *; ---- -== Virtual Graph - -Create a graph object (map) from information that's passed in. -It's basic structure is: `{name:"Name",properties:{properties},nodes:[nodes],relationships:[relationships]}` - -[cols="1m,5"] -|=== -| apoc.graph.from(data,'name',{properties}) yield graph | creates a virtual graph object for later processing it tries its best to extract the graph information from the data you pass in -| apoc.graph.fromData([nodes],[relationships],'name',{properties}) | creates a virtual graph object for later processing -| apoc.graph.fromPaths(path,'name',{properties}) | creates a virtual graph object for later processing -| apoc.graph.fromPaths([paths],'name',{properties}) | creates a virtual graph object for later processing -| apoc.graph.fromDB('name',{properties}) | creates a virtual graph object for later processing -| apoc.graph.fromCypher('statement',{params},'name',{properties}) | creates a virtual graph object for later processing -|=== - == Generating Graphs Generate undirected (random direction) graphs with semi-real random distributions based on theoretical models. @@ -616,295 +601,6 @@ CALL apoc.periodic.rock_n_roll('match (p:Person) return id(p) as id_p', 'MATCH ( // end::periodic[] -== Graph Refactoring - -[cols="1m,5"] -|=== -| call apoc.refactor.cloneNodes([node1,node2,...]) | clone nodes with their labels and properties -| call apoc.refactor.cloneNodesWithRelationships([node1,node2,...]) | clone nodes with their labels, properties and relationships -| call apoc.refactor.mergeNodes([node1,node2]) | merge nodes onto first in list -| call apoc.refactor.to(rel, endNode) | redirect relationship to use new end-node -| call apoc.refactor.from(rel, startNode) | redirect relationship to use new start-node -| call apoc.refactor.invert(rel) | inverts relationship direction -| call apoc.refactor.setType(rel, 'NEW-TYPE') | change relationship-type -| call apoc.refactor.extractNode([rel1,rel2,...], [labels], 'OUT','IN') | extract node from relationships -| call apoc.refactor.collapseNode([node1,node2],'TYPE') | collapse node to relationship, node with one rel becomes self-relationship -| call apoc.refactor.normalizeAsBoolean(entity, propertyKey, true_values, false_values) | normalize/convert a property to be boolean -| call apoc.refactor.categorize(node, propertyKey, type, outgoing, label) | turn each unique propertyKey into a category node and connect to it -|=== - -TODO: - -* merge nodes by label + property -* merge relationships - -=== Graph Refactoring Examples - -.Clone nodes - -We create a dataset -[source,cypher] ----- -CREATE (f:Foo{name:'Foo'}),(b:Bar{name:'Bar'}) ----- - -As result we have two nodes - -image::{img}/apoc.refactor.cloneNodes.dataset.png[width=800] - -[source,cypher] ----- -MATCH (f:Foo{name:'Foo'}),(b:Bar{name:'Bar'}) WITH f,b -CALL apoc.refactor.cloneNodes([f,b]) yield input, output RETURN * ----- - -As result we have the two nodes that we have created before and their clones - -image::{img}/apoc.refactor.cloneNodes.png[width=800] - -.Clone nodes with relationship - -We create a dataset of two different nodes of type `Actor` connected with other two different node of type `Movie` - -[source,cypher] ----- -CREATE (k:Actor {name:'Keanu Reeves'})-[:ACTED_IN {role:'Neo'}]->(m:Movie {title:'The Matrix'}), - (t:Actor {name:'Tom Hanks'})-[:ACTED_IN {role:'Forrest'}]->(f:Movie {title:'Forrest Gump'}) RETURN * ----- - -image::{img}/apoc.refactor.cloneNodesWithRelationships.dataset.png[width=800] - -[source,cypher] ----- -MATCH (k:Actor {name:'Keanu Reeves'}), (t:Actor {name:'Tom Hanks'}) -CALL apoc.refactor.cloneNodesWithRelationships([k,t]) YIELD input, output RETURN * ----- - -As result we have a copy of the nodes and relationships - -image::{img}/apoc.refactor.cloneNodesWithRelationships.png[width=800] - -.Merge nodes - -We create two nodes with different properties - -[source,cypher] ----- -CREATE (f:Person {name:'Foo'}), (b:Person {surname:'Bar'}) RETURN f,b ----- - -image::{img}/apoc.refactor.mergeNodes.dataset.png[width=800] - -Now we want to merge these nodes into one - -[source,cypher] ----- -MATCH (f:Person {name:'Foo'}), (b:Person {surname:'Bar'}) -CALL apoc.refactor.mergeNodes([f,b]) -YIELD node RETURN node ----- - -image::{img}/apoc.refactor.mergeNodes.png[width=800] - -Thus we have one node with both properties `name` and `surname` - -.Redirect relationship to - -We start with two nodes related each other with a relationship. We create a new node which we will use to redirect the relationship like end node - -[source,cypher] ----- -CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) -CREATE (p:Person {name:'Antony'}) -RETURN * ----- - -image::{img}/apoc.refactor.to.dataset.png[width=800] - -[source,cypher] ----- -MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) with id(rel) as id -MATCH (p:Person {name:'Antony'}) with p as p -MATCH ()-[r]->(), (p:Person) CALL apoc.refactor.to(r, p) YIELD input, output RETURN * ----- - -image::{img}/apoc.refactor.to.png[width=800] - -Now the relationship is towards the new node `Person` - -.Redirect relationship from - -We start with two nodes related each other with a relationship. We create a new node which we will use to redirect the relationship like start node - -[source,cypher] ----- -CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) -CREATE (p:Person {name:'Antony'}) -RETURN * ----- - -image::{img}/apoc.refactor.from.dataset.png[width=800] - -[source,cypher] ----- -MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) with id(rel) as id -MATCH (p:Person {name:'Antony'}) with p as p -MATCH ()-[r]->(), (p:Person) CALL apoc.refactor.from(r, p) YIELD input, output RETURN * ----- - -image::{img}/apoc.refactor.from.png[width=800] - -Now the relationship starts from the new node `Person` from the old node `Bar` - -.Invert relationship - -We start with two nodes connected by a relationship - -[source,cypher] ----- -CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) ----- - -image::{img}/apoc.refactor.invert.dataset.png[width=800] - -Now we want to invert the relationship direction - -[source,cypher] ----- -MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) WITH id(rel) as id -MATCH ()-[r]->() WHERE id(r) = id -CALL apoc.refactor.invert(r) yield input, output RETURN * ----- - -image::{img}/apoc.refactor.invert.call.png[width=800] - -image::{img}/apoc.refactor.invert.png[width=800] - -.Set type - -With a simple relationship between two node - -[source,cypher] ----- -CREATE (f:Foo)-[rel:FOOBAR]->(b:Bar) ----- - -image::{img}/apoc.refactor.setType.dataset.png[width=800] - -We can change the relationship type from `FOOBAR` to `NEW-TYPE` - -[source,cypher] ----- -MATCH (f:Foo)-[rel:FOOBAR]->(b:Bar) with rel -CALL apoc.refactor.setType(rel, 'NEW-TYPE') YIELD input, output RETURN * ----- - -image::{img}/apoc.refactor.setType.png[width=800] - -.Extract node from relationships - -[source,cypher] ----- -CREATE (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) ----- - -image::{img}/apoc.refactor.extractNode.dataset.png[width=800] - -We pass the ID of the relationship as parameter to extract a node - -[source,cypher] ----- -MATCH (f:Foo)-[rel:FOOBAR {a:1}]->(b:Bar) WITH id(rel) as id -CALL apoc.refactor.extractNode(id,['FooBar'],'FOO','BAR') -YIELD input, output RETURN * ----- - -image::{img}/apoc.refactor.extractNode.png[width=800] - -.Collapse node to relationship - -[source,cypher] ----- -CREATE (f:Foo)-[:FOO {a:1}]->(b:Bar {c:3})-[:BAR {b:2}]->(f) WITH id(b) as id -CALL apoc.refactor.collapseNode(id,'FOOBAR') -YIELD input, output RETURN * ----- - -Before we have this situation - -image::{img}/apoc.refactor.collapseNode.dataset.png[width=800] - -And the result are - -image::{img}/apoc.refactor.collapseNode.png[width=800] - -The property of the two relationship and the property of the node are joined in one relationship that has the properties `a:1`, `b:2`, `name:Bar` - -.Normalize As Boolean - -[source,cypher] ----- -CREATE (:Person {prop: 'Y', name:'A'}),(:Person {prop: 'Yes', name:'B'}),(:Person {prop: 'NO', name:'C'}),(:Person {prop: 'X', name:'D'}) ----- - -As a resul we have four nodes with different properties `prop` like `Y`, `Yes`, `NO`, `X` - -image::{img}/apoc.refactor.normalizeAsBoolean.dataset.png[width=800] - -Now we want to transform some properties into a boolean, `Y`, `Yes` into true and the properties `NO` into false. -The other properties that don't match these possibilities will be set as `null`. - -[source,cypher] ----- -MATCH (n) CALL apoc.refactor.normalizeAsBoolean(n,'prop',['Y','Yes'],['NO']) WITH n ORDER BY n.id RETURN n.prop AS prop ----- - -image::{img}/apoc.refactor.normalizeAsBoolean.png[width=800] - -.Categorize - -First of all we create some nodes as dataset - -[source,cypher] ----- -CREATE (:Person {prop: 'A', k: 'a', id: 1}), - (:Person {prop: 'A', k: 'a', id: 2}), - (:Person {prop: 'C', k: 'c', id: 3}), - (:Person { id: 4}), - (:Person {prop: 'B', k: 'b', id: 5}), - (:Person {prop: 'C', k: 'c', id: 6}) ----- - -As result we have six nodes with label 'Person' with different properties - -image::{img}/apoc.refactor.categorize.dataset.png[width=800] - -Now we want to transform the property `prop` into a separate node with label `Letter` and transfer the properties of the nodes `Person`: `prop` (now renamed in `name`) and `k`. -The nodes `Person` will keep only the propertie `id`, and will be connected with a relationship `IS_A` with the new nodes `Letter`. - -[source,cypher] ----- -CALL apoc.refactor.categorize('prop','IS_A',true,'Letter','name',['k'],1) ----- - -image::{img}/apoc.refactor.categorize.png[width=800] - -The direction of the relationship (in this case outgoing) is defined by the third field, if `true` outgoing else incoming. -If a node doesn't has the property `prop` (like node with `id: 4`) it won't be managed. - -=== Rename - -Procedures set for renaming labels, relationship types, nodes and relationships' properties. -They return the list of eventually impacted constraints and indexes, the user should take care of. - -[cols="1m,5"] -|=== -| call apoc.refactor.rename.label(oldLabel, newLabel, [nodes]) | rename a label from 'oldLabel' to 'newLabel' for all nodes. If 'nodes' is provided renaming is applied to this set only -| call apoc.refactor.rename.type(oldType, newType, [rels]) | rename all relationships with type 'oldType' to 'newType'. If 'rels' is provided renaming is applied to this set only -| call apoc.refactor.rename.nodeProperty(oldName, newName, [nodes]) | rename all node's property from 'oldName' to 'newName'. If 'nodes' is provided renaming is applied to this set only -| call apoc.refactor.rename.typeProperty(oldName, newName, [rels]) | rename all relationship's property from 'oldName' to 'newName'. If 'rels' is provided renaming is applied to this set only -|=== - == Spatial [cols="1m,5"] diff --git a/docs/virtual-graph.adoc b/docs/virtual-graph.adoc new file mode 100644 index 0000000000..9a8a650e70 --- /dev/null +++ b/docs/virtual-graph.adoc @@ -0,0 +1,62 @@ +== Virtual Graph + +Create a graph object (map) from information that's passed in. +It's basic structure is: `{name:"Name",properties:{properties},nodes:[nodes],relationships:[relationships]}` + +[cols="1m,5"] +|=== +| apoc.graph.from(data,'name',{properties}) yield graph | creates a virtual graph object for later processing it tries its best to extract the graph information from the data you pass in +| apoc.graph.fromData([nodes],[relationships],'name',{properties}) | creates a virtual graph object for later processing +| apoc.graph.fromPaths(path,'name',{properties}) | creates a virtual graph object for later processing +| apoc.graph.fromPaths([paths],'name',{properties}) | creates a virtual graph object for later processing +| apoc.graph.fromDB('name',{properties}) | creates a virtual graph object for later processing +| apoc.graph.fromCypher('statement',{params},'name',{properties}) | creates a virtual graph object for later processing +|=== + +=== Virtual Graph Examples + +We create a dataset for our examples + +[source,cypher] +---- +CREATE (a:Actor {name:'Tom Hanks'})-[r:ACTED_IN {roles:'Forrest'}]->(m:Movie {title:'Forrest Gump'}) RETURN * +---- + +.Virtual graph from data + +[source,cypher] +---- +MATCH (n)-[r]->(m) CALL apoc.graph.fromData([n,m],[r],'test',{answer:42}) YIELD graph RETURN * +---- + +.Virtual graph from path + +[source,cypher] +---- +MATCH path = (n)-[r]->(m) CALL apoc.graph.fromPath(path,'test',{answer:42}) YIELD graph RETURN * +---- + +.Virtual graph from paths + +[source,cypher] +---- +MATCH path = (n)-[r]->(m) CALL apoc.graph.fromPaths([path],'test',{answer:42}) YIELD graph RETURN * +---- + +.Virtual graph from DB + +[source,cypher] +---- +CALL apoc.graph.fromDB('test',{answer:42}) YIELD graph RETURN * +---- + +.Virtual graph from Cypher + +[source,cypher] +---- +CALL apoc.graph.fromCypher('MATCH (n)-[r]->(m) RETURN *',null,'test',{answer:42}) YIELD graph RETURN * +---- + +As a result we have a virtual graph object for later processing + +image::{img}/apoc.graph.png[width=800] \ No newline at end of file