Skip to content

Commit

Permalink
[Gc8EZpDO] Add element id as an option to functions using nodes/rels …
Browse files Browse the repository at this point in the history
…with type ANY
  • Loading branch information
gem-neo4j committed Sep 28, 2023
1 parent a36b11e commit cb9334e
Show file tree
Hide file tree
Showing 13 changed files with 442 additions and 159 deletions.
6 changes: 3 additions & 3 deletions common/src/main/java/apoc/get/Get.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@
import apoc.result.NodeResult;
import apoc.result.RelationshipResult;
import apoc.util.Util;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.procedure.Name;

import java.util.stream.Stream;

public class Get {

public Transaction tx;
public InternalTransaction tx;

public Get(Transaction tx) {
public Get(InternalTransaction tx) {
this.tx = tx;
}

Expand Down
27 changes: 17 additions & 10 deletions common/src/main/java/apoc/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,31 @@ public static Stream<Object> stream(Object values) {
return ConvertUtils.convertToList(values).stream();
}

public static Stream<Node> nodeStream(Transaction tx, Object ids) {
public static Stream<Node> nodeStream(InternalTransaction tx, Object ids) {
return stream(ids).map(id -> node(tx, id));
}

public static Node node(Transaction tx, Object id) {
if (id instanceof Node) return rebind(tx, (Node)id);
if (id instanceof Number) return tx.getNodeById(((Number)id).longValue());
throw new RuntimeException("Can't convert "+id.getClass()+" to a Node");
public static List<Node> nodeList(InternalTransaction tx, Object ids) {
if (ids == null) return List.of();
return stream(ids).map(id -> node(tx, id)).toList();
}

public static Stream<Relationship> relsStream(Transaction tx, Object ids) {
public static Node node(InternalTransaction tx, Object id) {
if (id instanceof Node node) return rebind(tx, node);
if (id instanceof Number nodeId) return tx.getNodeByElementId(tx.elementIdMapper().nodeElementId(nodeId.longValue()));
if (id instanceof String elementId) return tx.getNodeByElementId(elementId);
throw new RuntimeException("Can't convert " + id.getClass() + " to a Node");
}

public static Stream<Relationship> relsStream(InternalTransaction tx, Object ids) {
return stream(ids).map(id -> relationship(tx, id));
}

public static Relationship relationship(Transaction tx, Object id) {
if (id instanceof Relationship) return rebind(tx, (Relationship)id);
if (id instanceof Number) return tx.getRelationshipById(((Number)id).longValue());
throw new RuntimeException("Can't convert "+id.getClass()+" to a Relationship");
public static Relationship relationship(InternalTransaction tx, Object id) {
if (id instanceof Relationship rel) return rebind(tx, rel);
if (id instanceof Number relId) return tx.getRelationshipByElementId(tx.elementIdMapper().relationshipElementId(relId.longValue()));
if (id instanceof String elementId) return tx.getRelationshipByElementId(elementId);
throw new RuntimeException("Can't convert " + id.getClass() + " to a Relationship");
}

public static <T> T retryInTx(Log log, GraphDatabaseService db, Function<Transaction, T> function, long retry, long maxRetries, Consumer<Long> callbackForRetry) {
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/apoc/algo/Cover.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
Expand All @@ -44,7 +45,7 @@ public class Cover {
@Procedure("apoc.algo.cover")
@Description("Returns all relationships between a given set of nodes.")
public Stream<RelationshipResult> cover(@Name("nodes") Object nodes) {
Set<Node> nodeSet = Util.nodeStream(tx, nodes).collect(Collectors.toSet());
Set<Node> nodeSet = Util.nodeStream((InternalTransaction) tx, nodes).collect(Collectors.toSet());
return coverNodes(nodeSet).map(RelationshipResult::new);
}

Expand Down
19 changes: 10 additions & 9 deletions core/src/main/java/apoc/create/Create.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import apoc.util.collection.Iterables;
import apoc.uuid.UuidUtil;
import org.neo4j.graphdb.*;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.procedure.*;

import java.util.HashMap;
Expand Down Expand Up @@ -53,7 +54,7 @@ public Stream<NodeResult> node(@Name("labels") List<String> labelNames, @Name("p
@Description("Adds the given labels to the given nodes.")
public Stream<NodeResult> addLabels(@Name("nodes") Object nodes, @Name("labels") List<String> labelNames) {
Label[] labels = Util.labels(labelNames);
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
Node node = r.node;
for (Label label : labels) {
node.addLabel(label);
Expand All @@ -65,7 +66,7 @@ public Stream<NodeResult> addLabels(@Name("nodes") Object nodes, @Name("labels")
@Procedure(name = "apoc.create.setProperty", mode = Mode.WRITE)
@Description("Sets the given property to the given node(s).")
public Stream<NodeResult> setProperty(@Name("nodes") Object nodes, @Name("key") String key, @Name("value") Object value) {
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
setProperty(r.node, key,toPropertyValue(value));
return r;
});
Expand All @@ -74,7 +75,7 @@ public Stream<NodeResult> setProperty(@Name("nodes") Object nodes, @Name("key")
@Procedure(name = "apoc.create.setRelProperty", mode = Mode.WRITE)
@Description("Sets the given property on the relationship(s).")
public Stream<RelationshipResult> setRelProperty(@Name("rels") Object rels, @Name("key") String key, @Name("value") Object value) {
return new Get(tx).rels(rels).map((r) -> {
return new Get((InternalTransaction) tx).rels(rels).map((r) -> {
setProperty(r.rel,key,toPropertyValue(value));
return r;
});
Expand All @@ -83,7 +84,7 @@ public Stream<RelationshipResult> setRelProperty(@Name("rels") Object rels, @Nam
@Procedure(name = "apoc.create.setProperties", mode = Mode.WRITE)
@Description("Sets the given properties to the given node(s).")
public Stream<NodeResult> setProperties(@Name("nodes") Object nodes, @Name("keys") List<String> keys, @Name("values") List<Object> values) {
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
setProperties(r.node, Util.mapFromLists(keys,values));
return r;
});
Expand All @@ -92,7 +93,7 @@ public Stream<NodeResult> setProperties(@Name("nodes") Object nodes, @Name("keys
@Procedure(name = "apoc.create.removeProperties", mode = Mode.WRITE)
@Description("Removes the given properties from the given node(s).")
public Stream<NodeResult> removeProperties(@Name("nodes") Object nodes, @Name("keys") List<String> keys) {
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
keys.forEach( r.node::removeProperty );
return r;
});
Expand All @@ -101,7 +102,7 @@ public Stream<NodeResult> removeProperties(@Name("nodes") Object nodes, @Name("k
@Procedure(name = "apoc.create.setRelProperties", mode = Mode.WRITE)
@Description("Sets the given properties on the relationship(s).")
public Stream<RelationshipResult> setRelProperties(@Name("rels") Object rels, @Name("keys") List<String> keys, @Name("values") List<Object> values) {
return new Get(tx).rels(rels).map((r) -> {
return new Get((InternalTransaction) tx).rels(rels).map((r) -> {
setProperties(r.rel, Util.mapFromLists(keys,values));
return r;
});
Expand All @@ -110,7 +111,7 @@ public Stream<RelationshipResult> setRelProperties(@Name("rels") Object rels, @N
@Procedure(name = "apoc.create.removeRelProperties", mode = Mode.WRITE)
@Description("Removes the given properties from the given relationship(s).")
public Stream<RelationshipResult> removeRelProperties(@Name("rels") Object rels, @Name("keys") List<String> keys) {
return new Get(tx).rels(rels).map((r) -> {
return new Get((InternalTransaction) tx).rels(rels).map((r) -> {
keys.forEach( r.rel::removeProperty);
return r;
});
Expand All @@ -120,7 +121,7 @@ public Stream<RelationshipResult> removeRelProperties(@Name("rels") Object rels,
@Description("Sets the given labels to the given node(s). Non-matching labels are removed from the nodes.")
public Stream<NodeResult> setLabels(@Name("nodes") Object nodes, @Name("labels") List<String> labelNames) {
Label[] labels = Util.labels(labelNames);
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
Node node = r.node;
for (Label label : node.getLabels()) {
if (labelNames.contains(label.name())) continue;
Expand All @@ -138,7 +139,7 @@ public Stream<NodeResult> setLabels(@Name("nodes") Object nodes, @Name("labels")
@Description("Removes the given labels from the given node(s).")
public Stream<NodeResult> removeLabels(@Name("nodes") Object nodes, @Name("labels") List<String> labelNames) {
Label[] labels = Util.labels(labelNames);
return new Get(tx).nodes(nodes).map((r) -> {
return new Get((InternalTransaction) tx).nodes(nodes).map((r) -> {
Node node = r.node;
for (Label label : labels) {
node.removeLabel(label);
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/java/apoc/nodes/Nodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ public void link(@Name("nodes") List<Node> nodes, @Name("type") String type, @Na
@Procedure("apoc.nodes.get")
@Description("Returns all nodes with the given ids.")
public Stream<NodeResult> get(@Name("nodes") Object ids) {
return Util.nodeStream(tx, ids).map(NodeResult::new);
return Util.nodeStream((InternalTransaction) tx, ids).map(NodeResult::new);
}

@Procedure(name = "apoc.nodes.delete", mode = Mode.WRITE)
@Description("Deletes all nodes with the given ids.")
public Stream<LongResult> delete(@Name("nodes") Object ids, @Name("batchSize") long batchSize) {
Iterator<Node> it = Util.nodeStream(tx, ids).iterator();
Iterator<Node> it = Util.nodeStream((InternalTransaction) tx, ids).iterator();
long count = 0;
while (it.hasNext()) {
final List<Node> batch = Util.take(it, (int)batchSize);
Expand All @@ -189,7 +189,7 @@ public Stream<LongResult> delete(@Name("nodes") Object ids, @Name("batchSize") l
@Procedure("apoc.nodes.rels")
@Description("Returns all relationships with the given ids.")
public Stream<RelationshipResult> rels(@Name("rels") Object ids) {
return Util.relsStream(tx, ids).map(RelationshipResult::new);
return Util.relsStream((InternalTransaction) tx, ids).map(RelationshipResult::new);
}

@UserFunction("apoc.node.relationship.exists")
Expand Down Expand Up @@ -520,7 +520,7 @@ public List<String> relationshipTypes(@Name("node") Node node, @Name(value = "re
@Description("Returns a list of distinct relationship types from the given list of nodes.")
public List<Map<String, Object>> nodesRelationshipTypes(@Name("nodes") Object ids, @Name(value = "types",defaultValue = "") String types) {
if (ids == null) return null;
return Util.nodeStream(tx, ids)
return Util.nodeStream((InternalTransaction) tx, ids)
.map(node -> {
final List<String> relationshipTypes = relationshipTypes(node, types);
if (relationshipTypes == null) {
Expand Down Expand Up @@ -551,7 +551,7 @@ public Map<String,Boolean> relationshipExists(@Name("node") Node node, @Name(val
@Description("Returns a boolean based on whether or not the given nodes have the given relationships.")
public List<Map<String, Object>> nodesRelationshipExists(@Name("nodes") Object ids, @Name(value = "types", defaultValue = "") String types) {
if (ids == null) return null;
return Util.nodeStream(tx, ids)
return Util.nodeStream((InternalTransaction) tx, ids)
.map(node -> {
final Map<String, Boolean> existsMap = relationshipExists(node, types);
if (existsMap == null) {
Expand Down
66 changes: 17 additions & 49 deletions core/src/main/java/apoc/path/PathExplorer.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import java.util.stream.StreamSupport;

import static apoc.path.PathExplorer.NodeFilter.*;
import static apoc.util.Util.getNodeElementId;

public class PathExplorer {
public static final Uniqueness UNIQUENESS = Uniqueness.RELATIONSHIP_PATH;
Expand All @@ -57,27 +56,26 @@ public Stream<PathResult> explorePath(@Name("startNode") Object start
, @Name("relFilter") String pathFilter
, @Name("labelFilter") String labelFilter
, @Name("minDepth") long minLevel
, @Name("maxDepth") long maxLevel ) throws Exception {
List<Node> nodes = startToNodes(start);
, @Name("maxDepth") long maxLevel ) {
List<Node> nodes = Util.nodeList((InternalTransaction) tx, start);
return explorePathPrivate(nodes, pathFilter, labelFilter, minLevel, maxLevel, BFS, UNIQUENESS, false, -1, null, null, true).map( PathResult::new );
}

//
@NotThreadSafe
@Procedure("apoc.path.expandConfig")
@Description("Returns paths expanded from the start node the given relationship types from min-depth to max-depth.")
public Stream<PathResult> expandConfig(@Name("startNode") Object start, @Name("config") Map<String,Object> config) throws Exception {
public Stream<PathResult> expandConfig(@Name("startNode") Object start, @Name("config") Map<String,Object> config) {
return expandConfigPrivate(start, config).map( PathResult::new );
}

@NotThreadSafe
@Procedure("apoc.path.subgraphNodes")
@Description("Returns the nodes in the sub-graph reachable from the start node following the given relationship types to max-depth.")
public Stream<NodeResult> subgraphNodes(@Name("startNode") Object start, @Name("config") Map<String,Object> config) throws Exception {
public Stream<NodeResult> subgraphNodes(@Name("startNode") Object start, @Name("config") Map<String,Object> config) {
Map<String, Object> configMap = new HashMap<>(config);
configMap.put("uniqueness", "NODE_GLOBAL");

if (config.containsKey("minLevel") && !config.get("minLevel").equals(0l) && !config.get("minLevel").equals(1l)) {
if (config.containsKey("minLevel") && !config.get("minLevel").equals(0L) && !config.get("minLevel").equals(1L)) {
throw new IllegalArgumentException("minLevel can only be 0 or 1 in subgraphNodes()");
}

Expand All @@ -87,12 +85,12 @@ public Stream<NodeResult> subgraphNodes(@Name("startNode") Object start, @Name("
@NotThreadSafe
@Procedure("apoc.path.subgraphAll")
@Description("Returns the sub-graph reachable from the start node following the given relationship types to max-depth.")
public Stream<GraphResult> subgraphAll(@Name("startNode") Object start, @Name("config") Map<String,Object> config) throws Exception {
public Stream<GraphResult> subgraphAll(@Name("startNode") Object start, @Name("config") Map<String,Object> config) {
Map<String, Object> configMap = new HashMap<>(config);
configMap.remove("optional"); // not needed, will return empty collections anyway if no results
configMap.put("uniqueness", "NODE_GLOBAL");

if (config.containsKey("minLevel") && !config.get("minLevel").equals(0l) && !config.get("minLevel").equals(1l)) {
if (config.containsKey("minLevel") && !config.get("minLevel").equals(0L) && !config.get("minLevel").equals(1L)) {
throw new IllegalArgumentException("minLevel can only be 0 or 1 in subgraphAll()");
}

Expand All @@ -105,11 +103,11 @@ public Stream<GraphResult> subgraphAll(@Name("startNode") Object start, @Name("c
@NotThreadSafe
@Procedure("apoc.path.spanningTree")
@Description("Returns spanning tree paths expanded from the start node following the given relationship types to max-depth.")
public Stream<PathResult> spanningTree(@Name("startNode") Object start, @Name("config") Map<String,Object> config) throws Exception {
public Stream<PathResult> spanningTree(@Name("startNode") Object start, @Name("config") Map<String,Object> config) {
Map<String, Object> configMap = new HashMap<>(config);
configMap.put("uniqueness", "NODE_GLOBAL");

if (config.containsKey("minLevel") && !config.get("minLevel").equals(0l) && !config.get("minLevel").equals(1l)) {
if (config.containsKey("minLevel") && !config.get("minLevel").equals(0L) && !config.get("minLevel").equals(1L)) {
throw new IllegalArgumentException("minLevel can only be 0 or 1 in spanningTree()");
}

Expand All @@ -123,38 +121,8 @@ private Uniqueness getUniqueness(String uniqueness) {
return UNIQUENESS;
}

/*
, @Name("relationshipFilter") String pathFilter
, @Name("labelFilter") String labelFilter
, @Name("minLevel") long minLevel
, @Name("maxLevel") long maxLevel ) throws Exception {
*/
@SuppressWarnings("unchecked")
private List<Node> startToNodes(Object start) throws Exception {
if (start == null) return Collections.emptyList();
if (start instanceof Node) {
return Collections.singletonList((Node) start);
}
if (start instanceof Number) {
return Collections.singletonList(tx.getNodeByElementId(getNodeElementId((InternalTransaction) tx, ((Number) start).longValue())));
}
if (start instanceof List) {
List list = (List) start;
if (list.isEmpty()) return Collections.emptyList();

Object first = list.get(0);
if (first instanceof Node) return (List<Node>)list;
if (first instanceof Number) {
List<Node> nodes = new ArrayList<>();
for (Number n : ((List<Number>)list)) nodes.add(tx.getNodeByElementId(getNodeElementId((InternalTransaction) tx, n.longValue())));
return nodes;
}
}
throw new Exception("Unsupported data type for start parameter a Node or an Identifier (long) of a Node must be given!");
}

private Stream<Path> expandConfigPrivate(@Name("start") Object start, @Name("config") Map<String,Object> config) throws Exception {
List<Node> nodes = startToNodes(start);
private Stream<Path> expandConfigPrivate(@Name("start") Object start, @Name("config") Map<String,Object> config) {
List<Node> nodes = Util.nodeList((InternalTransaction) tx, start);

String uniqueness = (String) config.getOrDefault("uniqueness", UNIQUENESS.name());
String relationshipFilter = (String) config.getOrDefault("relationshipFilter", null);
Expand All @@ -168,12 +136,12 @@ private Stream<Path> expandConfigPrivate(@Name("start") Object start, @Name("con
String sequence = (String) config.getOrDefault("sequence", null);
boolean beginSequenceAtStart = Util.toBoolean(config.getOrDefault("beginSequenceAtStart", true));

List<Node> endNodes = startToNodes(config.get("endNodes"));
List<Node> terminatorNodes = startToNodes(config.get("terminatorNodes"));
List<Node> whitelistNodes = startToNodes(config.get("whitelistNodes")); // DEPRECATED REMOVE 6.0
List<Node> blacklistNodes = startToNodes(config.get("blacklistNodes")); // DEPRECATED REMOVE 6.0
List<Node> allowlistNodes = startToNodes(config.get("allowlistNodes"));
List<Node> denylistNodes = startToNodes(config.get("denylistNodes"));
List<Node> endNodes = Util.nodeList((InternalTransaction) tx, config.get("endNodes"));
List<Node> terminatorNodes = Util.nodeList((InternalTransaction) tx, config.get("terminatorNodes"));
List<Node> whitelistNodes = Util.nodeList((InternalTransaction) tx, config.get("whitelistNodes")); // DEPRECATED REMOVE 6.0
List<Node> blacklistNodes = Util.nodeList((InternalTransaction) tx, config.get("blacklistNodes")); // DEPRECATED REMOVE 6.0
List<Node> allowlistNodes = Util.nodeList((InternalTransaction) tx, config.get("allowlistNodes"));
List<Node> denylistNodes = Util.nodeList((InternalTransaction) tx, config.get("denylistNodes"));
EnumMap<NodeFilter, List<Node>> nodeFilter = new EnumMap<>(NodeFilter.class);

if (endNodes != null && !endNodes.isEmpty()) {
Expand Down
Loading

0 comments on commit cb9334e

Please sign in to comment.