Skip to content

Commit

Permalink
fixes #699 - implement functions from pgsql (#860)
Browse files Browse the repository at this point in the history
  • Loading branch information
AngeloBusato authored and jexp committed Jul 20, 2018
1 parent bbc36ef commit f0d772b
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 9 deletions.
10 changes: 9 additions & 1 deletion docs/overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ Sometimes type information gets lost, these functions help you to coerce an "Any
| apoc.coll.insert(coll, index, value) | insert value at index
| apoc.coll.insertAll(coll, index, values) | insert values at index
| apoc.coll.remove(coll, index, [length=1]) | remove range of values from index to length

| apoc.coll.different(values) | returns true if value are different
|===

=== Lookup and Manipulation Procedures
Expand All @@ -805,6 +805,8 @@ Sometimes type information gets lost, these functions help you to coerce an "Any
| apoc.node.relationship.types(node, rel-direction-pattern) | returns a list of distinct relationship types
| 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
| apoc.node.id(node) | returns id for (virtual) nodes
| apoc.node.degree.in(node, relationshipName) | returns total number of incoming relationship
| apoc.node.degree.out(node, relationshipName) | returns total number of outgoing relationship
| apoc.node.labels(node) | returns labels for (virtual) nodes
| apoc.rel.id(rel) | returns id for (virtual) relationships
| apoc.rel.type(rel) | returns type for (virtual) relationships
Expand Down Expand Up @@ -906,6 +908,12 @@ Example: `'FRIEND|MENTORS>|<REPORTS_TO'` will match to :FRIEND relationships in
|===


=== Label Functions

[cols="1m,5"]
|===
| apoc.label.exists(element, label) | returns true or false related to label existance
|===

== Utilities

Expand Down
12 changes: 9 additions & 3 deletions src/main/java/apoc/coll/Coll.java
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,9 @@ public List<Object> reverse(@Name("coll") List<Object> coll) {
@UserFunction("apoc.coll.sortMulti")
@Description("apoc.coll.sortMulti(coll, ['^name','age'],[limit],[skip]) - sort list of maps by several sort fields (ascending with ^ prefix) and optionally applies limit and skip")
public List<Map<String,Object>> sortMulti(@Name("coll") java.util.List<Map<String,Object>> coll,
@Name(value="orderFields", defaultValue = "[]") java.util.List<String> orderFields,
@Name(value="limit", defaultValue = "-1") long limit,
@Name(value="skip", defaultValue = "0") long skip) {
@Name(value="orderFields", defaultValue = "[]") java.util.List<String> orderFields,
@Name(value="limit", defaultValue = "-1") long limit,
@Name(value="skip", defaultValue = "0") long skip) {
List<Map<String,Object>> result = new ArrayList<>(coll);

if (orderFields != null && !orderFields.isEmpty()) {
Expand Down Expand Up @@ -768,4 +768,10 @@ public List<List<Object>> combinations(@Name("coll") List<Object> coll, @Name(va

return combinations;
}

@UserFunction
@Description("apoc.coll.different(values) - returns true if values are different")
public boolean different(@Name("values") List<Object> values) {
return new HashSet(values).size() == values.size();
}
}
21 changes: 21 additions & 0 deletions src/main/java/apoc/label/Label.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package apoc.label;

import org.neo4j.graphdb.*;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;

public class Label {

@Context public GraphDatabaseService db;

@UserFunction("apoc.label.exists")
@Description("apoc.label.exists(element, label) - returns true or false related to label existance")
public boolean exists(@Name("node") Object element, @Name("label") String label) {

return element instanceof Node ? ((Node) element).hasLabel(org.neo4j.graphdb.Label.label(label)) :
element instanceof Relationship ? ((Relationship) element).isType(RelationshipType.withName(label)) : false;

}
}
29 changes: 27 additions & 2 deletions src/main/java/apoc/nodes/Nodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public Stream<LongResult> delete(@Name("nodes") Object ids, @Name("batchSize") l
while (it.hasNext()) {
final List<Node> batch = Util.take(it, (int)batchSize);
// count += Util.inTx(api,() -> batch.stream().peek( n -> {n.getRelationships().forEach(Relationship::delete);n.delete();}).count());
count += Util.inTx(db,() -> {db.execute("FOREACH (n in {nodes} | DETACH DELETE n)",map("nodes",batch)).close();return batch.size();});
count += Util.inTx(db,() -> {db.execute("FOREACH (n in {nodes} | DETACH DELETE n)",map("nodes",batch)).close();return batch.size();});
}
return Stream.of(new LongResult(count));
}
Expand Down Expand Up @@ -347,6 +347,31 @@ public long degree(@Name("node") Node node, @Name(value = "types",defaultValue =
return degree;
}

@UserFunction("apoc.node.degree.in")
@Description("apoc.node.degree.in(node, relationshipName) - returns total number number of incoming relationships")
public long degreeIn(@Name("node") Node node, @Name(value = "types",defaultValue = "") String type) {

if (type==null || type.isEmpty()) {
return node.getDegree(Direction.INCOMING);
}

return node.getDegree(RelationshipType.withName(type), Direction.INCOMING);

}

@UserFunction("apoc.node.degree.out")
@Description("apoc.node.degree.out(node, relationshipName) - returns total number number of outgoing relationships")
public long degreeOut(@Name("node") Node node, @Name(value = "types",defaultValue = "") String type) {

if (type==null || type.isEmpty()) {
return node.getDegree(Direction.OUTGOING);
}

return node.getDegree(RelationshipType.withName(type), Direction.OUTGOING);

}


@UserFunction("apoc.node.relationship.types")
@Description("apoc.node.relationship.types(node, rel-direction-pattern) - returns a list of distinct relationship types")
public List<String> relationshipTypes(@Name("node") Node node, @Name(value = "types",defaultValue = "") String types) {
Expand Down Expand Up @@ -386,4 +411,4 @@ private int getDegreeSafe(Node node, RelationshipType relType, Direction directi
return node.getDegree(relType, direction);
}

}
}
16 changes: 16 additions & 0 deletions src/test/java/apoc/coll/CollTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -688,4 +688,20 @@ public void testCombinationsWithMinAndMaxSelect() throws Exception {
assertEquals(result, row.get("value"));
});
}

@Test
public void testVerifyAllValuesAreDifferent() throws Exception {
testCall(db, "RETURN apoc.coll.different([1, 2, 3]) as value",
(row) -> {
assertEquals(true, row.get("value"));
});
testCall(db, "RETURN apoc.coll.different([1, 1, 1]) as value",
(row) -> {
assertEquals(false, row.get("value"));
});
testCall(db, "RETURN apoc.coll.different([3, 3, 1]) as value",
(row) -> {
assertEquals(false, row.get("value"));
});
}
}
59 changes: 59 additions & 0 deletions src/test/java/apoc/label/LabelTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package apoc.label;

import apoc.util.TestUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.test.TestGraphDatabaseFactory;

import static apoc.util.TestUtil.testCall;
import static org.junit.Assert.assertEquals;

public class LabelTest {

private static GraphDatabaseService db;

@BeforeClass
public static void setUp() throws Exception {
db = new TestGraphDatabaseFactory().newImpermanentDatabase();
TestUtil.registerProcedure(db, Label.class);
}

@AfterClass
public static void tearDown() {
db.shutdown();
}

@Test
public void testVerifyNodeLabelExistance() throws Exception {

db.execute("create (a:Person{name:'Foo'})");

testCall(db, "MATCH (a) RETURN apoc.label.exists(a, 'Person') as value",
(row) -> {
assertEquals(true, row.get("value"));
});
testCall(db, "MATCH (a) RETURN apoc.label.exists(a, 'Dog') as value",
(row) -> {
assertEquals(false, row.get("value"));
});
}

@Test
public void testVerifyRelTypeExistance() throws Exception {


db.execute("create (a:Person{name:'Foo'}), (b:Person{name:'Bar'}), (a)-[:LOVE{since:2010}]->(b)");

testCall(db, "MATCH ()-[a]->() RETURN apoc.label.exists(a, 'LOVE') as value",
(row) -> {
assertEquals(true, row.get("value"));
});
testCall(db, "MATCH ()-[a]->() RETURN apoc.label.exists(a, 'LIVES_IN') as value",
(row) -> {
assertEquals(false, row.get("value"));
});

}
}
30 changes: 27 additions & 3 deletions src/test/java/apoc/nodes/NodesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ public void testConnected() throws Exception {
int relCount = 20;
for (int rel=0;rel<relCount;rel++) {
db.execute("MATCH (st:StartThin),(et:EndThin),(ed:EndDense) " +
" CREATE (st)-[:REL"+rel+"]->(et) " +
" WITH * UNWIND RANGE(1,{count}) AS id CREATE (st)-[:REL"+rel+"]->(ed)",
" CREATE (st)-[:REL"+rel+"]->(et) " +
" WITH * UNWIND RANGE(1,{count}) AS id CREATE (st)-[:REL"+rel+"]->(ed)",
map("count",relCount-rel)).close();
}

Expand Down Expand Up @@ -179,6 +179,30 @@ public void testDegreeDirectionOnly() {

}

@Test
public void testDegreeInOutDirectionOnly() {
db.execute("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)").close();

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.execute("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)").close();

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 testId() {
assertTrue(db.execute("CREATE (f:Foo {foo:'bar'}) RETURN apoc.node.id(f) AS id").<Long>columnAs("id").next() >= 0);
Expand Down Expand Up @@ -253,4 +277,4 @@ public void testRelType() {
assertNull(db.execute("RETURN apoc.rel.type(null) AS type").columnAs("type").next());
}

}
}

0 comments on commit f0d772b

Please sign in to comment.