Nodes { @Procedure @Description("apoc.nodes.get(node|nodes|id|[ids]) - quickly returns all nodes with these ids") public Stream<NodeResult> get(@Name("nodes") Object ids) { return Util.nodeStream(tx, ids).map(NodeResult::new); }  @Procedure(mode = Mode.WRITE) @Description("apoc.nodes.link([nodes],'REL_TYPE') - creates a linked list of nodes from first to last") void link(@Name("nodes") List<Node> nodes, @Name("type") String type); @Procedure @Description("apoc.nodes.get(node|nodes|id|[ids]) - quickly returns all nodes with these ids") Stream<NodeResult> get(@Name("nodes") Object ids); @Procedure(mode = Mode.WRITE) @Description("apoc.nodes.delete(node|nodes|id|[ids]) - quickly delete all nodes with these ids") Stream<LongResult> delete(@Name("nodes") Object ids, @Name("batchSize") long batchSize); @Procedure @Description("apoc.get.rels(rel|id|[ids]) - quickly returns all relationships with these ids") Stream<RelationshipResult> rels(@Name("relationships") Object ids); @UserFunction("apoc.node.relationship.exists") @Description("apoc.node.relationship.exists(node, rel-direction-pattern) - returns true when the node has the relationships of the pattern") boolean hasRelationship(@Name("node") Node node, @Name(value = "types", defaultValue = "") String types); @UserFunction("apoc.nodes.connected") @Description("apoc.nodes.connected(start, end, rel-direction-pattern) - returns true when the node is connected to the other node, optimized for dense nodes") boolean connected(@Name("start") Node start, @Name("start") Node end, @Name(value = "types", defaultValue = "") String types); @Procedure @Description("apoc.nodes.collapse([nodes...],[{properties:'overwrite' or 'discard' or 'combine'}]) yield from, rel, to merge nodes onto first in list") Stream<VirtualPathResult> collapse(@Name("nodes") List<Node> nodes, @Name(value = "config", defaultValue = "{}") Map<String, Object> config); @UserFunction("apoc.node.labels") @Description("returns labels for (virtual) nodes") List<String> labels(@Name("node") Node node); @UserFunction("apoc.node.id") @Description("returns id for (virtual) nodes") Long id(@Name("node") Node node); @UserFunction("apoc.rel.id") @Description("returns id for (virtual) relationships") Long relId(@Name("rel") Relationship rel); @UserFunction("apoc.rel.type") @Description("returns type for (virtual) relationships") String type(@Name("rel") Relationship rel); @UserFunction("apoc.any.properties") @Description("returns properties for virtual and real, nodes, rels and maps") Map<String,Object> properties(@Name("thing") Object thing, @Name(value = "keys",defaultValue = "null") List<String> keys); @UserFunction("apoc.any.property") @Description("returns property for virtual and real, nodes, rels and maps") Object property(@Name("thing") Object thing, @Name(value = "key") String key); @UserFunction("apoc.node.degree") @Description("apoc.node.degree(node, rel-direction-pattern) - returns total degrees of the given relationships in the pattern, can use '>' or '<' for all outgoing or incoming relationships") long degree(@Name("node") Node node, @Name(value = "types",defaultValue = "") String types); @UserFunction("apoc.node.degree.in") @Description("apoc.node.degree.in(node, relationshipName) - returns total number number of incoming relationships") long degreeIn(@Name("node") Node node, @Name(value = "types",defaultValue = "") String type); @UserFunction("apoc.node.degree.out") @Description("apoc.node.degree.out(node, relationshipName) - returns total number number of outgoing relationships") long degreeOut(@Name("node") Node node, @Name(value = "types",defaultValue = "") String type); @UserFunction("apoc.node.relationship.types") @Description("apoc.node.relationship.types(node, rel-direction-pattern) - returns a list of distinct relationship types") List<String> relationshipTypes(@Name("node") Node node, @Name(value = "types",defaultValue = "") String types); @UserFunction("apoc.nodes.relationship.types") @Description("apoc.nodes.relationship.types(node|nodes|id|[ids], rel-direction-pattern) - returns " + "a list of maps where each one has two fields: `node` which is the node subject of the analysis " + "and `types` which is a list of distinct relationship types") List<Map<String, Object>> nodesRelationshipTypes(@Name("ids") Object ids, @Name(value = "types",defaultValue = "") String types); @UserFunction("apoc.node.relationships.exist") @Description("apoc.node.relationships.exist(node, rel-direction-pattern) - returns a map with rel-pattern, boolean for the given relationship patterns") Map<String,Boolean> relationshipExists(@Name("node") Node node, @Name(value = "types", defaultValue = "") String types); @UserFunction("apoc.nodes.relationships.exist") @Description("apoc.nodes.relationships.exist(node|nodes|id|[ids], rel-direction-pattern) - returns " + "a list of maps where each one has two fields: `node` which is the node subject of the analysis " + "and `exists` which is a map with rel-pattern, boolean for the given relationship patterns") List<Map<String, Object>> nodesRelationshipExists(@Name("ids") Object ids, @Name(value = "types", defaultValue = "") String types); @UserFunction @Description("apoc.nodes.isDense(node) - returns true if it is a dense node") boolean isDense(@Name("node") Node node); @Context
    public GraphDatabaseService db; @Context
    public Transaction tx; @Context
    public KernelTransaction ktx; @Context
    public Pools pools; }
@Test public void types() throws Exception { db.executeTransactionally("CREATE (f:Foo) CREATE (b:Bar) CREATE (f)-[:Y]->(f) CREATE (f)-[:X]->(f)"); TestUtil.testCall(db, "MATCH (n:Foo) RETURN apoc.node.relationship.types(n) AS value", (r) -> assertEquals(asSet("X","Y"), asSet(((List)r.get("value")).toArray()))); TestUtil.testCall(db, "MATCH (n:Foo) RETURN apoc.node.relationship.types(n,'X') AS value", (r) -> assertEquals(asList("X"), r.get("value"))); TestUtil.testCall(db, "MATCH (n:Foo) RETURN apoc.node.relationship.types(n,'X|Z') AS value", (r) -> assertEquals(asList("X"), r.get("value"))); TestUtil.testCall(db, "MATCH (n:Bar) RETURN apoc.node.relationship.types(n) AS value", (r) -> assertEquals(Collections.emptyList(), r.get("value"))); TestUtil.testCall(db, "RETURN apoc.node.relationship.types(null) AS value", (r) -> assertEquals(null, r.get("value"))); }
@Test public void nodesTypes() throws Exception { db.executeTransactionally("CREATE (f:Foo), (f)-[:Y]->(f), (f)-[:X]->(f)"); db.executeTransactionally("CREATE (f:Bar), (f)-[:YY]->(f), (f)-[:XX]->(f)"); TestUtil.testCall(db, "MATCH (n) RETURN apoc.nodes.relationship.types(collect(n)) AS value", (result) -> { List<Map<String, Object>> list = (List<Map<String, Object>>) result.get("value"); assertFalse("value should not be empty", list.isEmpty()); list.forEach(map -> { Node node = (Node) map.get("node"); List<String> data = (List<String>) map.get("types"); if (node.hasLabel(Label.label("Foo"))) { assertEquals(asSet("X", "Y"), asSet(data.iterator())); } else { assertEquals(asSet("XX", "YY"), asSet(data.iterator())); } }); }); }
@Test public void nodesHasRelationship() throws Exception { db.executeTransactionally("CREATE (f:Foo), (f)-[:X]->(f)"); db.executeTransactionally("CREATE (b:Bar), (b)-[:Y]->(b)"); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.nodes.relationships.exist(collect(n), 'X|Y') AS value", (result) -> { List<Map<String, Object>> list = (List<Map<String, Object>>) result.get("value"); assertFalse("value should not be empty", list.isEmpty()); list.forEach(map -> { Map<String, Boolean> data = (Map<String, Boolean>) map.get("exists"); assertEquals(map("X", true, "Y", false), data); }); }); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.nodes.relationships.exist(collect(n), 'X|Y') AS value", (result) -> { List<Map<String, Object>> list = (List<Map<String, Object>>) result.get("value"); assertFalse("value should not be empty", list.isEmpty()); list.forEach(map -> { Map<String, Boolean> data = (Map<String, Boolean>) map.get("exists"); assertEquals(map("X", false, "Y", true), data); }); }); }
@Test public void hasRelationhip() throws Exception { db.executeTransactionally("CREATE (:Foo)-[:Y]->(:Bar),(n:FooBar) WITH n UNWIND range(1,100) as _ CREATE (n)-[:X]->(n)"); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationship.exists(n,'Y') AS value",(r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationship.exists(n,'Y>') AS value", (r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationship.exists(n,'<Y') AS value", (r)-> assertEquals(false,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationship.exists(n,'X') AS value", (r)-> assertEquals(false,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationship.exists(n,'Y') AS value",(r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationship.exists(n,'Y>') AS value", (r)-> assertEquals(false,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationship.exists(n,'<Y') AS value", (r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationship.exists(n,'X') AS value", (r)-> assertEquals(false,r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationship.exists(n,'X') AS value",(r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationship.exists(n,'X>') AS value", (r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationship.exists(n,'<X') AS value", (r)-> assertEquals(true,r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationship.exists(n,'Y') AS value", (r)-> assertEquals(false,r.get("value"))); }
@Test public void hasRelationhips() throws Exception { db.executeTransactionally("CREATE (:Foo)-[:Y]->(:Bar),(n:FooBar) WITH n UNWIND range(1,100) as _ CREATE (n)-[:X]->(n)"); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationships.exist(n,'Y') AS value",(r)-> assertEquals(map("Y",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationships.exist(n,'Y>') AS value", (r)-> assertEquals(map("Y>",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationships.exist(n,'<Y') AS value", (r)-> assertEquals(map("<Y",false),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Foo) RETURN apoc.node.relationships.exist(n,'X') AS value", (r)-> assertEquals(map("X",false),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationships.exist(n,'Y') AS value",(r)-> assertEquals(map("Y",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationships.exist(n,'Y>') AS value", (r)-> assertEquals(map("Y>",false),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationships.exist(n,'<Y') AS value", (r)-> assertEquals(map("<Y",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:Bar) RETURN apoc.node.relationships.exist(n,'X') AS value", (r)-> assertEquals(map("X",false),r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationships.exist(n,'X') AS value",(r)-> assertEquals(map("X",true ),r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationships.exist(n,'X>') AS value", (r)-> assertEquals(map("X>",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationships.exist(n,'<X') AS value", (r)-> assertEquals(map("<X",true),r.get("value"))); TestUtil.testCall(db,"MATCH (n:FooBar) RETURN apoc.node.relationships.exist(n,'Y') AS value", (r)-> assertEquals(map("Y",false),r.get("value"))); }
@Test public void testDegreeTypeAndDirection() { db.executeTransactionally("CREATE (f:Foo) CREATE (b:Bar) CREATE (f)-[:Y]->(b) CREATE (f)-[:Y]->(b) CREATE (f)-[:X]->(b) CREATE (f)<-[:X]-(b)"); TestUtil.testCall(db, "MATCH (f:Foo),(b:Bar) RETURN apoc.node.degree(f, '<X') as in, apoc.node.degree(f, 'Y>') as out", (r) -> { assertEquals(1l, r.get("in")); assertEquals(2l, r.get("out")); }); }
@Test public void testDegreeMultiple() { db.executeTransactionally("CREATE (f:Foo) CREATE (b:Bar) CREATE (f)-[:Y]->(b) CREATE (f)-[:Y]->(b) CREATE (f)-[:X]->(b) CREATE (f)<-[:X]-(b)"); TestUtil.testCall(db, "MATCH (f:Foo),(b:Bar) RETURN apoc.node.degree(f, '<X|Y') as all", (r) -> { assertEquals(3l, r.get("all")); }); }
@Test public void testDegreeTypeOnly() { db.executeTransactionally("CREATE (f:Foo) CREATE (b:Bar) CREATE (f)-[:Y]->(b) CREATE (f)-[:Y]->(b) CREATE (f)-[:X]->(b) CREATE (f)<-[:X]-(b)"); TestUtil.testCall(db, "MATCH (f:Foo),(b:Bar) RETURN apoc.node.degree(f, 'X') as in, apoc.node.degree(f, 'Y') as out", (r) -> { assertEquals(2l, r.get("in")); assertEquals(2l, r.get("out")); }); }
@Test public void testDegreeDirectionOnly() { db.executeTransactionally("CREATE (f:Foo) CREATE (b:Bar) CREATE (f)-[:Y]->(b) CREATE (f)-[:X]->(b) CREATE (f)<-[:X]-(b)"); TestUtil.testCall(db, "MATCH (f:Foo),(b:Bar) RETURN apoc.node.degree(f, '<') as in, apoc.node.degree(f, '>') as out", (r) -> { assertEquals(1l, r.get("in")); assertEquals(2l, r.get("out")); }); }
@Test public void testDegreeInOutDirectionOnly() { db.executeTransactionally("CREATE (a:Person{name:'test'}) CREATE (b:Person) CREATE (c:Person) CREATE (d:Person) CREATE (a)-[:Rel1]->(b) CREATE (a)-[:Rel1]->(c) CREATE (a)-[:Rel2]->(d) CREATE (a)-[:Rel1]->(b) CREATE (a)<-[:Rel2]-(b) CREATE (a)<-[:Rel2]-(c) CREATE (a)<-[:Rel2]-(d) CREATE (a)<-[:Rel1]-(d)"); TestUtil.testCall(db, "MATCH (a:Person{name:'test'}) RETURN apoc.node.degree.in(a) as in, apoc.node.degree.out(a) as out", (r) -> { assertEquals(4l, r.get("in")); assertEquals(4l, r.get("out")); }); }
@Test public void testDegreeInOutType() { db.executeTransactionally("CREATE (a:Person{name:'test'}) CREATE (b:Person) CREATE (c:Person) CREATE (d:Person) CREATE (a)-[:Rel1]->(b) CREATE (a)-[:Rel1]->(c) CREATE (a)-[:Rel2]->(d) CREATE (a)-[:Rel1]->(b) CREATE (a)<-[:Rel2]-(b) CREATE (a)<-[:Rel2]-(c) CREATE (a)<-[:Rel2]-(d) CREATE (a)<-[:Rel1]-(d)"); TestUtil.testCall(db, "MATCH (a:Person{name:'test'}) RETURN apoc.node.degree.in(a, 'Rel1') as in1, apoc.node.degree.out(a, 'Rel1') as out1, apoc.node.degree.in(a, 'Rel2') as in2, apoc.node.degree.out(a, 'Rel2') as out2", (r) -> { assertEquals(1l, r.get("in1")); assertEquals(3l, r.get("out1")); assertEquals(3l, r.get("in2")); assertEquals(1l, r.get("out2")); }); }
@Test public void testMergeNotSelfRelationship() { db.executeTransactionally("MATCH (n) detach delete (n)"); db.executeTransactionally("CREATE (a1:ALabel {name:'a1'})-[:KNOWS]->(b1:BLabel {name:'b1'})"); Set<Label> label = asSet(label("ALabel"), label("BLabel")); TestUtil.testResult(db, "MATCH (p:ALabel)-[r:KNOWS]->(c:BLabel) WITH p,c " + "CALL apoc.nodes.collapse([p,c],{mergeVirtualRels:true, countMerge: true}) yield from, rel, to " + "return from, rel, to", (r) -> { Map<String, Object> map = r.next(); assertEquals(Util.map("name","b1", "count", 2), ((VirtualNode)map.get("from")).getAllProperties()); assertEquals(label, labelSet((VirtualNode)map.get("from"))); assertNull(((VirtualRelationship) map.get("rel"))); assertNull(((Node) map.get("to"))); assertFalse(r.hasNext()); }); }
@Test public void testMergeWithRelationshipDirection() { db.executeTransactionally("MATCH (n) detach delete (n)"); db.executeTransactionally("CREATE " + "(a1:ALabel {name:'a1'})-[:KNOWS]->(b1:BLabel {name:'b1'})," + "(a1)<-[:KNOWS]-(b2:CLabel {name:'c1'})"); Set<Label> label = asSet(label("ALabel"), label("BLabel")); TestUtil.testResult(db, "MATCH (p:ALabel)-[r:KNOWS]->(c:BLabel) WITH p,c " + "CALL apoc.nodes.collapse([p,c],{mergeVirtualRels:true, selfRel: true}) yield from, rel, to " + "return from, rel, to", (r) -> { Map<String, Object> map = r.next(); assertEquals(Util.map("name","c1"), ((Node)map.get("from")).getAllProperties()); assertEquals(asList(label("CLabel")), ((Node)map.get("from")).getLabels()); assertEquals(Collections.emptyMap(), ((VirtualRelationship)map.get("rel")).getAllProperties()); assertEquals("KNOWS", ((VirtualRelationship)map.get("rel")).getType().name()); assertEquals(Util.map("name", "b1", "count", 2), ((Node)map.get("to")).getAllProperties()); assertEquals(label, labelSet((Node)map.get("to"))); assertTrue(r.hasNext()); map = r.next(); assertEquals(Util.map("name","b1", "count", 2), ((VirtualNode)map.get("from")).getAllProperties()); assertEquals(label, labelSet((VirtualNode)map.get("from"))); assertEquals(Util.map("count", 1), ((VirtualRelationship)map.get("rel")).getAllProperties()); assertEquals("KNOWS", ((VirtualRelationship)map.get("rel")).getType().name()); assertEquals(Util.map("name", "b1", "count", 2), ((VirtualNode)map.get("to")).getAllProperties()); assertEquals(label, labelSet((VirtualNode)map.get("to"))); assertFalse(r.hasNext()); }); }
@Test public void testMergeVirtualNode() { db.executeTransactionally("CREATE \n" + "(p:Person {name: 'John'})-[:LIVES_IN]->(c:City{name:'London'}),\n" + "(p1:Person {name: 'Mike'})-[:LIVES_IN]->(c),\n" + "(p2:Person {name: 'Kate'})-[:LIVES_IN]->(c),\n" + "(p3:Person {name: 'Budd'})-[:LIVES_IN]->(c),\n" + "(p4:Person {name: 'Alex'})-[:LIVES_IN]->(c),\n" + "(p1)-[:KNOWS]->(p),\n" + "(p2)-[:KNOWS]->(p1),\n" + "(p2)-[:KNOWS]->(p3),\n" + "(p4)-[:KNOWS]->(p3)\n"); Set<Label> label = asSet(label("City"), label("Person")); TestUtil.testResult(db, "MATCH (p:Person)-[:LIVES_IN]->(c:City)\n" + "WITH c, c + collect(p) as subgraph\n" + "CALL apoc.nodes.collapse(subgraph,{properties:'discard', mergeVirtualRels:true, countMerge: true}) yield from, rel, to return from, rel, to", null, result -> { Map<String, Object> map = result.next(); assertEquals(Util.map("name","London", "count", 6), ((VirtualNode)map.get("from")).getAllProperties()); assertEquals(label, labelSet((VirtualNode) map.get("from"))); assertNull(map.get("rel")); assertNull(map.get("to")); assertFalse(result.hasNext()); }); }